Merge "MediaFocusControl: priority to playing players for media button"
diff --git a/Android.mk b/Android.mk
index adc9ef1..be7e055 100644
--- a/Android.mk
+++ b/Android.mk
@@ -155,6 +155,7 @@
core/java/android/net/INetworkManagementEventObserver.aidl \
core/java/android/net/INetworkPolicyListener.aidl \
core/java/android/net/INetworkPolicyManager.aidl \
+ core/java/android/net/INetworkScoreService.aidl \
core/java/android/net/INetworkStatsService.aidl \
core/java/android/net/INetworkStatsSession.aidl \
core/java/android/net/nsd/INsdManager.aidl \
@@ -288,11 +289,14 @@
media/java/android/media/IRemoteDisplayProvider.aidl \
media/java/android/media/IRemoteVolumeObserver.aidl \
media/java/android/media/IRingtonePlayer.aidl \
- media/java/android/media/session/IMediaController.aidl \
- media/java/android/media/session/IMediaControllerCallback.aidl \
- media/java/android/media/session/IMediaSession.aidl \
- media/java/android/media/session/IMediaSessionCallback.aidl \
- media/java/android/media/session/IMediaSessionManager.aidl \
+ media/java/android/media/routeprovider/IRouteConnection.aidl \
+ media/java/android/media/routeprovider/IRouteProvider.aidl \
+ media/java/android/media/routeprovider/IRouteProviderCallback.aidl \
+ media/java/android/media/session/ISessionController.aidl \
+ media/java/android/media/session/ISessionControllerCallback.aidl \
+ media/java/android/media/session/ISession.aidl \
+ media/java/android/media/session/ISessionCallback.aidl \
+ media/java/android/media/session/ISessionManager.aidl \
telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
telephony/java/com/android/internal/telephony/ITelephony.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index ffdac4e..c6f6a62 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -189,6 +189,7 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/media/java/android/media/IMedia*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/view/IMagnificationCallbacks*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/tv/)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/media/java/android/media/)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/api/current.txt b/api/current.txt
index 3ad7c72..4e2a3bb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27,6 +27,7 @@
field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
+ field public static final java.lang.String BIND_ROUTE_PROVIDER = "android.permission.BIND_ROUTE_PROVIDER";
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";
@@ -9735,9 +9736,13 @@
method public int save();
method public int save(int);
method public int saveLayer(android.graphics.RectF, android.graphics.Paint, int);
+ method public int saveLayer(android.graphics.RectF, android.graphics.Paint);
method public int saveLayer(float, float, float, float, android.graphics.Paint, int);
+ method public int saveLayer(float, float, float, float, android.graphics.Paint);
method public int saveLayerAlpha(android.graphics.RectF, int, int);
+ method public int saveLayerAlpha(android.graphics.RectF, int);
method public int saveLayerAlpha(float, float, float, float, int, int);
+ method public int saveLayerAlpha(float, float, float, float, int);
method public void scale(float, float);
method public final void scale(float, float, float, float);
method public void setBitmap(android.graphics.Bitmap);
@@ -10067,6 +10072,7 @@
method public android.graphics.Xfermode getXfermode();
method public final boolean isAntiAlias();
method public final boolean isDither();
+ method public boolean isElegantTextHeight();
method public final boolean isFakeBoldText();
method public final boolean isFilterBitmap();
method public final boolean isLinearText();
@@ -10085,6 +10091,7 @@
method public void setColor(int);
method public android.graphics.ColorFilter setColorFilter(android.graphics.ColorFilter);
method public void setDither(boolean);
+ method public void setElegantTextHeight(boolean);
method public void setFakeBoldText(boolean);
method public void setFilterBitmap(boolean);
method public void setFlags(int);
@@ -12117,7 +12124,9 @@
public final class VirtualDisplay {
method public android.view.Display getDisplay();
+ method public android.view.Surface getSurface();
method public void release();
+ method public void setSurface(android.view.Surface);
}
}
@@ -14955,24 +14964,68 @@
}
+package android.media.routeprovider {
+
+ public final class RouteConnection {
+ ctor public RouteConnection(android.media.routeprovider.RouteProviderService, android.media.session.RouteInfo);
+ method public android.media.routeprovider.RouteInterfaceHandler addRouteInterface(java.lang.String);
+ method public android.media.routeprovider.RouteInterfaceHandler getRouteInterface(java.lang.String);
+ method public void shutDown();
+ }
+
+ public final class RouteInterfaceHandler {
+ method public void addListener(android.media.routeprovider.RouteInterfaceHandler.CommandListener, android.os.Handler);
+ method public java.lang.String getName();
+ method public void removeListener(android.media.routeprovider.RouteInterfaceHandler.CommandListener);
+ method public void sendEvent(java.lang.String, android.os.Bundle);
+ method public static void sendResult(android.os.ResultReceiver, int, android.os.Bundle);
+ }
+
+ public static abstract class RouteInterfaceHandler.CommandListener {
+ ctor public RouteInterfaceHandler.CommandListener();
+ method public abstract boolean onCommand(android.media.routeprovider.RouteInterfaceHandler, java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+ }
+
+ public final class RoutePlaybackControlsHandler {
+ method public void addListener(android.media.routeprovider.RoutePlaybackControlsHandler.Listener);
+ method public void addListener(android.media.routeprovider.RoutePlaybackControlsHandler.Listener, android.os.Handler);
+ method public static android.media.routeprovider.RoutePlaybackControlsHandler addTo(android.media.routeprovider.RouteConnection);
+ method public void removeListener(android.media.routeprovider.RoutePlaybackControlsHandler.Listener);
+ method public void sendPlaybackChangeEvent(int);
+ }
+
+ public static abstract class RoutePlaybackControlsHandler.Listener extends android.media.routeprovider.RouteInterfaceHandler.CommandListener {
+ ctor public RoutePlaybackControlsHandler.Listener();
+ method public boolean fastForward();
+ method public long getCapabilities();
+ method public long getCurrentPosition();
+ method public final boolean onCommand(android.media.routeprovider.RouteInterfaceHandler, java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+ method public boolean pause();
+ method public void playNow(java.lang.String, android.os.ResultReceiver);
+ method public boolean resume();
+ }
+
+ public abstract class RouteProviderService extends android.app.Service {
+ ctor public RouteProviderService();
+ method public abstract android.media.routeprovider.RouteConnection connect(android.media.session.RouteInfo, android.media.routeprovider.RouteRequest);
+ method public abstract java.util.List<android.media.session.RouteInfo> getMatchingRoutes(java.util.List<android.media.routeprovider.RouteRequest>);
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public void updateDiscoveryRequests(java.util.List<android.media.routeprovider.RouteRequest>);
+ field public static final java.lang.String SERVICE_INTERFACE = "com.android.media.session.MediaRouteProvider";
+ }
+
+ public final class RouteRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.media.session.RouteOptions getConnectionOptions();
+ method public android.media.session.SessionInfo getSessionInfo();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+}
+
package android.media.session {
- public final class MediaController {
- method public void addCallback(android.media.session.MediaController.Callback);
- method public void addCallback(android.media.session.MediaController.Callback, android.os.Handler);
- method public static android.media.session.MediaController fromToken(android.media.session.MediaSessionToken);
- method public android.media.session.TransportController getTransportController();
- method public void removeCallback(android.media.session.MediaController.Callback);
- method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
- method public void sendMediaButton(int);
- }
-
- public static abstract class MediaController.Callback {
- ctor public MediaController.Callback();
- method public void onEvent(java.lang.String, android.os.Bundle);
- method public void onRouteChanged(android.os.Bundle);
- }
-
public final class MediaMetadata implements android.os.Parcelable {
method public int describeContents();
method public android.graphics.Bitmap getBitmap(java.lang.String);
@@ -15013,36 +15066,6 @@
method public android.media.session.MediaMetadata.Builder putString(java.lang.String, java.lang.String);
}
- public final class MediaSession {
- method public void addCallback(android.media.session.MediaSession.Callback);
- method public void addCallback(android.media.session.MediaSession.Callback, android.os.Handler);
- method public android.media.session.MediaSessionToken getSessionToken();
- method public android.media.session.TransportPerformer getTransportPerformer();
- method public void publish();
- method public void release();
- method public void removeCallback(android.media.session.MediaSession.Callback);
- method public void sendEvent(java.lang.String, android.os.Bundle);
- method public android.media.session.TransportPerformer setTransportPerformerEnabled();
- }
-
- public static abstract class MediaSession.Callback {
- ctor public MediaSession.Callback();
- method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
- method public void onMediaButton(android.content.Intent);
- method public void onRequestRouteChange(android.os.Bundle);
- }
-
- public final class MediaSessionManager {
- method public android.media.session.MediaSession createSession(java.lang.String);
- method public java.util.List<android.media.session.MediaController> getActiveSessions();
- }
-
- public class MediaSessionToken implements android.os.Parcelable {
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- }
-
public final class PlaybackState implements android.os.Parcelable {
ctor public PlaybackState();
ctor public PlaybackState(android.media.session.PlaybackState);
@@ -15071,6 +15094,7 @@
field public static final long ACTION_STOP = 1L; // 0x1L
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int PLAYSTATE_BUFFERING = 6; // 0x6
+ field public static final int PLAYSTATE_CONNECTING = 8; // 0x8
field public static final int PLAYSTATE_ERROR = 7; // 0x7
field public static final int PLAYSTATE_FAST_FORWARDING = 4; // 0x4
field public static final int PLAYSTATE_NONE = 0; // 0x0
@@ -15080,11 +15104,44 @@
field public static final int PLAYSTATE_STOPPED = 1; // 0x1
}
+ public final class Route {
+ method public android.media.session.RouteInterface getInterface(java.lang.String);
+ method public android.media.session.RouteOptions getOptions();
+ method public android.media.session.RouteInfo getRouteInfo();
+ }
+
+ public final class RouteInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.util.List<android.media.session.RouteOptions> getConnectionMethods();
+ method public java.lang.String getId();
+ method public java.lang.String getName();
+ method public java.lang.String getProvider();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public static final class RouteInfo.Builder {
+ ctor public RouteInfo.Builder(android.media.session.RouteInfo);
+ ctor public RouteInfo.Builder();
+ method public android.media.session.RouteInfo.Builder addRouteOptions(android.media.session.RouteOptions);
+ method public android.media.session.RouteInfo build();
+ method public android.media.session.RouteInfo.Builder clearRouteOptions();
+ method public int getOptionsSize();
+ method public android.media.session.RouteInfo.Builder setId(java.lang.String);
+ method public android.media.session.RouteInfo.Builder setName(java.lang.String);
+ }
+
public final class RouteInterface {
method public void addListener(android.media.session.RouteInterface.EventListener);
method public void addListener(android.media.session.RouteInterface.EventListener, android.os.Handler);
method public void removeListener(android.media.session.RouteInterface.EventListener);
- method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+ method public boolean sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+ field public static final int RESULT_COMMAND_NOT_SUPPORTED = -3; // 0xfffffffd
+ field public static final int RESULT_ERROR = -1; // 0xffffffff
+ field public static final int RESULT_INTERFACE_NOT_SUPPORTED = -2; // 0xfffffffe
+ field public static final int RESULT_NOT_CONNECTED = -5; // 0xfffffffb
+ field public static final int RESULT_ROUTE_IS_STALE = -4; // 0xfffffffc
+ field public static final int RESULT_SUCCESS = 1; // 0x1
}
public static abstract class RouteInterface.EventListener {
@@ -15092,40 +15149,100 @@
method public abstract void onEvent(java.lang.String, android.os.Bundle);
}
- public static abstract class RouteInterface.Stub {
- ctor public RouteInterface.Stub();
- method public abstract java.lang.String getName();
- method public abstract void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
- method public final void sendEvent(android.media.session.MediaSession, java.lang.String, android.os.Bundle);
+ public final class RouteOptions implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getConnectionParams();
+ method public java.util.List<java.lang.String> getInterfaceNames();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
}
- public final class RouteTransportControls {
- method public void addListener(android.media.session.RouteTransportControls.Listener);
- method public void addListener(android.media.session.RouteTransportControls.Listener, android.os.Handler);
- method public void fastForward(float);
- method public static android.media.session.RouteTransportControls from(android.media.session.MediaController);
+ public static final class RouteOptions.Builder {
+ ctor public RouteOptions.Builder();
+ method public android.media.session.RouteOptions.Builder addInterface(java.lang.String);
+ method public android.media.session.RouteOptions build();
+ method public android.media.session.RouteOptions.Builder setParameters(android.os.Bundle);
+ }
+
+ public final class RoutePlaybackControls {
+ method public void addListener(android.media.session.RoutePlaybackControls.Listener);
+ method public void addListener(android.media.session.RoutePlaybackControls.Listener, android.os.Handler);
+ method public void fastForward();
+ method public static android.media.session.RoutePlaybackControls from(android.media.session.Route);
method public void getCapabilities(android.os.ResultReceiver);
method public void getCurrentPosition(android.os.ResultReceiver);
method public void pause();
- method public void play();
- method public void removeListener(android.media.session.RouteTransportControls.Listener);
- field public static final java.lang.String NAME = "android.media.session.RouteTransportControls";
+ method public void playNow(java.lang.String);
+ method public void removeListener(android.media.session.RoutePlaybackControls.Listener);
+ method public void resume();
+ field public static final java.lang.String NAME = "android.media.session.RoutePlaybackControls";
}
- public static abstract class RouteTransportControls.Listener {
- ctor public RouteTransportControls.Listener();
- method public void onMetadataUpdate(android.os.Bundle);
+ public static abstract class RoutePlaybackControls.Listener extends android.media.session.RouteInterface.EventListener {
+ ctor public RoutePlaybackControls.Listener();
+ method public final void onEvent(java.lang.String, android.os.Bundle);
+ method public void onMetadataUpdate(android.media.session.MediaMetadata);
method public void onPlaybackStateChange(int);
}
- public static abstract class RouteTransportControls.Stub extends android.media.session.RouteInterface.Stub {
- ctor public RouteTransportControls.Stub(android.media.session.MediaSession);
- method public void fastForward(float);
- method public long getCapabilities();
- method public long getCurrentPosition();
- method public java.lang.String getName();
+ public final class Session {
+ method public void addCallback(android.media.session.Session.Callback);
+ method public void addCallback(android.media.session.Session.Callback, android.os.Handler);
+ method public void connect(android.media.session.RouteInfo, android.media.session.RouteOptions);
+ method public void disconnect(android.media.session.RouteInfo);
+ method public android.media.session.SessionToken getSessionToken();
+ method public android.media.session.TransportPerformer getTransportPerformer();
+ method public void publish();
+ method public void release();
+ method public void removeCallback(android.media.session.Session.Callback);
+ method public void sendEvent(java.lang.String, android.os.Bundle);
+ method public void setRouteOptions(java.util.List<android.media.session.RouteOptions>);
+ method public android.media.session.TransportPerformer setTransportPerformerEnabled();
+ }
+
+ public static abstract class Session.Callback {
+ ctor public Session.Callback();
method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
- method public final void updatePlaybackState(int);
+ method public void onMediaButton(android.content.Intent);
+ method public void onRequestRouteChange(android.media.session.RouteInfo);
+ method public void onRouteConnected(android.media.session.Route);
+ method public void onRouteDisconnected(android.media.session.Route, int);
+ }
+
+ public final class SessionController {
+ method public void addCallback(android.media.session.SessionController.Callback);
+ method public void addCallback(android.media.session.SessionController.Callback, android.os.Handler);
+ method public static android.media.session.SessionController fromToken(android.media.session.SessionToken);
+ method public android.media.session.TransportController getTransportController();
+ method public void removeCallback(android.media.session.SessionController.Callback);
+ method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+ method public void sendMediaButton(int);
+ method public void showRoutePicker();
+ }
+
+ public static abstract class SessionController.Callback {
+ ctor public SessionController.Callback();
+ method public void onEvent(java.lang.String, android.os.Bundle);
+ method public void onRouteChanged(android.media.session.RouteInfo);
+ }
+
+ public final class SessionInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.String getId();
+ method public java.lang.String getPackageName();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public final class SessionManager {
+ method public android.media.session.Session createSession(java.lang.String);
+ method public java.util.List<android.media.session.SessionController> getActiveSessions();
+ }
+
+ public class SessionToken implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
}
public final class TransportController {
@@ -24353,6 +24470,15 @@
method public void setRed(int, int);
}
+ public final class ScriptIntrinsicResize extends android.renderscript.ScriptIntrinsic {
+ method public static android.renderscript.ScriptIntrinsicResize create(android.renderscript.RenderScript);
+ method public void forEach_bicubic(android.renderscript.Allocation);
+ method public void forEach_bicubic(android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
+ method public android.renderscript.Script.FieldID getFieldID_Input();
+ method public android.renderscript.Script.KernelID getKernelID_bicubic();
+ method public void setInput(android.renderscript.Allocation);
+ }
+
public final class ScriptIntrinsicYuvToRGB extends android.renderscript.ScriptIntrinsic {
method public static android.renderscript.ScriptIntrinsicYuvToRGB create(android.renderscript.RenderScript, android.renderscript.Element);
method public void forEach(android.renderscript.Allocation);
@@ -27848,7 +27974,6 @@
public static final class TvInputManager.Session {
method public void release();
- method public void setSurface(android.view.Surface);
method public void setVolume(float);
method public void tune(android.net.Uri);
}
@@ -27870,12 +27995,22 @@
field public static final java.lang.String SERVICE_INTERFACE = "android.tv.TvInputService";
}
- public static abstract class TvInputService.TvInputSessionImpl {
+ public abstract class TvInputService.TvInputSessionImpl {
ctor public TvInputService.TvInputSessionImpl();
+ method public android.view.View onCreateOverlayView();
method public abstract void onRelease();
method public abstract boolean onSetSurface(android.view.Surface);
method public abstract void onSetVolume(float);
method public abstract boolean onTune(android.net.Uri);
+ method public void setOverlayViewEnabled(boolean);
+ }
+
+ public class TvView extends android.view.SurfaceView {
+ ctor public TvView(android.content.Context);
+ 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 void unbindTvInput();
}
}
@@ -32613,7 +32748,8 @@
method public void clearSslPreferences();
method public deprecated void clearView();
method public android.webkit.WebBackForwardList copyBackForwardList();
- method public android.print.PrintDocumentAdapter createPrintDocumentAdapter();
+ method public deprecated android.print.PrintDocumentAdapter createPrintDocumentAdapter();
+ method public android.print.PrintDocumentAdapter createPrintDocumentAdapter(java.lang.String);
method public void destroy();
method public void documentHasImages(android.os.Message);
method public void evaluateJavascript(java.lang.String, android.webkit.ValueCallback<java.lang.String>);
@@ -34966,6 +35102,7 @@
method public void setCursorVisible(boolean);
method public void setCustomSelectionActionModeCallback(android.view.ActionMode.Callback);
method public final void setEditableFactory(android.text.Editable.Factory);
+ method public void setElegantTextHeight(boolean);
method public void setEllipsize(android.text.TextUtils.TruncateAt);
method public void setEms(int);
method public void setError(java.lang.CharSequence);
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 51cb12a..edf21dd 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -33,17 +33,20 @@
import android.view.Surface;
import android.view.TextureView;
import android.view.TextureView.SurfaceTextureListener;
-import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import dalvik.system.CloseGuard;
+
+import java.lang.ref.WeakReference;
/** @hide */
public class ActivityView extends ViewGroup {
- private final String TAG = "ActivityView";
- private final boolean DEBUG = false;
+ private static final String TAG = "ActivityView";
+ private static final boolean DEBUG = false;
+ DisplayMetrics mMetrics;
private final TextureView mTextureView;
- private IActivityContainer mActivityContainer;
+ private ActivityContainerWrapper mActivityContainer;
private Activity mActivity;
private int mWidth;
private int mHeight;
@@ -75,9 +78,23 @@
throw new IllegalStateException("The ActivityView's Context is not an Activity.");
}
+ try {
+ mActivityContainer = new ActivityContainerWrapper(
+ ActivityManagerNative.getDefault().createActivityContainer(
+ mActivity.getActivityToken(), new ActivityContainerCallback(this)));
+ } catch (RemoteException e) {
+ throw new RuntimeException("ActivityView: Unable to create ActivityContainer. "
+ + e);
+ }
+
mTextureView = new TextureView(context);
mTextureView.setSurfaceTextureListener(new ActivityViewSurfaceTextureListener());
addView(mTextureView);
+
+ WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
+ mMetrics = new DisplayMetrics();
+ wm.getDefaultDisplay().getMetrics(mMetrics);
+
if (DEBUG) Log.v(TAG, "ctor()");
}
@@ -86,57 +103,8 @@
mTextureView.layout(0, 0, r - l, b - t);
}
- @Override
- protected void onAttachedToWindow() {
- if (DEBUG) Log.v(TAG, "onAttachedToWindow()");
- super.onAttachedToWindow();
- try {
- final IBinder token = mActivity.getActivityToken();
- mActivityContainer = ActivityManagerNative.getDefault().createActivityContainer(token,
- new ActivityContainerCallback());
- } catch (RemoteException e) {
- throw new IllegalStateException("ActivityView: Unable to create ActivityContainer. "
- + e);
- }
-
- attachToSurfaceWhenReady();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- if (DEBUG) Log.v(TAG, "onDetachedFromWindow(): mActivityContainer=" + mActivityContainer);
- super.onDetachedFromWindow();
- if (mActivityContainer != null) {
- detach();
- try {
- ActivityManagerNative.getDefault().deleteActivityContainer(mActivityContainer);
- } catch (RemoteException e) {
- }
- mActivityContainer = null;
- }
- }
-
- @Override
- protected void onWindowVisibilityChanged(int visibility) {
- if (DEBUG) Log.v(TAG, "onWindowVisibilityChanged(): visibility=" + visibility);
- super.onWindowVisibilityChanged(visibility);
- switch (visibility) {
- case View.VISIBLE:
- attachToSurfaceWhenReady();
- break;
- case View.INVISIBLE:
- break;
- case View.GONE:
- break;
- }
- }
-
private boolean injectInputEvent(InputEvent event) {
- try {
- return mActivityContainer != null && mActivityContainer.injectEvent(event);
- } catch (RemoteException e) {
- return false;
- }
+ return mActivityContainer != null && mActivityContainer.injectEvent(event);
}
@Override
@@ -154,40 +122,45 @@
return super.onGenericMotionEvent(event);
}
+ @Override
+ public void onAttachedToWindow() {
+ if (DEBUG) Log.v(TAG, "onAttachedToWindow(): mActivityContainer=" + mActivityContainer +
+ " mSurface=" + mSurface);
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ if (DEBUG) Log.v(TAG, "onDetachedFromWindow(): mActivityContainer=" + mActivityContainer +
+ " mSurface=" + mSurface);
+ }
+
public boolean isAttachedToDisplay() {
return mSurface != null;
}
public void startActivity(Intent intent) {
+ if (mActivityContainer == null) {
+ throw new IllegalStateException("Attempt to call startActivity after release");
+ }
if (DEBUG) Log.v(TAG, "startActivity(): intent=" + intent + " " +
(isAttachedToDisplay() ? "" : "not") + " attached");
if (mSurface != null) {
- try {
- mActivityContainer.startActivity(intent);
- } catch (RemoteException e) {
- throw new IllegalStateException("ActivityView: Unable to startActivity. " + e);
- }
+ mActivityContainer.startActivity(intent);
} else {
mQueuedIntent = intent;
mQueuedPendingIntent = null;
}
}
- private void startActivityIntentSender(IIntentSender iIntentSender) {
- try {
- mActivityContainer.startActivityIntentSender(iIntentSender);
- } catch (RemoteException e) {
- throw new IllegalStateException(
- "ActivityView: Unable to startActivity from IntentSender. " + e);
- }
- }
-
public void startActivity(IntentSender intentSender) {
+ if (mActivityContainer == null) {
+ throw new IllegalStateException("Attempt to call startActivity after release");
+ }
if (DEBUG) Log.v(TAG, "startActivityIntentSender(): intentSender=" + intentSender + " " +
(isAttachedToDisplay() ? "" : "not") + " attached");
final IIntentSender iIntentSender = intentSender.getTarget();
if (mSurface != null) {
- startActivityIntentSender(iIntentSender);
+ mActivityContainer.startActivityIntentSender(iIntentSender);
} else {
mQueuedPendingIntent = iIntentSender;
mQueuedIntent = null;
@@ -195,84 +168,102 @@
}
public void startActivity(PendingIntent pendingIntent) {
+ if (mActivityContainer == null) {
+ throw new IllegalStateException("Attempt to call startActivity after release");
+ }
if (DEBUG) Log.v(TAG, "startActivityPendingIntent(): PendingIntent=" + pendingIntent + " "
+ (isAttachedToDisplay() ? "" : "not") + " attached");
final IIntentSender iIntentSender = pendingIntent.getTarget();
if (mSurface != null) {
- startActivityIntentSender(iIntentSender);
+ mActivityContainer.startActivityIntentSender(iIntentSender);
} else {
mQueuedPendingIntent = iIntentSender;
mQueuedIntent = null;
}
}
+ public void release() {
+ if (DEBUG) Log.v(TAG, "release() mActivityContainer=" + mActivityContainer +
+ " mSurface=" + mSurface);
+ if (mActivityContainer == null) {
+ Log.e(TAG, "Duplicate call to release");
+ return;
+ }
+ mActivityContainer.release();
+ mActivityContainer = null;
+
+ if (mSurface != null) {
+ mSurface.release();
+ mSurface = null;
+ }
+
+ mTextureView.setSurfaceTextureListener(null);
+ }
+
private void attachToSurfaceWhenReady() {
final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
- if (mActivityContainer == null || surfaceTexture == null || mSurface != null) {
+ if (surfaceTexture == null || mSurface != null) {
// Either not ready to attach, or already attached.
return;
}
- WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
- DisplayMetrics metrics = new DisplayMetrics();
- wm.getDefaultDisplay().getMetrics(metrics);
-
mSurface = new Surface(surfaceTexture);
try {
- mActivityContainer.attachToSurface(mSurface, mWidth, mHeight, metrics.densityDpi);
+ mActivityContainer.setSurface(mSurface, mWidth, mHeight, mMetrics.densityDpi);
} catch (RemoteException e) {
mSurface.release();
mSurface = null;
- throw new IllegalStateException(
- "ActivityView: Unable to create ActivityContainer. " + e);
+ throw new RuntimeException("ActivityView: Unable to create ActivityContainer. " + e);
}
if (DEBUG) Log.v(TAG, "attachToSurfaceWhenReady: " + (mQueuedIntent != null ||
mQueuedPendingIntent != null ? "" : "no") + " queued intent");
if (mQueuedIntent != null) {
- startActivity(mQueuedIntent);
+ mActivityContainer.startActivity(mQueuedIntent);
mQueuedIntent = null;
} else if (mQueuedPendingIntent != null) {
- startActivityIntentSender(mQueuedPendingIntent);
+ mActivityContainer.startActivityIntentSender(mQueuedPendingIntent);
mQueuedPendingIntent = null;
}
}
- private void detach() {
- if (DEBUG) Log.d(TAG, "detach: attached=" + isAttachedToDisplay());
- if (mSurface != null) {
- try {
- mActivityContainer.detachFromDisplay();
- } catch (RemoteException e) {
- }
- mSurface.release();
- mSurface = null;
- }
- }
-
private class ActivityViewSurfaceTextureListener implements SurfaceTextureListener {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width,
int height) {
+ if (mActivityContainer == null) {
+ return;
+ }
if (DEBUG) Log.d(TAG, "onSurfaceTextureAvailable: width=" + width + " height="
+ height);
mWidth = width;
mHeight = height;
- if (mActivityContainer != null) {
- attachToSurfaceWhenReady();
- }
+ attachToSurfaceWhenReady();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width,
int height) {
+ if (mActivityContainer == null) {
+ return;
+ }
if (DEBUG) Log.d(TAG, "onSurfaceTextureSizeChanged: w=" + width + " h=" + height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
+ if (mActivityContainer == null) {
+ return true;
+ }
if (DEBUG) Log.d(TAG, "onSurfaceTextureDestroyed");
- detach();
+ mSurface.release();
+ mSurface = null;
+ try {
+ mActivityContainer.setSurface(null, mWidth, mHeight, mMetrics.densityDpi);
+ } catch (RemoteException e) {
+ throw new RuntimeException(
+ "ActivityView: Unable to set surface of ActivityContainer. " + e);
+ }
return true;
}
@@ -283,13 +274,95 @@
}
- private class ActivityContainerCallback extends IActivityContainerCallback.Stub {
+ private static class ActivityContainerCallback extends IActivityContainerCallback.Stub {
+ private final WeakReference<ActivityView> mActivityViewWeakReference;
+
+ ActivityContainerCallback(ActivityView activityView) {
+ mActivityViewWeakReference = new WeakReference<ActivityView>(activityView);
+ }
+
@Override
public void setVisible(IBinder container, boolean visible) {
- if (DEBUG) Log.v(TAG, "setVisible(): container=" + container + " visible=" + visible);
- if (visible) {
- } else {
+ if (DEBUG) Log.v(TAG, "setVisible(): container=" + container + " visible=" + visible +
+ " ActivityView=" + mActivityViewWeakReference.get());
+ }
+ }
+
+ private static class ActivityContainerWrapper {
+ private final IActivityContainer mIActivityContainer;
+ private final CloseGuard mGuard = CloseGuard.get();
+
+ ActivityContainerWrapper(IActivityContainer container) {
+ mIActivityContainer = container;
+ mGuard.open("release");
+ }
+
+ void attachToDisplay(int displayId) {
+ try {
+ mIActivityContainer.attachToDisplay(displayId);
+ } catch (RemoteException e) {
}
}
+
+ void setSurface(Surface surface, int width, int height, int density)
+ throws RemoteException {
+ mIActivityContainer.setSurface(surface, width, height, density);
+ }
+
+ int startActivity(Intent intent) {
+ try {
+ return mIActivityContainer.startActivity(intent);
+ } catch (RemoteException e) {
+ throw new RuntimeException("ActivityView: Unable to startActivity. " + e);
+ }
+ }
+
+ int startActivityIntentSender(IIntentSender intentSender) {
+ try {
+ return mIActivityContainer.startActivityIntentSender(intentSender);
+ } catch (RemoteException e) {
+ throw new RuntimeException(
+ "ActivityView: Unable to startActivity from IntentSender. " + e);
+ }
+ }
+
+ int getDisplayId() {
+ try {
+ return mIActivityContainer.getDisplayId();
+ } catch (RemoteException e) {
+ return -1;
+ }
+ }
+
+ boolean injectEvent(InputEvent event) {
+ try {
+ return mIActivityContainer.injectEvent(event);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ void release() {
+ if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: release called");
+ try {
+ mIActivityContainer.release();
+ mGuard.close();
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: finalize called");
+ try {
+ if (mGuard != null) {
+ mGuard.warnIfOpen();
+ release();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
}
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 77b5485..f1ce54a 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -67,7 +67,7 @@
import android.location.LocationManager;
import android.media.AudioManager;
import android.media.MediaRouter;
-import android.media.session.MediaSessionManager;
+import android.media.session.SessionManager;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
import android.net.INetworkPolicyManager;
@@ -639,7 +639,7 @@
registerService(MEDIA_SESSION_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
- return new MediaSessionManager(ctx);
+ return new SessionManager(ctx);
}
});
registerService(TRUST_SERVICE, new ServiceFetcher() {
diff --git a/core/java/android/app/IActivityContainer.aidl b/core/java/android/app/IActivityContainer.aidl
index 5b80e06..cc3b10c 100644
--- a/core/java/android/app/IActivityContainer.aidl
+++ b/core/java/android/app/IActivityContainer.aidl
@@ -26,10 +26,10 @@
/** @hide */
interface IActivityContainer {
void attachToDisplay(int displayId);
- void attachToSurface(in Surface surface, int width, int height, int density);
- void detachFromDisplay();
+ void setSurface(in Surface surface, int width, int height, int density);
int startActivity(in Intent intent);
int startActivityIntentSender(in IIntentSender intentSender);
int getDisplayId();
boolean injectEvent(in InputEvent event);
+ void release();
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index bb6eeda..8681f5c 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -23,6 +23,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.service.notification.INotificationListener;
+import android.service.notification.ZenModeConfig;
/** {@hide} */
interface INotificationManager
@@ -49,4 +50,7 @@
StatusBarNotification[] getActiveNotificationsFromListener(in INotificationListener token, in String[] keys);
String[] getActiveNotificationKeysFromListener(in INotificationListener token);
+
+ ZenModeConfig getZenModeConfig();
+ boolean setZenModeConfig(in ZenModeConfig config);
}
\ No newline at end of file
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ff92d82..906484a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2387,10 +2387,10 @@
/**
* Use with {@link #getSystemService} to retrieve a
- * {@link android.media.session.MediaSessionManager} for managing media Sessions.
+ * {@link android.media.session.SessionManager} for managing media Sessions.
*
* @see #getSystemService
- * @see android.media.session.MediaSessionManager
+ * @see android.media.session.SessionManager
*/
public static final String MEDIA_SESSION_SERVICE = "media_session";
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 2c53f03..bb290af 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -570,6 +570,14 @@
public static abstract class CaptureListener {
/**
+ * This constant is used to indicate that no images were captured for
+ * the request.
+ *
+ * @hide
+ */
+ public static final int NO_FRAMES_CAPTURED = -1;
+
+ /**
* This method is called when the camera device has started capturing
* the output image for the request, at the beginning of image exposure.
*
@@ -693,9 +701,12 @@
* The CameraDevice sending the callback.
* @param sequenceId
* A sequence ID returned by the {@link #capture} family of functions.
- * @param frameNumber
+ * @param lastFrameNumber
* The last frame number (returned by {@link CaptureResult#getFrameNumber}
* or {@link CaptureFailure#getFrameNumber}) in the capture sequence.
+ * The last frame number may be equal to NO_FRAMES_CAPTURED if no images
+ * were captured for this sequence. This can happen, for example, when a
+ * repeating request or burst is cleared right after being set.
*
* @see CaptureResult#getFrameNumber()
* @see CaptureFailure#getFrameNumber()
@@ -703,7 +714,7 @@
* @see CaptureFailure#getSequenceId()
*/
public void onCaptureSequenceCompleted(CameraDevice camera,
- int sequenceId, int frameNumber) {
+ int sequenceId, int lastFrameNumber) {
// default empty implementation
}
}
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 70d3c63..d8981c8 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2134,8 +2134,8 @@
* @see #SYNC_FRAME_NUMBER_UNKNOWN
* @hide
*/
- public static final Key<Integer> SYNC_FRAME_NUMBER =
- new Key<Integer>("android.sync.frameNumber", int.class);
+ public static final Key<Long> SYNC_FRAME_NUMBER =
+ new Key<Long>("android.sync.frameNumber", long.class);
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index cd44b51..ee2adac 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -292,6 +292,76 @@
return submitCaptureRequest(requests, listener, handler, /*streaming*/false);
}
+ /**
+ * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
+ * starting and stopping repeating request and flushing.
+ *
+ * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
+ * sent to HAL. Then onCaptureSequenceCompleted is immediately triggered.
+ * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
+ * is added to the list mFrameNumberRequestPairs.</p>
+ *
+ * @param requestId the request ID of the current repeating request.
+ *
+ * @param lastFrameNumber last frame number returned from binder.
+ */
+ private void checkEarlyTriggerSequenceComplete(
+ final int requestId, final long lastFrameNumber) {
+ // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
+ // was never sent to HAL. Should trigger onCaptureSequenceCompleted immediately.
+ if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) {
+ final CaptureListenerHolder holder;
+ int index = mCaptureListenerMap.indexOfKey(requestId);
+ holder = (index >= 0) ? mCaptureListenerMap.valueAt(index) : null;
+ if (holder != null) {
+ mCaptureListenerMap.removeAt(index);
+ if (DEBUG) {
+ Log.v(TAG, String.format(
+ "remove holder for requestId %d, "
+ + "because lastFrame is %d.",
+ requestId, lastFrameNumber));
+ }
+ }
+
+ if (holder != null) {
+ if (DEBUG) {
+ Log.v(TAG, "immediately trigger onCaptureSequenceCompleted because"
+ + " request did not reach HAL");
+ }
+
+ Runnable resultDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDevice.this.isClosed()) {
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "early trigger sequence complete for request %d",
+ requestId));
+ }
+ if (lastFrameNumber < Integer.MIN_VALUE
+ || lastFrameNumber > Integer.MAX_VALUE) {
+ throw new AssertionError(lastFrameNumber + " cannot be cast to int");
+ }
+ holder.getListener().onCaptureSequenceCompleted(
+ CameraDevice.this,
+ requestId,
+ (int)lastFrameNumber);
+ }
+ }
+ };
+ holder.getHandler().post(resultDispatch);
+ } else {
+ Log.w(TAG, String.format(
+ "did not register listener to request %d",
+ requestId));
+ }
+ } else {
+ mFrameNumberRequestPairs.add(
+ new SimpleEntry<Long, Integer>(lastFrameNumber,
+ requestId));
+ }
+ }
+
private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureListener listener,
Handler handler, boolean repeating) throws CameraAccessException {
@@ -313,7 +383,7 @@
try {
requestId = mRemoteDevice.submitRequestList(requestList, repeating,
/*out*/lastFrameNumberRef);
- if (!repeating) {
+ if (DEBUG) {
Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
}
} catch (CameraRuntimeException e) {
@@ -322,25 +392,21 @@
// impossible
return -1;
}
+
if (listener != null) {
mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener,
requestList, handler, repeating));
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Listen for request " + requestId + " is null");
+ }
}
long lastFrameNumber = lastFrameNumberRef.getNumber();
- /**
- * If it's the first repeating request, then returned lastFrameNumber can be
- * negative. Otherwise, it should always be non-negative.
- */
- if (((lastFrameNumber < 0) && (requestId > 0))
- || ((lastFrameNumber < 0) && (!repeating))) {
- throw new AssertionError(String.format("returned bad frame number %d",
- lastFrameNumber));
- }
+
if (repeating) {
if (mRepeatingRequestId != REQUEST_ID_NONE) {
- mFrameNumberRequestPairs.add(
- new SimpleEntry<Long, Integer>(lastFrameNumber, mRepeatingRequestId));
+ checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
}
mRepeatingRequestId = requestId;
} else {
@@ -395,12 +461,9 @@
LongParcelable lastFrameNumberRef = new LongParcelable();
mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
long lastFrameNumber = lastFrameNumberRef.getNumber();
- if ((lastFrameNumber < 0) && (requestId > 0)) {
- throw new AssertionError(String.format("returned bad frame number %d",
- lastFrameNumber));
- }
- mFrameNumberRequestPairs.add(
- new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
+
+ checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
+
} catch (CameraRuntimeException e) {
throw e.asChecked();
} catch (RemoteException e) {
@@ -443,11 +506,7 @@
mRemoteDevice.flush(/*out*/lastFrameNumberRef);
if (mRepeatingRequestId != REQUEST_ID_NONE) {
long lastFrameNumber = lastFrameNumberRef.getNumber();
- if (lastFrameNumber < 0) {
- Log.e(TAG, String.format("returned bad frame number %d", lastFrameNumber));
- }
- mFrameNumberRequestPairs.add(
- new SimpleEntry<Long, Integer>(lastFrameNumber, mRepeatingRequestId));
+ checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
mRepeatingRequestId = REQUEST_ID_NONE;
}
} catch (CameraRuntimeException e) {
@@ -582,8 +641,8 @@
*/
if (frameNumber != mCompletedFrameNumber + 1) {
throw new AssertionError(String.format(
- "result frame number %d comes out of order",
- frameNumber));
+ "result frame number %d comes out of order, should be %d + 1",
+ frameNumber, mCompletedFrameNumber));
}
mCompletedFrameNumber++;
}
@@ -607,11 +666,18 @@
final int requestId = frameNumberRequestPair.getValue();
final CaptureListenerHolder holder;
synchronized (mLock) {
- int index = CameraDevice.this.mCaptureListenerMap.indexOfKey(requestId);
- holder = (index >= 0) ? CameraDevice.this.mCaptureListenerMap.valueAt(index)
+ int index = mCaptureListenerMap.indexOfKey(requestId);
+ holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
: null;
if (holder != null) {
- CameraDevice.this.mCaptureListenerMap.removeAt(index);
+ mCaptureListenerMap.removeAt(index);
+ if (DEBUG) {
+ Log.v(TAG, String.format(
+ "remove holder for requestId %d, "
+ + "because lastFrame %d is <= %d",
+ requestId, frameNumberRequestPair.getKey(),
+ completedFrameNumber));
+ }
}
}
iter.remove();
@@ -628,11 +694,16 @@
requestId));
}
+ long lastFrameNumber = frameNumberRequestPair.getKey();
+ if (lastFrameNumber < Integer.MIN_VALUE
+ || lastFrameNumber > Integer.MAX_VALUE) {
+ throw new AssertionError(lastFrameNumber
+ + " cannot be cast to int");
+ }
holder.getListener().onCaptureSequenceCompleted(
CameraDevice.this,
requestId,
- // TODO: this is problematic, crop long to int
- frameNumberRequestPair.getKey().intValue());
+ (int)lastFrameNumber);
}
}
};
@@ -705,6 +776,9 @@
}
// Fire onCaptureSequenceCompleted
+ if (DEBUG) {
+ Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
+ }
mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
checkAndFireSequenceComplete();
@@ -764,20 +838,40 @@
CaptureResultExtras resultExtras) throws RemoteException {
int requestId = resultExtras.getRequestId();
if (DEBUG) {
- Log.d(TAG, "Received result for id " + requestId);
+ Log.v(TAG, "Received result frame " + resultExtras.getFrameNumber() + " for id "
+ + requestId);
}
- final CaptureListenerHolder holder =
- CameraDevice.this.mCaptureListenerMap.get(requestId);
+ final CaptureListenerHolder holder;
+ synchronized (mLock) {
+ holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
+ }
Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
+ // Update tracker (increment counter) when it's not a partial result.
+ if (!quirkIsPartialResult) {
+ mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/false);
+ }
+
// Check if we have a listener for this
if (holder == null) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "holder is null, early return at frame "
+ + resultExtras.getFrameNumber());
+ }
return;
}
- if (isClosed()) return;
+ if (isClosed()) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "camera is closed, early return at frame "
+ + resultExtras.getFrameNumber());
+ }
+ return;
+ }
final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
@@ -817,7 +911,6 @@
// Fire onCaptureSequenceCompleted
if (!quirkIsPartialResult) {
- mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/false);
checkAndFireSequenceComplete();
}
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index a517bc5..79673b3 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -437,6 +437,14 @@
* The behavior of the virtual display depends on the flags that are provided
* to this method. By default, virtual displays are created to be private,
* non-presentation and unsecure. Permissions may be required to use certain flags.
+ * </p><p>
+ * As of {@link android.os.Build.VERSION_CODES#KITKAT_WATCH}, the surface may
+ * be attached or detached dynamically using {@link VirtualDisplay#setSurface}.
+ * Previously, the surface had to be non-null when {@link #createVirtualDisplay}
+ * was called and could not be changed for the lifetime of the display.
+ * </p><p>
+ * Detaching the surface that backs a virtual display has a similar effect to
+ * turning off the screen.
* </p>
*
* @param name The name of the virtual display, must be non-empty.
@@ -444,7 +452,7 @@
* @param height The height of the virtual display in pixels, must be greater than 0.
* @param densityDpi The density of the virtual display in dpi, must be greater than 0.
* @param surface The surface to which the content of the virtual display should
- * be rendered, must be non-null.
+ * be rendered, or null if there is none initially.
* @param flags A combination of virtual display flags:
* {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}, {@link #VIRTUAL_DISPLAY_FLAG_PRESENTATION},
* {@link #VIRTUAL_DISPLAY_FLAG_SECURE}, or {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 3417430..a8d55e8 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -377,9 +377,6 @@
throw new IllegalArgumentException("width, height, and densityDpi must be "
+ "greater than 0");
}
- if (surface == null) {
- throw new IllegalArgumentException("surface must not be null");
- }
Binder token = new Binder();
int displayId;
@@ -404,7 +401,15 @@
}
return null;
}
- return new VirtualDisplay(this, display, token);
+ return new VirtualDisplay(this, display, token, surface);
+ }
+
+ public void setVirtualDisplaySurface(IBinder token, Surface surface) {
+ try {
+ mDm.setVirtualDisplaySurface(token, surface);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to set virtual display surface.", ex);
+ }
}
public void releaseVirtualDisplay(IBinder token) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 68eb13f..23c58c8 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -63,5 +63,8 @@
String name, int width, int height, int densityDpi, in Surface surface, int flags);
// No permissions required but must be same Uid as the creator.
+ void setVirtualDisplaySurface(in IBinder token, in Surface surface);
+
+ // No permissions required but must be same Uid as the creator.
void releaseVirtualDisplay(in IBinder token);
}
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
index 01e5bac..691d6a0 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -17,15 +17,18 @@
import android.os.IBinder;
import android.view.Display;
+import android.view.Surface;
/**
* Represents a virtual display. The content of a virtual display is rendered to a
* {@link android.view.Surface} that you must provide to {@link DisplayManager#createVirtualDisplay
* createVirtualDisplay()}.
- * <p>Because a virtual display renders to a surface provided by the application, it will be
+ * <p>
+ * Because a virtual display renders to a surface provided by the application, it will be
* released automatically when the process terminates and all remaining windows on it will
- * be forcibly removed. However, you should also explicitly call {@link #release} when you're
- * done with it.
+ * be forcibly removed. However, you should also explicitly call {@link #release} when
+ * you're done with it.
+ * </p>
*
* @see DisplayManager#createVirtualDisplay
*/
@@ -33,11 +36,14 @@
private final DisplayManagerGlobal mGlobal;
private final Display mDisplay;
private IBinder mToken;
+ private Surface mSurface;
- VirtualDisplay(DisplayManagerGlobal global, Display display, IBinder token) {
+ VirtualDisplay(DisplayManagerGlobal global, Display display, IBinder token,
+ Surface surface) {
mGlobal = global;
mDisplay = display;
mToken = token;
+ mSurface = surface;
}
/**
@@ -48,6 +54,32 @@
}
/**
+ * Gets the surface that backs the virtual display.
+ */
+ public Surface getSurface() {
+ return mSurface;
+ }
+
+ /**
+ * Sets the surface that backs the virtual display.
+ * <p>
+ * Detaching the surface that backs a virtual display has a similar effect to
+ * turning off the screen.
+ * </p><p>
+ * It is still the caller's responsibility to destroy the surface after it has
+ * been detached.
+ * </p>
+ *
+ * @param surface The surface to set, or null to detach the surface from the virtual display.
+ */
+ public void setSurface(Surface surface) {
+ if (mSurface != surface) {
+ mGlobal.setVirtualDisplaySurface(mToken, surface);
+ mSurface = surface;
+ }
+ }
+
+ /**
* Releases the virtual display and destroys its underlying surface.
* <p>
* All remaining windows on the virtual display will be forcibly removed
@@ -63,6 +95,7 @@
@Override
public String toString() {
- return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken + "}";
+ return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken
+ + ", surface=" + mSurface + "}";
}
}
diff --git a/core/java/android/net/INetworkScoreService.aidl b/core/java/android/net/INetworkScoreService.aidl
new file mode 100644
index 0000000..a72d9a0
--- /dev/null
+++ b/core/java/android/net/INetworkScoreService.aidl
@@ -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 android.net;
+
+import android.net.ScoredNetwork;
+
+/**
+ * A service for updating network scores from a network scorer application.
+ * @hide
+ */
+interface INetworkScoreService
+{
+ /**
+ * Update scores.
+ * @return whether the update was successful.
+ * @throws SecurityException if the caller is not the current active scorer.
+ */
+ boolean updateScores(in ScoredNetwork[] networks);
+
+ /**
+ * Clear all scores.
+ * @return whether the clear was successful.
+ * @throws SecurityException if the caller is neither the current active scorer nor the scorer
+ * manager.
+ */
+ boolean clearScores();
+
+ /**
+ * Set the active scorer and clear existing scores.
+ * @param packageName the package name of the new scorer to use.
+ * @return true if the operation succeeded, or false if the new package is not a valid scorer.
+ * @throws SecurityException if the caller is not the scorer manager.
+ */
+ boolean setActiveScorer(in String packageName);
+}
diff --git a/core/java/android/net/NetworkKey.java b/core/java/android/net/NetworkKey.java
index cc3ad3e..bc19658 100644
--- a/core/java/android/net/NetworkKey.java
+++ b/core/java/android/net/NetworkKey.java
@@ -19,11 +19,19 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* Information which identifies a specific network.
*
* @hide
*/
+// NOTE: Ideally, we would abstract away the details of what identifies a network of a specific
+// type, so that all networks appear the same and can be scored without concern to the network type
+// itself. However, because no such cross-type identifier currently exists in the Android framework,
+// and because systems might obtain information about networks from sources other than Android
+// devices, we need to provide identifying details about each specific network type (wifi, cell,
+// etc.) so that clients can pull out these details depending on the type of network.
public class NetworkKey implements Parcelable {
/** A wifi network, for which {@link #wifiKey} will be populated. */
@@ -79,6 +87,21 @@
}
@Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ NetworkKey that = (NetworkKey) o;
+
+ return type == that.type && Objects.equals(wifiKey, that.wifiKey);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, wifiKey);
+ }
+
+ @Override
public String toString() {
switch (type) {
case TYPE_WIFI:
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 3430547..5e61613 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -19,6 +19,9 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
/**
* Class that manages communication between network subsystems and a network scorer.
@@ -40,7 +43,7 @@
* <p>The system keeps track of a default scorer application; at any time, only this application
* will receive {@link #ACTION_SCORE_NETWORKS} broadcasts and will be permitted to call
* {@link #updateScores}. Applications may determine the current default scorer with
- * {@link #getDefaultScorerPackage()} and request to change the default scorer by sending an
+ * {@link #getActiveScorerPackage()} and request to change the default scorer by sending an
* {@link #ACTION_CHANGE_DEFAULT} broadcast with another scorer.
*
* @hide
@@ -81,38 +84,82 @@
public static final String EXTRA_NETWORKS_TO_SCORE = "networksToScore";
private final Context mContext;
+ private final INetworkScoreService mService;
/** @hide */
public NetworkScoreManager(Context context) {
mContext = context;
+ IBinder iBinder = ServiceManager.getService(Context.NETWORK_SCORE_SERVICE);
+ mService = INetworkScoreService.Stub.asInterface(iBinder);
}
/**
- * Obtain the package name of the current default network scorer.
+ * Obtain the package name of the current active network scorer.
*
- * At any time, only one scorer application will receive {@link #ACTION_SCORE_NETWORKS}
+ * <p>At any time, only one scorer application will receive {@link #ACTION_SCORE_NETWORKS}
* broadcasts and be allowed to call {@link #updateScores}. Applications may use this method to
* determine the current scorer and offer the user the ability to select a different scorer via
* the {@link #ACTION_CHANGE_DEFAULT} intent.
- * @return the full package name of the current default scorer, or null if there is no active
+ * @return the full package name of the current active scorer, or null if there is no active
* scorer.
*/
- public String getDefaultScorerPackage() {
- // TODO: Implement.
- return null;
+ public String getActiveScorerPackage() {
+ return NetworkScorerAppManager.getActiveScorer(mContext);
}
/**
* Update network scores.
*
- * This may be called at any time to re-score active networks. Scores will generally be updated
- * quickly, but if this method is called too frequently, the scores may be held and applied at
- * a later time.
+ * <p>This may be called at any time to re-score active networks. Scores will generally be
+ * updated quickly, but if this method is called too frequently, the scores may be held and
+ * applied at a later time.
*
* @param networks the networks which have been scored by the scorer.
- * @throws SecurityException if the caller is not the default scorer.
+ * @return whether the update was successful.
+ * @throws SecurityException if the caller is not the active scorer.
*/
- public void updateScores(ScoredNetwork[] networks) throws SecurityException {
- // TODO: Implement.
+ public boolean updateScores(ScoredNetwork[] networks) throws SecurityException {
+ try {
+ return mService.updateScores(networks);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Clear network scores.
+ *
+ * <p>Should be called when all scores need to be invalidated, i.e. because the scoring
+ * algorithm has changed and old scores can no longer be compared to future scores.
+ *
+ * <p>Note that scores will be cleared automatically when the active scorer changes, as scores
+ * from one scorer cannot be compared to those from another scorer.
+ *
+ * @return whether the clear was successful.
+ * @throws SecurityException if the caller is not the active scorer or privileged.
+ */
+ public boolean clearScores() throws SecurityException {
+ try {
+ return mService.clearScores();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Set the active scorer to a new package and clear existing scores.
+ *
+ * @return true if the operation succeeded, or false if the new package is not a valid scorer.
+ * @throws SecurityException if the caller does not hold the
+ * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission indicating that
+ * it can manage scorer applications.
+ * @hide
+ */
+ public boolean setActiveScorer(String packageName) throws SecurityException {
+ try {
+ return mService.setActiveScorer(packageName);
+ } catch (RemoteException e) {
+ return false;
+ }
}
}
diff --git a/core/java/android/net/NetworkScorerApplication.java b/core/java/android/net/NetworkScorerAppManager.java
similarity index 88%
rename from core/java/android/net/NetworkScorerApplication.java
rename to core/java/android/net/NetworkScorerAppManager.java
index b137ad3..726208a 100644
--- a/core/java/android/net/NetworkScorerApplication.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -26,6 +26,7 @@
import android.provider.Settings;
import android.provider.Settings.Global;
import android.text.TextUtils;
+import android.util.Log;
import java.util.ArrayList;
import java.util.Collection;
@@ -36,13 +37,14 @@
*
* @hide
*/
-public final class NetworkScorerApplication {
+public final class NetworkScorerAppManager {
+ private static final String TAG = "NetworkScorerAppManager";
private static final Intent SCORE_INTENT =
new Intent(NetworkScoreManager.ACTION_SCORE_NETWORKS);
/** This class cannot be instantiated. */
- private NetworkScorerApplication() {}
+ private NetworkScorerAppManager() {}
/**
* Returns the list of available scorer app package names.
@@ -111,30 +113,38 @@
* @param context the context of the calling application
* @param packageName the packageName of the new scorer to use. If null, scoring will be
* disabled. Otherwise, the scorer will only be set if it is a valid scorer application.
+ * @return true if the scorer was changed, or false if the package is not a valid scorer.
*/
- public static void setActiveScorer(Context context, String packageName) {
+ public static boolean setActiveScorer(Context context, String packageName) {
String oldPackageName = Settings.Global.getString(context.getContentResolver(),
Settings.Global.NETWORK_SCORER_APP);
if (TextUtils.equals(oldPackageName, packageName)) {
// No change.
- return;
+ return true;
}
+ Log.i(TAG, "Changing network scorer from " + oldPackageName + " to " + packageName);
+
if (packageName == null) {
Settings.Global.putString(context.getContentResolver(), Global.NETWORK_SCORER_APP,
null);
+ return true;
} else {
// We only make the change if the new package is valid.
Collection<String> applications = getAllValidScorers(context);
if (isPackageValidScorer(applications, packageName)) {
Settings.Global.putString(context.getContentResolver(),
Settings.Global.NETWORK_SCORER_APP, packageName);
+ return true;
+ } else {
+ Log.w(TAG, "Requested network scorer is not valid: " + packageName);
+ return false;
}
}
}
/** Determine whether the application with the given UID is the enabled scorer. */
- public static boolean isCallerDefaultScorer(Context context, int callingUid) {
+ public static boolean isCallerActiveScorer(Context context, int callingUid) {
String defaultApp = getActiveScorer(context);
if (defaultApp == null) {
return false;
diff --git a/core/java/android/net/RssiCurve.java b/core/java/android/net/RssiCurve.java
index 7af7998..33e81c2 100644
--- a/core/java/android/net/RssiCurve.java
+++ b/core/java/android/net/RssiCurve.java
@@ -19,6 +19,9 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Arrays;
+import java.util.Objects;
+
/**
* A curve defining the network score over a range of RSSI values.
*
@@ -94,6 +97,30 @@
out.writeByteArray(rssiBuckets);
}
+ /**
+ * Determine if two RSSI curves are defined in the same way.
+ *
+ * <p>Note that two curves can be equivalent but defined differently, e.g. if one bucket in one
+ * curve is split into two buckets in another. For the purpose of this method, these curves are
+ * not considered equal to each other.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ RssiCurve rssiCurve = (RssiCurve) o;
+
+ return start == rssiCurve.start &&
+ bucketWidth == rssiCurve.bucketWidth &&
+ Arrays.equals(rssiBuckets, rssiCurve.rssiBuckets);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(start, bucketWidth, rssiBuckets);
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/net/ScoredNetwork.java b/core/java/android/net/ScoredNetwork.java
index 8af3c3c..7902313 100644
--- a/core/java/android/net/ScoredNetwork.java
+++ b/core/java/android/net/ScoredNetwork.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* A network identifier along with a score for the quality of that network.
*
@@ -80,6 +82,22 @@
}
@Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ScoredNetwork that = (ScoredNetwork) o;
+
+ return Objects.equals(networkKey, that.networkKey) &&
+ Objects.equals(rssiCurve, that.rssiCurve);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkKey, rssiCurve);
+ }
+
+ @Override
public String toString() {
return "ScoredNetwork[key=" + networkKey + ",score=" + rssiCurve + "]";
}
diff --git a/core/java/android/net/WifiKey.java b/core/java/android/net/WifiKey.java
index ffcd85a..9e92e89 100644
--- a/core/java/android/net/WifiKey.java
+++ b/core/java/android/net/WifiKey.java
@@ -19,6 +19,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
import java.util.regex.Pattern;
/**
@@ -87,6 +88,21 @@
}
@Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ WifiKey wifiKey = (WifiKey) o;
+
+ return Objects.equals(ssid, wifiKey.ssid) && Objects.equals(bssid, wifiKey.bssid);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(ssid, bssid);
+ }
+
+ @Override
public String toString() {
return "WifiKey[SSID=" + ssid + ",BSSID=" + bssid + "]";
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 426f21e..e640649 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2861,29 +2861,21 @@
int oldTemp = -1;
int oldVolt = -1;
long lastTime = -1;
+ long firstTime = -1;
- public void printNextItem(PrintWriter pw, HistoryItem rec, long now, boolean checkin,
+ public void printNextItem(PrintWriter pw, HistoryItem rec, long baseTime, boolean checkin,
boolean verbose) {
if (!checkin) {
pw.print(" ");
- if (now >= 0) {
- TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
- } else {
- TimeUtils.formatDuration(rec.time, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
- }
+ TimeUtils.formatDuration(rec.time - baseTime, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
pw.print(" (");
pw.print(rec.numReadInts);
pw.print(") ");
} else {
if (lastTime < 0) {
- if (now >= 0) {
- pw.print("@");
- pw.print(rec.time-now);
- } else {
- pw.print(rec.time);
- }
+ pw.print(rec.time - baseTime);
} else {
- pw.print(rec.time-lastTime);
+ pw.print(rec.time - lastTime);
}
lastTime = rec.time;
}
@@ -3132,10 +3124,27 @@
pw.println("):");
HistoryPrinter hprinter = new HistoryPrinter();
long lastTime = -1;
+ long baseTime = -1;
+ boolean printed = false;
while (getNextHistoryLocked(rec)) {
lastTime = rec.time;
+ if (baseTime < 0) {
+ baseTime = lastTime;
+ }
if (rec.time >= histStart) {
- hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, false,
+ if (histStart >= 0 && !printed) {
+ if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
+ printed = true;
+ } else if (rec.currentTime != 0) {
+ printed = true;
+ byte cmd = rec.cmd;
+ rec.cmd = HistoryItem.CMD_CURRENT_TIME;
+ hprinter.printNextItem(pw, rec, baseTime, false,
+ (flags&DUMP_VERBOSE) != 0);
+ rec.cmd = cmd;
+ }
+ }
+ hprinter.printNextItem(pw, rec, baseTime, false,
(flags&DUMP_VERBOSE) != 0);
}
}
@@ -3152,8 +3161,12 @@
try {
pw.println("Old battery History:");
HistoryPrinter hprinter = new HistoryPrinter();
+ long baseTime = -1;
while (getNextOldHistoryLocked(rec)) {
- hprinter.printNextItem(pw, rec, now, false, (flags&DUMP_VERBOSE) != 0);
+ if (baseTime < 0) {
+ baseTime = rec.time;
+ }
+ hprinter.printNextItem(pw, rec, baseTime, false, (flags&DUMP_VERBOSE) != 0);
}
pw.println();
} finally {
@@ -3226,20 +3239,42 @@
pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
pw.print(HISTORY_STRING_POOL); pw.print(',');
pw.print(i);
- pw.print(',');
- pw.print(getHistoryTagPoolString(i));
- pw.print(',');
+ pw.print(",");
pw.print(getHistoryTagPoolUid(i));
+ pw.print(",\"");
+ String str = getHistoryTagPoolString(i);
+ str = str.replace("\\", "\\\\");
+ str = str.replace("\"", "\\\"");
+ pw.print(str);
+ pw.print("\"");
pw.println();
}
HistoryPrinter hprinter = new HistoryPrinter();
long lastTime = -1;
+ long baseTime = -1;
+ boolean printed = false;
while (getNextHistoryLocked(rec)) {
lastTime = rec.time;
+ if (baseTime < 0) {
+ baseTime = lastTime;
+ }
if (rec.time >= histStart) {
+ if (histStart >= 0 && !printed) {
+ if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
+ printed = true;
+ } else if (rec.currentTime != 0) {
+ printed = true;
+ byte cmd = rec.cmd;
+ rec.cmd = HistoryItem.CMD_CURRENT_TIME;
+ pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+ pw.print(HISTORY_DATA); pw.print(',');
+ hprinter.printNextItem(pw, rec, baseTime, true, false);
+ rec.cmd = cmd;
+ }
+ }
pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
pw.print(HISTORY_DATA); pw.print(',');
- hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, true, false);
+ hprinter.printNextItem(pw, rec, baseTime, true, false);
}
}
if (histStart >= 0) {
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 63e15a9..19be2c8 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -487,7 +487,7 @@
* new changes in behavior:</p>
* <ul>
* <li> {@link android.content.Context#bindService Context.bindService} now
- * requires an explicit Intent, and will throw an exception if given an explicit
+ * requires an explicit Intent, and will throw an exception if given an implicit
* Intent.</li>
* </ul>
*/
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index e96398a..e98a26b 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -42,6 +42,7 @@
private static final String ENV_SECONDARY_STORAGE = "SECONDARY_STORAGE";
private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
private static final String ENV_OEM_ROOT = "OEM_ROOT";
+ private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT";
/** {@hide} */
public static final String DIR_ANDROID = "Android";
@@ -57,6 +58,7 @@
private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
+ private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
private static final File DIR_MEDIA_STORAGE = getDirectory(ENV_MEDIA_STORAGE, "/data/media");
private static final String CANONCIAL_EMULATED_STORAGE_TARGET = getCanonicalPathOrNull(
@@ -225,6 +227,15 @@
}
/**
+ * Return root directory of the "vendor" partition that holds vendor-provided
+ * software that should persist across simple reflashing of the "system" partition.
+ * @hide
+ */
+ public static File getVendorDirectory() {
+ return DIR_VENDOR_ROOT;
+ }
+
+ /**
* Gets the system directory available for secure storage.
* If Encrypted File system is enabled, it returns an encrypted directory (/data/secure/system).
* Otherwise, it returns the unencrypted /data/system directory.
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 325b2e6..ff16f6c 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -329,6 +329,11 @@
if (preferenceScreen != null) {
preferenceScreen.bind(getListView());
}
+ onBindPreferences();
+ }
+
+ /** @hide */
+ protected void onBindPreferences() {
}
/** @hide */
diff --git a/core/java/android/preference/PreferenceGroupAdapter.java b/core/java/android/preference/PreferenceGroupAdapter.java
index 9b41ff0..381a5f0 100644
--- a/core/java/android/preference/PreferenceGroupAdapter.java
+++ b/core/java/android/preference/PreferenceGroupAdapter.java
@@ -20,7 +20,6 @@
import java.util.Collections;
import java.util.List;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.preference.Preference.OnPreferenceChangeInternalListener;
@@ -243,6 +242,7 @@
if (position == mHighlightedPosition && mHighlightedDrawable != null) {
result.setBackgroundDrawable(mHighlightedDrawable);
}
+ result.setTag(preference.getKey());
return result;
}
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index ae24968..cfab1b3 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -109,14 +109,18 @@
* An intent to perform a search for music media and automatically play content from the
* result when possible. This can be fired, for example, by the result of a voice recognition
* command to listen to music.
- * <p>
- * Contains the {@link android.app.SearchManager#QUERY} extra, which is a string
- * that can contain any type of unstructured music search, like the name of an artist,
- * an album, a song, a genre, or any combination of these.
- * <p>
- * Because this intent includes an open-ended unstructured search string, it makes the most
- * sense for apps that can support large-scale search of music, such as services connected
- * to an online database of music which can be streamed and played on the device.
+ * <p>This intent always includes the {@link android.provider.MediaStore#EXTRA_MEDIA_FOCUS}
+ * and {@link android.app.SearchManager#QUERY} extras. The
+ * {@link android.provider.MediaStore#EXTRA_MEDIA_FOCUS} extra determines the search mode, and
+ * the value of the {@link android.app.SearchManager#QUERY} extra depends on the search mode.
+ * For more information about the search modes for this intent, see
+ * <a href="{@docRoot}guide/components/intents-common.html#PlaySearch">Play music based
+ * on a search query</a> in <a href="{@docRoot}guide/components/intents-common.html">Common
+ * Intents</a>.</p>
+ *
+ * <p>This intent makes the most sense for apps that can support large-scale search of music,
+ * such as services connected to an online database of music which can be streamed and played
+ * on the device.</p>
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH =
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1e202ca..2ce6210 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6120,6 +6120,13 @@
}
/**
+ * Opaque value, changes when persisted zen mode configuration changes.
+ *
+ * @hide
+ */
+ public static final String ZEN_MODE_CONFIG_ETAG = "zen_mode_config_etag";
+
+ /**
* Defines global heads up toggle. One of HEADS_UP_OFF, HEADS_UP_ON.
*
* @hide
diff --git a/core/java/android/service/notification/ZenModeConfig.aidl b/core/java/android/service/notification/ZenModeConfig.aidl
new file mode 100644
index 0000000..c73b75e
--- /dev/null
+++ b/core/java/android/service/notification/ZenModeConfig.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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;
+
+parcelable ZenModeConfig;
+
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
new file mode 100644
index 0000000..925ddcf
--- /dev/null
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -0,0 +1,234 @@
+/**
+ * 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.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Persisted configuration for zen mode.
+ *
+ * @hide
+ */
+public class ZenModeConfig implements Parcelable {
+
+ public static final String SLEEP_MODE_NIGHTS = "nights";
+ public static final String SLEEP_MODE_WEEKNIGHTS = "weeknights";
+
+ private static final int XML_VERSION = 1;
+ private static final String ZEN_TAG = "zen";
+ private static final String ZEN_ATT_VERSION = "version";
+ private static final String ALLOW_TAG = "allow";
+ private static final String ALLOW_ATT_CALLS = "calls";
+ private static final String ALLOW_ATT_MESSAGES = "messages";
+ private static final String SLEEP_TAG = "sleep";
+ private static final String SLEEP_ATT_MODE = "mode";
+
+ private static final String SLEEP_ATT_START_HR = "startHour";
+ private static final String SLEEP_ATT_START_MIN = "startMin";
+ private static final String SLEEP_ATT_END_HR = "endHour";
+ private static final String SLEEP_ATT_END_MIN = "endMin";
+
+ public boolean allowCalls;
+ public boolean allowMessages;
+
+ public String sleepMode;
+ public int sleepStartHour;
+ public int sleepStartMinute;
+ public int sleepEndHour;
+ public int sleepEndMinute;
+
+ public ZenModeConfig() { }
+
+ public ZenModeConfig(Parcel source) {
+ allowCalls = source.readInt() == 1;
+ allowMessages = source.readInt() == 1;
+ if (source.readInt() == 1) {
+ sleepMode = source.readString();
+ }
+ sleepStartHour = source.readInt();
+ sleepStartMinute = source.readInt();
+ sleepEndHour = source.readInt();
+ sleepEndMinute = source.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(allowCalls ? 1 : 0);
+ dest.writeInt(allowMessages ? 1 : 0);
+ if (sleepMode != null) {
+ dest.writeInt(1);
+ dest.writeString(sleepMode);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeInt(sleepStartHour);
+ dest.writeInt(sleepStartMinute);
+ dest.writeInt(sleepEndHour);
+ dest.writeInt(sleepEndMinute);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
+ .append("allowCalls=").append(allowCalls)
+ .append(",allowMessages=").append(allowMessages)
+ .append(",sleepMode=").append(sleepMode)
+ .append(",sleepStart=").append(sleepStartHour).append('.').append(sleepStartMinute)
+ .append(",sleepEnd=").append(sleepEndHour).append('.').append(sleepEndMinute)
+ .append(']').toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ZenModeConfig)) return false;
+ if (o == this) return true;
+ final ZenModeConfig other = (ZenModeConfig) o;
+ return other.allowCalls == allowCalls
+ && other.allowMessages == allowMessages
+ && Objects.equals(other.sleepMode, sleepMode)
+ && other.sleepStartHour == sleepStartHour
+ && other.sleepStartMinute == sleepStartMinute
+ && other.sleepEndHour == sleepEndHour
+ && other.sleepEndMinute == sleepEndMinute;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(allowCalls, allowMessages, sleepMode, sleepStartHour,
+ sleepStartMinute, sleepEndHour, sleepEndMinute);
+ }
+
+ public boolean isValid() {
+ return isValidHour(sleepStartHour) && isValidMinute(sleepStartMinute)
+ && isValidHour(sleepEndHour) && isValidMinute(sleepEndMinute)
+ && (sleepMode == null || sleepMode.equals(SLEEP_MODE_NIGHTS)
+ || sleepMode.equals(SLEEP_MODE_WEEKNIGHTS));
+ }
+
+ public static ZenModeConfig readXml(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int type = parser.getEventType();
+ if (type != XmlPullParser.START_TAG) return null;
+ String tag = parser.getName();
+ if (!ZEN_TAG.equals(tag)) return null;
+ final ZenModeConfig rt = new ZenModeConfig();
+ final int version = Integer.parseInt(parser.getAttributeValue(null, ZEN_ATT_VERSION));
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ tag = parser.getName();
+ if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) return rt;
+ if (type == XmlPullParser.START_TAG) {
+ if (ALLOW_TAG.equals(tag)) {
+ rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, false);
+ rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, false);
+ } else if (SLEEP_TAG.equals(tag)) {
+ final String mode = parser.getAttributeValue(null, SLEEP_ATT_MODE);
+ rt.sleepMode = (SLEEP_MODE_NIGHTS.equals(mode)
+ || SLEEP_MODE_WEEKNIGHTS.equals(mode)) ? mode : null;
+ final int startHour = safeInt(parser, SLEEP_ATT_START_HR, 0);
+ final int startMinute = safeInt(parser, SLEEP_ATT_START_MIN, 0);
+ final int endHour = safeInt(parser, SLEEP_ATT_END_HR, 0);
+ final int endMinute = safeInt(parser, SLEEP_ATT_END_MIN, 0);
+ rt.sleepStartHour = isValidHour(startHour) ? startHour : 0;
+ rt.sleepStartMinute = isValidMinute(startMinute) ? startMinute : 0;
+ rt.sleepEndHour = isValidHour(endHour) ? endHour : 0;
+ rt.sleepEndMinute = isValidMinute(endMinute) ? endMinute : 0;
+ }
+ }
+ }
+ return rt;
+ }
+
+ public void writeXml(XmlSerializer out) throws IOException {
+ out.startTag(null, ZEN_TAG);
+ out.attribute(null, ZEN_ATT_VERSION, Integer.toString(XML_VERSION));
+
+ out.startTag(null, ALLOW_TAG);
+ out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls));
+ out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages));
+ out.endTag(null, ALLOW_TAG);
+
+ out.startTag(null, SLEEP_TAG);
+ if (sleepMode != null) {
+ out.attribute(null, SLEEP_ATT_MODE, sleepMode);
+ }
+ out.attribute(null, SLEEP_ATT_START_HR, Integer.toString(sleepStartHour));
+ out.attribute(null, SLEEP_ATT_START_MIN, Integer.toString(sleepStartMinute));
+ out.attribute(null, SLEEP_ATT_END_HR, Integer.toString(sleepEndHour));
+ out.attribute(null, SLEEP_ATT_END_MIN, Integer.toString(sleepEndMinute));
+ out.endTag(null, SLEEP_TAG);
+
+ out.endTag(null, ZEN_TAG);
+ }
+
+ public static boolean isValidHour(int val) {
+ return val >= 0 && val < 24;
+ }
+
+ public static boolean isValidMinute(int val) {
+ return val >= 0 && val < 60;
+ }
+
+ private static boolean safeBoolean(XmlPullParser parser, String att, boolean defValue) {
+ final String val = parser.getAttributeValue(null, att);
+ if (TextUtils.isEmpty(val)) return defValue;
+ return Boolean.valueOf(val);
+ }
+
+ private static int safeInt(XmlPullParser parser, String att, int defValue) {
+ final String val = parser.getAttributeValue(null, att);
+ if (TextUtils.isEmpty(val)) return defValue;
+ return Integer.valueOf(val);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public ZenModeConfig copy() {
+ final Parcel parcel = Parcel.obtain();
+ try {
+ writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ return new ZenModeConfig(parcel);
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+ public static final Parcelable.Creator<ZenModeConfig> CREATOR
+ = new Parcelable.Creator<ZenModeConfig>() {
+ @Override
+ public ZenModeConfig createFromParcel(Parcel source) {
+ return new ZenModeConfig(source);
+ }
+
+ @Override
+ public ZenModeConfig[] newArray(int size) {
+ return new ZenModeConfig[size];
+ }
+ };
+}
diff --git a/core/java/android/tv/ITvInputManager.aidl b/core/java/android/tv/ITvInputManager.aidl
index a927dc9..a4c99e4 100644
--- a/core/java/android/tv/ITvInputManager.aidl
+++ b/core/java/android/tv/ITvInputManager.aidl
@@ -17,6 +17,7 @@
package android.tv;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.net.Uri;
import android.tv.ITvInputClient;
import android.tv.TvInputInfo;
@@ -40,4 +41,9 @@
void setSurface(in IBinder sessionToken, in Surface surface, int userId);
void setVolume(in IBinder sessionToken, float volume, int userId);
void tune(in IBinder sessionToken, in Uri channelUri, int userId);
+
+ void createOverlayView(in IBinder sessionToken, in IBinder windowToken, in Rect frame,
+ int userId);
+ void relayoutOverlayView(in IBinder sessionToken, in Rect frame, int userId);
+ void removeOverlayView(in IBinder sessionToken, int userId);
}
diff --git a/core/java/android/tv/ITvInputService.aidl b/core/java/android/tv/ITvInputService.aidl
index d80f286..672784f 100644
--- a/core/java/android/tv/ITvInputService.aidl
+++ b/core/java/android/tv/ITvInputService.aidl
@@ -17,7 +17,6 @@
package android.tv;
import android.tv.ITvInputServiceCallback;
-import android.tv.ITvInputSession;
import android.tv.ITvInputSessionCallback;
/**
diff --git a/core/java/android/tv/ITvInputSession.aidl b/core/java/android/tv/ITvInputSession.aidl
index d379d2d..32fee4b 100644
--- a/core/java/android/tv/ITvInputSession.aidl
+++ b/core/java/android/tv/ITvInputSession.aidl
@@ -16,6 +16,7 @@
package android.tv;
+import android.graphics.Rect;
import android.net.Uri;
import android.view.Surface;
@@ -31,4 +32,8 @@
// is to introduce some new concepts that will solve a number of problems in audio policy today.
void setVolume(float volume);
void tune(in Uri channelUri);
+
+ void createOverlayView(in IBinder windowToken, in Rect frame);
+ void relayoutOverlayView(in Rect frame);
+ void removeOverlayView();
}
diff --git a/core/java/android/tv/ITvInputSessionWrapper.java b/core/java/android/tv/ITvInputSessionWrapper.java
index 66fe5e1..a6e0877 100644
--- a/core/java/android/tv/ITvInputSessionWrapper.java
+++ b/core/java/android/tv/ITvInputSessionWrapper.java
@@ -17,13 +17,16 @@
package android.tv;
import android.content.Context;
+import android.graphics.Rect;
import android.net.Uri;
+import android.os.IBinder;
import android.os.Message;
import android.tv.TvInputService.TvInputSessionImpl;
import android.util.Log;
import android.view.Surface;
import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.SomeArgs;
/**
* Implements the internal ITvInputSession interface to convert incoming calls on to it back to
@@ -38,6 +41,9 @@
private static final int DO_SET_SURFACE = 2;
private static final int DO_SET_VOLUME = 3;
private static final int DO_TUNE = 4;
+ private static final int DO_CREATE_OVERLAY_VIEW = 5;
+ 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;
@@ -71,6 +77,20 @@
mTvInputSession.tune((Uri) msg.obj);
return;
}
+ case DO_CREATE_OVERLAY_VIEW: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mTvInputSession.createOverlayView((IBinder) args.arg1, (Rect) args.arg2);
+ args.recycle();
+ return;
+ }
+ case DO_RELAYOUT_OVERLAY_VIEW: {
+ mTvInputSession.relayoutOverlayView((Rect) msg.obj);
+ return;
+ }
+ case DO_REMOVE_OVERLAY_VIEW: {
+ mTvInputSession.removeOverlayView(true);
+ return;
+ }
default: {
Log.w(TAG, "Unhandled message code: " + msg.what);
return;
@@ -97,4 +117,20 @@
public void tune(Uri channelUri) {
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_TUNE, channelUri));
}
+
+ @Override
+ public void createOverlayView(IBinder windowToken, Rect frame) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_OVERLAY_VIEW, windowToken,
+ frame));
+ }
+
+ @Override
+ public void relayoutOverlayView(Rect frame) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RELAYOUT_OVERLAY_VIEW, frame));
+ }
+
+ @Override
+ public void removeOverlayView() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_OVERLAY_VIEW));
+ }
}
diff --git a/core/java/android/tv/TvInputManager.java b/core/java/android/tv/TvInputManager.java
index 4cf2b35..05f0b9c 100644
--- a/core/java/android/tv/TvInputManager.java
+++ b/core/java/android/tv/TvInputManager.java
@@ -17,6 +17,7 @@
package android.tv;
import android.content.ComponentName;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
@@ -24,6 +25,7 @@
import android.util.Log;
import android.util.SparseArray;
import android.view.Surface;
+import android.view.View;
import java.util.ArrayList;
import java.util.HashMap;
@@ -320,8 +322,8 @@
/** The Session provides the per-session functionality of TV inputs. */
public static final class Session {
private final ITvInputManager mService;
- private final IBinder mToken;
private final int mUserId;
+ private IBinder mToken;
/** @hide */
private Session(ComponentName name, IBinder token, ITvInputManager service, int userId) {
@@ -332,10 +334,16 @@
/**
* Releases this session.
+ *
+ * @throws IllegalStateException if the session has been already released.
*/
public void release() {
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
try {
mService.releaseSession(mToken, mUserId);
+ mToken = null;
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -345,8 +353,12 @@
* Sets the {@link android.view.Surface} for this session.
*
* @param surface A {@link android.view.Surface} used to render video.
+ * @throws IllegalStateException if the session has been already released.
*/
- public void setSurface(Surface surface) {
+ void setSurface(Surface surface) {
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
// surface can be null.
try {
mService.setSurface(mToken, surface, mUserId);
@@ -360,8 +372,12 @@
*
* @param volume A volume value between 0.0f to 1.0f.
* @throws IllegalArgumentException if the volume value is out of range.
+ * @throws IllegalStateException if the session has been already released.
*/
public void setVolume(float volume) {
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
try {
if (volume < 0.0f || volume > 1.0f) {
throw new IllegalArgumentException("volume should be between 0.0f and 1.0f");
@@ -377,16 +393,90 @@
*
* @param channelUri The URI of a channel.
* @throws IllegalArgumentException if the argument is {@code null}.
+ * @throws IllegalStateException if the session has been already released.
*/
public void tune(Uri channelUri) {
if (channelUri == null) {
throw new IllegalArgumentException("channelUri cannot be null");
}
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
try {
mService.tune(mToken, channelUri, mUserId);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
+
+ /**
+ * Creates an overlay view. Once the overlay view is created, {@link #relayoutOverlayView}
+ * should be called whenever the layout of its containing view is changed.
+ * {@link #removeOverlayView()} should be called to remove the overlay view.
+ * Since a session can have only one overlay view, this method should be called only once
+ * or it can be called again after calling {@link #removeOverlayView()}.
+ *
+ * @param view A view playing TV.
+ * @param frame A position of the overlay view.
+ * @throws IllegalArgumentException if any of the arguments is {@code null}.
+ * @throws IllegalStateException if {@code view} is not attached to a window or
+ * if the session has been already released.
+ */
+ void createOverlayView(View view, Rect frame) {
+ if (view == null) {
+ throw new IllegalArgumentException("view cannot be null");
+ }
+ if (frame == null) {
+ throw new IllegalArgumentException("frame cannot be null");
+ }
+ if (view.getWindowToken() == null) {
+ throw new IllegalStateException("view must be attached to a window");
+ }
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
+ try {
+ mService.createOverlayView(mToken, view.getWindowToken(), frame, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Relayouts the current overlay view.
+ *
+ * @param frame A new position of the overlay view.
+ * @throws IllegalArgumentException if the arguments is {@code null}.
+ * @throws IllegalStateException if the session has been already released.
+ */
+ void relayoutOverlayView(Rect frame) {
+ if (frame == null) {
+ throw new IllegalArgumentException("frame cannot be null");
+ }
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
+ try {
+ mService.relayoutOverlayView(mToken, frame, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Removes the current overlay view.
+ *
+ * @throws IllegalStateException if the session has been already released.
+ */
+ void removeOverlayView() {
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
+ try {
+ mService.removeOverlayView(mToken, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
}
diff --git a/core/java/android/tv/TvInputService.java b/core/java/android/tv/TvInputService.java
index d7f6c32..80eb407 100644
--- a/core/java/android/tv/TvInputService.java
+++ b/core/java/android/tv/TvInputService.java
@@ -18,7 +18,10 @@
import android.app.Service;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
@@ -26,7 +29,10 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
+import android.view.Gravity;
import android.view.Surface;
+import android.view.View;
+import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
@@ -125,7 +131,37 @@
/**
* Base class for derived classes to implement to provide {@link TvInputManager.Session}.
*/
- public abstract static class TvInputSessionImpl {
+ public abstract class TvInputSessionImpl {
+ private final WindowManager mWindowManager;
+ private WindowManager.LayoutParams mWindowParams;
+ private View mOverlayView;
+ private boolean mOverlayViewEnabled;
+ private IBinder mWindowToken;
+ private Rect mOverlayFrame;
+
+ public TvInputSessionImpl() {
+ mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
+ }
+
+ public void setOverlayViewEnabled(final boolean enable) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (enable == mOverlayViewEnabled) {
+ return;
+ }
+ mOverlayViewEnabled = enable;
+ if (enable) {
+ if (mWindowToken != null) {
+ createOverlayView(mWindowToken, mOverlayFrame);
+ }
+ } else {
+ removeOverlayView(false);
+ }
+ }
+ });
+ }
+
/**
* Called when the session is released.
*/
@@ -157,11 +193,22 @@
public abstract boolean onTune(Uri channelUri);
/**
+ * Called when an application requests to create an overlay view. Each session
+ * implementation can override this method and return its own view.
+ *
+ * @return a view attached to the overlay window
+ */
+ public View onCreateOverlayView() {
+ return null;
+ }
+
+ /**
* This method is called when the application would like to stop using the current input
* session.
*/
void release() {
onRelease();
+ removeOverlayView(true);
}
/**
@@ -186,6 +233,87 @@
onTune(channelUri);
// TODO: Handle failure.
}
+
+ /**
+ * 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.
+ */
+ void createOverlayView(IBinder windowToken, Rect frame) {
+ if (mOverlayView != null) {
+ mWindowManager.removeView(mOverlayView);
+ mOverlayView = null;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "create overlay view(" + frame + ")");
+ }
+ mWindowToken = windowToken;
+ mOverlayFrame = frame;
+ if (!mOverlayViewEnabled) {
+ return;
+ }
+ mOverlayView = onCreateOverlayView();
+ if (mOverlayView == null) {
+ return;
+ }
+ // TvView's window type is TYPE_APPLICATION_MEDIA and we want to create
+ // an overlay window above the media window but below the application window.
+ int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+ // We make the overlay view non-focusable and non-touchable so that
+ // the application that owns the window token can decide whether to consume or
+ // dispatch the input events.
+ int flag = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ mWindowParams = new WindowManager.LayoutParams(
+ frame.right - frame.left, frame.bottom - frame.top,
+ frame.left, frame.top, type, flag, PixelFormat.TRANSPARENT);
+ mWindowParams.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ mWindowParams.gravity = Gravity.START | Gravity.TOP;
+ mWindowParams.token = windowToken;
+ mWindowManager.addView(mOverlayView, mWindowParams);
+ }
+
+ /**
+ * Relayouts the current overlay view.
+ *
+ * @param frame A new position of the overlay view.
+ */
+ void relayoutOverlayView(Rect frame) {
+ if (DEBUG) {
+ Log.d(TAG, "relayout overlay view(" + frame + ")");
+ }
+ mOverlayFrame = frame;
+ if (!mOverlayViewEnabled || mOverlayView == null) {
+ return;
+ }
+ mWindowParams.x = frame.left;
+ mWindowParams.y = frame.top;
+ mWindowParams.width = frame.right - frame.left;
+ mWindowParams.height = frame.bottom - frame.top;
+ mWindowManager.updateViewLayout(mOverlayView, mWindowParams);
+ }
+
+ /**
+ * Removes the current overlay view.
+ */
+ void removeOverlayView(boolean clearWindowToken) {
+ if (DEBUG) {
+ Log.d(TAG, "remove overlay view(" + mOverlayView + ")");
+ }
+ if (clearWindowToken) {
+ mWindowToken = null;
+ mOverlayFrame = null;
+ }
+ if (mOverlayView != null) {
+ mWindowManager.removeView(mOverlayView);
+ mOverlayView = null;
+ mWindowParams = null;
+ }
+ }
}
private final class ServiceHandler extends Handler {
diff --git a/core/java/android/tv/TvView.java b/core/java/android/tv/TvView.java
new file mode 100644
index 0000000..325950d
--- /dev/null
+++ b/core/java/android/tv/TvView.java
@@ -0,0 +1,228 @@
+/*
+ * 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.tv;
+
+import android.content.ComponentName;
+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.SessionCreateCallback;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewTreeObserver;
+
+/**
+ * View playing TV
+ */
+public class TvView extends SurfaceView {
+ private static final String TAG = "TvView";
+
+ private final Handler mHandler = new Handler();
+ private TvInputManager.Session mSession;
+ private Surface mSurface;
+ private boolean mOverlayViewCreated;
+ private Rect mOverlayViewFrame;
+ private boolean mGlobalListenersAdded;
+ private TvInputManager mTvInputManager;
+ private SessionCreateCallback mSessionCreateCallback;
+
+ private 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
+ + ", height=" + height + ")");
+ if (holder.getSurface() == mSurface) {
+ return;
+ }
+ mSurface = holder.getSurface();
+ setSessionSurface(mSurface);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mSurface = holder.getSurface();
+ setSessionSurface(mSurface);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mSurface = null;
+ setSessionSurface(null);
+ }
+ };
+
+ public TvView(Context context) {
+ this(context, null, 0);
+ }
+
+ public TvView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TvView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ getHolder().addCallback(mSurfaceHolderCallback);
+ mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
+ }
+
+ /**
+ * Binds a TV input to this view. {@link SessionCreateCallback#onSessionCreated} will be
+ * called to send the result of this binding with {@link TvInputManager.Session}.
+ * If a TV input is already bound, the input will be unbound from this view and its session
+ * will be released.
+ *
+ * @param name TV input name will be bound to this view.
+ * @param callback called when TV input is bound. The callback sends
+ * {@link TvInputManager.Session}
+ * @throws IllegalArgumentException if any of the arguments is {@code null}.
+ */
+ public void bindTvInput(ComponentName name, SessionCreateCallback callback) {
+ if (name == null) {
+ throw new IllegalArgumentException("name cannot be null");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+ if (mSession != null) {
+ release();
+ }
+ // When bindTvInput is called multiple times before the callback is called,
+ // only the callback of the last bindTvInput call will be actually called back.
+ // The previous callbacks will be ignored. For the logic, mSessionCreateCallback
+ // is newly assigned for every bindTvInput call and compared with
+ // MySessionCreateCallback.this.
+ mSessionCreateCallback = new MySessionCreateCallback(callback);
+ mTvInputManager.createSession(name, mSessionCreateCallback, mHandler);
+ }
+
+ /**
+ * Unbinds a TV input currently bound. Its corresponding {@link TvInputManager.Session}
+ * is released.
+ */
+ public void unbindTvInput() {
+ if (mSession != null) {
+ release();
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ createSessionOverlayView();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ removeSessionOverlayView();
+ super.onDetachedFromWindow();
+ }
+
+ /** @hide */
+ @Override
+ protected void updateWindow(boolean force, boolean redrawNeeded) {
+ super.updateWindow(force, redrawNeeded);
+ relayoutSessionOverlayView();
+ }
+
+ private void release() {
+ setSessionSurface(null);
+ removeSessionOverlayView();
+ mSession.release();
+ mSession = null;
+ }
+
+ private void setSessionSurface(Surface surface) {
+ if (mSession == null) {
+ return;
+ }
+ mSession.setSurface(surface);
+ }
+
+ private void createSessionOverlayView() {
+ if (mSession == null || !isAttachedToWindow()
+ || mOverlayViewCreated) {
+ return;
+ }
+ mOverlayViewFrame = getViewFrameOnScreen();
+ mSession.createOverlayView(this, mOverlayViewFrame);
+ mOverlayViewCreated = true;
+ }
+
+ private void removeSessionOverlayView() {
+ if (mSession == null || !mOverlayViewCreated) {
+ return;
+ }
+ mSession.removeOverlayView();
+ mOverlayViewCreated = false;
+ mOverlayViewFrame = null;
+ }
+
+ private void relayoutSessionOverlayView() {
+ if (mSession == null || !isAttachedToWindow()
+ || !mOverlayViewCreated) {
+ return;
+ }
+ Rect viewFrame = getViewFrameOnScreen();
+ if (viewFrame.equals(mOverlayViewFrame)) {
+ return;
+ }
+ mSession.relayoutOverlayView(viewFrame);
+ mOverlayViewFrame = viewFrame;
+ }
+
+ private Rect getViewFrameOnScreen() {
+ int[] location = new int[2];
+ getLocationOnScreen(location);
+ return new Rect(location[0], location[1],
+ location[0] + getWidth(), location[1] + getHeight());
+ }
+
+ private class MySessionCreateCallback implements SessionCreateCallback {
+ final SessionCreateCallback mExternalCallback;
+
+ MySessionCreateCallback(SessionCreateCallback externalCallback) {
+ mExternalCallback = externalCallback;
+ }
+
+ @Override
+ public void onSessionCreated(Session session) {
+ if (this != mSessionCreateCallback) {
+ // This callback is obsolete.
+ session.release();
+ return;
+ }
+ mSession = session;
+ if (session != null) {
+ // mSurface may not be ready yet as soon as starting an application.
+ // In the case, we don't send Session.setSurface(null) unnecessarily.
+ // setSessionSurface will be called in surfaceCreated.
+ if (mSurface != null) {
+ setSessionSurface(mSurface);
+ }
+ createSessionOverlayView();
+ }
+ if (mExternalCallback != null) {
+ mExternalCallback.onSessionCreated(session);
+ }
+ }
+ }
+}
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index abd173a..2b81072 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -352,6 +352,7 @@
/** @hide */ public static final int LOG_ID_RADIO = 1;
/** @hide */ public static final int LOG_ID_EVENTS = 2;
/** @hide */ public static final int LOG_ID_SYSTEM = 3;
+ /** @hide */ public static final int LOG_ID_CRASH = 4;
/** @hide */ public static native int println_native(int bufID,
int priority, String tag, String msg);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 23123dd..4a2cc1a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -422,7 +422,8 @@
mWindowType = type;
}
- private void updateWindow(boolean force, boolean redrawNeeded) {
+ /** @hide */
+ protected void updateWindow(boolean force, boolean redrawNeeded) {
if (!mHaveFrame) {
return;
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f44cc87..be316e2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -720,6 +720,11 @@
private static boolean sIgnoreMeasureCache = false;
/**
+ * Ignore the clipBounds of this view for the children.
+ */
+ static boolean sIgnoreClipBoundsForChildren = false;
+
+ /**
* This view does not want keystrokes. Use with TAKES_FOCUS_MASK when
* calling setFlags.
*/
@@ -2963,7 +2968,7 @@
/**
* Current clip bounds. to which all drawing of this view are constrained.
*/
- private Rect mClipBounds = null;
+ Rect mClipBounds = null;
private boolean mLastIsOpaque;
@@ -3511,6 +3516,9 @@
// of whether a layout was requested on that View.
sIgnoreMeasureCache = targetSdkVersion < KITKAT;
+ // Older apps may need this to ignore the clip bounds
+ sIgnoreClipBoundsForChildren = targetSdkVersion < L;
+
sCompatibilityDone = true;
}
}
@@ -4729,13 +4737,13 @@
private void manageFocusHotspot(boolean focused, View v) {
if (mBackground != null && mBackground.supportsHotspots()) {
final Rect r = new Rect();
- if (v != null) {
+ if (!focused && v != null) {
v.getBoundsOnScreen(r);
final int[] location = new int[2];
getLocationOnScreen(location);
r.offset(-location[0], -location[1]);
} else {
- r.set(mLeft, mTop, mRight, mBottom);
+ r.set(0, 0, mRight - mLeft, mBottom - mTop);
}
final float x = r.exactCenterX();
@@ -4850,16 +4858,13 @@
if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
mPrivateFlags &= ~PFLAG_FOCUSED;
- if (hasFocus()) {
- manageFocusHotspot(false, focused);
- }
-
if (propagate && mParent != null) {
mParent.clearChildFocus(this);
}
onFocusChanged(false, 0, null);
+ manageFocusHotspot(false, focused);
refreshDrawableState();
if (propagate && (!refocus || !rootViewRequestFocus())) {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index a64bdc7..9d4ffb1 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2962,14 +2962,24 @@
}
}
- int saveCount = 0;
+ int clipSaveCount = 0;
final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
+ boolean hasClipBounds = mClipBounds != null && !sIgnoreClipBoundsForChildren;
+ boolean clippingNeeded = clipToPadding || hasClipBounds;
+
+ if (clippingNeeded) {
+ clipSaveCount = canvas.save();
+ }
+
if (clipToPadding) {
- saveCount = canvas.save();
canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
mScrollX + mRight - mLeft - mPaddingRight,
mScrollY + mBottom - mTop - mPaddingBottom);
+ }
+ if (hasClipBounds) {
+ canvas.clipRect(mClipBounds.left, mClipBounds.top, mClipBounds.right,
+ mClipBounds.bottom);
}
// We will draw our child's animation, let's reset the flag
@@ -3010,8 +3020,8 @@
onDebugDraw(canvas);
}
- if (clipToPadding) {
- canvas.restoreToCount(saveCount);
+ if (clippingNeeded) {
+ canvas.restoreToCount(clipSaveCount);
}
// mGroupFlags might have been updated by drawChild()
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index eec4354..097dff0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1173,7 +1173,15 @@
void dispatchApplyInsets(View host) {
mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets);
- host.dispatchApplyWindowInsets(new WindowInsets(mFitSystemWindowsInsets));
+ boolean isRound = false;
+ if ((mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0
+ && mDisplay.getDisplayId() == 0) {
+ // we're fullscreen and not hosted in an ActivityView
+ isRound = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_windowIsRound);
+ }
+ host.dispatchApplyWindowInsets(new WindowInsets(
+ mFitSystemWindowsInsets, isRound));
}
private void performTraversals() {
@@ -3714,7 +3722,8 @@
if (result == InputMethodManager.DISPATCH_HANDLED) {
return FINISH_HANDLED;
} else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
- return FINISH_NOT_HANDLED;
+ // The IME could not handle it, so skip along to the next InputStage
+ return FORWARD;
} else {
return DEFER; // callback will be invoked later
}
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index f8cc793..2160efe 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -51,6 +51,11 @@
}
/** @hide */
+ public WindowInsets(Rect systemWindowInsets, boolean isRound) {
+ this(systemWindowInsets, EMPTY_RECT, isRound);
+ }
+
+ /** @hide */
public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, boolean isRound) {
mSystemWindowInsets = systemWindowInsets;
mWindowDecorInsets = windowDecorInsets;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 62fbbc4..d2e7324 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1136,9 +1136,18 @@
}
/**
+ * @deprecated Use {@link #createPrintDocumentAdapter(String)} which requires user
+ * to provide a print document name.
+ */
+ @Deprecated
+ public PrintDocumentAdapter createPrintDocumentAdapter() {
+ checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "createPrintDocumentAdapter");
+ return mProvider.createPrintDocumentAdapter("default");
+ }
+
+ /**
* Creates a PrintDocumentAdapter that provides the content of this Webview for printing.
- * Only supported for API levels
- * {@link android.os.Build.VERSION_CODES#KITKAT} and above.
*
* The adapter works by converting the Webview contents to a PDF stream. The Webview cannot
* be drawn during the conversion process - any such draws are undefined. It is recommended
@@ -1146,11 +1155,14 @@
* temporarily hide a visible WebView by using a custom PrintDocumentAdapter instance
* wrapped around the object returned and observing the onStart and onFinish methods. See
* {@link android.print.PrintDocumentAdapter} for more information.
+ *
+ * @param documentName The user-facing name of the printed document. See
+ * {@link android.print.PrintDocumentInfo}
*/
- public PrintDocumentAdapter createPrintDocumentAdapter() {
+ public PrintDocumentAdapter createPrintDocumentAdapter(String documentName) {
checkThread();
if (DebugFlags.TRACE_API) Log.d(LOGTAG, "createPrintDocumentAdapter");
- return mProvider.createPrintDocumentAdapter();
+ return mProvider.createPrintDocumentAdapter(documentName);
}
/**
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 9488cdd..5081ff5 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -148,7 +148,7 @@
public Picture capturePicture();
- public PrintDocumentAdapter createPrintDocumentAdapter();
+ public PrintDocumentAdapter createPrintDocumentAdapter(String documentName);
public float getScale();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a7278da..b91111d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -652,6 +652,7 @@
boolean allCaps = false;
int shadowcolor = 0;
float dx = 0, dy = 0, r = 0;
+ boolean elegant = false;
final Resources.Theme theme = context.getTheme();
@@ -728,6 +729,10 @@
case com.android.internal.R.styleable.TextAppearance_shadowRadius:
r = appearance.getFloat(attr, 0);
break;
+
+ case com.android.internal.R.styleable.TextAppearance_elegantTextHeight:
+ elegant = appearance.getBoolean(attr, false);
+ break;
}
}
@@ -1065,6 +1070,10 @@
case com.android.internal.R.styleable.TextView_textAllCaps:
allCaps = a.getBoolean(attr, false);
break;
+
+ case com.android.internal.R.styleable.TextView_elegantTextHeight:
+ elegant = a.getBoolean(attr, false);
+ break;
}
}
a.recycle();
@@ -1245,6 +1254,7 @@
setHighlightColor(textColorHighlight);
}
setRawTextSize(textSize);
+ setElegantTextHeight(elegant);
if (allCaps) {
setTransformationMethod(new AllCapsTransformationMethod(getContext()));
@@ -2468,6 +2478,11 @@
setTransformationMethod(new AllCapsTransformationMethod(getContext()));
}
+ if (appearance.hasValue(com.android.internal.R.styleable.TextAppearance_elegantTextHeight)) {
+ setElegantTextHeight(appearance.getBoolean(
+ com.android.internal.R.styleable.TextAppearance_elegantTextHeight, false));
+ }
+
appearance.recycle();
}
@@ -2615,6 +2630,17 @@
}
/**
+ * Set the TextView's elegant height metrics flag. This setting selects font
+ * variants that have not been compacted to fit Latin-based vertical
+ * metrics, and also increases top and bottom bounds to provide more space.
+ *
+ * @param elegant set the paint's elegant metrics flag.
+ */
+ public void setElegantTextHeight(boolean elegant) {
+ mTextPaint.setElegantTextHeight(elegant);
+ }
+
+ /**
* Sets the text color for all the states (normal, selected,
* focused) to be this color.
*
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4c11fa9..eaedba5 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5580,13 +5580,13 @@
if (end) {
Slog.w(TAG, "New history ends before old history!");
} else if (!out.same(mHistoryReadTmp)) {
- long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
PrintWriter pw = new FastPrintWriter(new LogWriter(android.util.Log.WARN, TAG));
pw.println("Histories differ!");
pw.println("Old history:");
- (new HistoryPrinter()).printNextItem(pw, out, now, false, true);
+ (new HistoryPrinter()).printNextItem(pw, out, 0, false, true);
pw.println("New history:");
- (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now, false, true);
+ (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, 0, false,
+ true);
pw.flush();
}
}
@@ -5664,7 +5664,12 @@
return false;
}
+ final long lastRealtime = out.time;
+ final long lastWalltime = out.currentTime;
readHistoryDelta(mHistoryBuffer, out);
+ if (out.cmd != HistoryItem.CMD_CURRENT_TIME && lastWalltime != 0) {
+ out.currentTime = lastWalltime + (out.time - lastRealtime);
+ }
return true;
}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 5538dca..4a26b4b 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -55,6 +55,11 @@
private static final native void nativeFinishInit();
private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup);
+ private static int Clog_e(String tag, String msg, Throwable tr) {
+ return Log.println_native(Log.LOG_ID_CRASH, Log.ERROR, tag,
+ msg + '\n' + Log.getStackTraceString(tr));
+ }
+
/**
* Use this to log a message when a thread exits due to an uncaught
* exception. The framework catches these for the main threads, so
@@ -68,7 +73,7 @@
mCrashing = true;
if (mApplicationObject == null) {
- Slog.e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
+ Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
} else {
StringBuilder message = new StringBuilder();
message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");
@@ -77,7 +82,7 @@
message.append("Process: ").append(processName).append(", ");
}
message.append("PID: ").append(Process.myPid());
- Slog.e(TAG, message.toString(), e);
+ Clog_e(TAG, message.toString(), e);
}
// Bring up crash dialog, wait for it to be dismissed
@@ -85,9 +90,9 @@
mApplicationObject, new ApplicationErrorReport.CrashInfo(e));
} catch (Throwable t2) {
try {
- Slog.e(TAG, "Error reporting crash", t2);
+ Clog_e(TAG, "Error reporting crash", t2);
} catch (Throwable t3) {
- // Even Slog.e() fails! Oh well.
+ // Even Clog_e() fails! Oh well.
}
} finally {
// Try everything to make sure this process goes away.
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index f77a389..08a88d1 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -357,6 +357,24 @@
obj->setPaintOptionsAndroid(paintOpts);
}
+ static jboolean isElegantTextHeight(JNIEnv* env, jobject paint) {
+ NPE_CHECK_RETURN_ZERO(env, paint);
+ SkPaint* obj = GraphicsJNI::getNativePaint(env, paint);
+ SkPaintOptionsAndroid paintOpts = obj->getPaintOptionsAndroid();
+ return paintOpts.getFontVariant() == SkPaintOptionsAndroid::kElegant_Variant;
+ }
+
+ static void setElegantTextHeight(JNIEnv* env, jobject paint, jboolean aa) {
+ NPE_CHECK_RETURN_VOID(env, paint);
+ SkPaint* obj = GraphicsJNI::getNativePaint(env, paint);
+ SkPaintOptionsAndroid::FontVariant variant =
+ aa ? SkPaintOptionsAndroid::kElegant_Variant :
+ SkPaintOptionsAndroid::kDefault_Variant;
+ SkPaintOptionsAndroid paintOpts = obj->getPaintOptionsAndroid();
+ paintOpts.setFontVariant(variant);
+ obj->setPaintOptionsAndroid(paintOpts);
+ }
+
static jfloat getTextSize(JNIEnv* env, jobject paint) {
NPE_CHECK_RETURN_ZERO(env, paint);
return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getTextSize());
@@ -401,10 +419,30 @@
return SkScalarToFloat(metrics.fDescent);
}
+ static SkScalar getMetricsInternal(SkPaint *paint, SkPaint::FontMetrics *metrics) {
+ const int kElegantTop = 2500;
+ const int kElegantBottom = -1000;
+ const int kElegantAscent = 1946;
+ const int kElegantDescent = -512;
+ const int kElegantLeading = 0;
+ SkScalar spacing = paint->getFontMetrics(metrics);
+ SkPaintOptionsAndroid paintOpts = paint->getPaintOptionsAndroid();
+ if (paintOpts.getFontVariant() == SkPaintOptionsAndroid::kElegant_Variant) {
+ SkScalar size = paint->getTextSize();
+ metrics->fTop = -size * kElegantTop / 2048;
+ metrics->fBottom = -size * kElegantBottom / 2048;
+ metrics->fAscent = -size * kElegantAscent / 2048;
+ metrics->fDescent = -size * kElegantDescent / 2048;
+ metrics->fLeading = size * kElegantLeading / 2048;
+ spacing = metrics->fDescent - metrics->fAscent + metrics->fLeading;
+ }
+ return spacing;
+ }
+
static jfloat getFontMetrics(JNIEnv* env, jobject paint, jobject metricsObj) {
NPE_CHECK_RETURN_ZERO(env, paint);
SkPaint::FontMetrics metrics;
- SkScalar spacing = GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics);
+ SkScalar spacing = getMetricsInternal(GraphicsJNI::getNativePaint(env, paint), &metrics);
if (metricsObj) {
SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
@@ -421,7 +459,7 @@
NPE_CHECK_RETURN_ZERO(env, paint);
SkPaint::FontMetrics metrics;
- GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics);
+ getMetricsInternal(GraphicsJNI::getNativePaint(env, paint), &metrics);
int ascent = SkScalarRoundToInt(metrics.fAscent);
int descent = SkScalarRoundToInt(metrics.fDescent);
int leading = SkScalarRoundToInt(metrics.fLeading);
@@ -894,6 +932,8 @@
{"native_getTextAlign","(J)I", (void*) SkPaintGlue::getTextAlign},
{"native_setTextAlign","(JI)V", (void*) SkPaintGlue::setTextAlign},
{"native_setTextLocale","(JLjava/lang/String;)V", (void*) SkPaintGlue::setTextLocale},
+ {"isElegantTextHeight","()Z", (void*) SkPaintGlue::isElegantTextHeight},
+ {"setElegantTextHeight","(Z)V", (void*) SkPaintGlue::setElegantTextHeight},
{"getTextSize","()F", (void*) SkPaintGlue::getTextSize},
{"setTextSize","(F)V", (void*) SkPaintGlue::setTextSize},
{"getTextScaleX","()F", (void*) SkPaintGlue::getTextScaleX},
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 475e926..662af89 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -23,6 +23,7 @@
#include "JNIHelp.h"
#include <fcntl.h>
+#include <inttypes.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -334,7 +335,7 @@
if (b == NULL) {
b = new JavaBBinder(env, obj);
mBinder = b;
- ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%d\n",
+ ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%" PRId32 "\n",
b.get(), b->getWeakRefs(), obj, b->getWeakRefs()->getWeakCount());
}
@@ -697,9 +698,9 @@
"Not allowed to write file descriptors here");
break;
default:
- ALOGE("Unknown binder error code. 0x%x", err);
+ ALOGE("Unknown binder error code. 0x%" PRIx32, err);
String8 msg;
- msg.appendFormat("Unknown binder error code. 0x%x", err);
+ msg.appendFormat("Unknown binder error code. 0x%" PRIx32, err);
// RemoteException is a checked exception, only throw from certain methods.
jniThrowException(env, canThrowRemoteException
? "android/os/RemoteException" : "java/lang/RuntimeException", msg.string());
@@ -733,7 +734,7 @@
if (uid > 0 && uid < 999) {
// In Android currently there are no uids in this range.
char buf[128];
- sprintf(buf, "Restoring bad calling ident: 0x%Lx", token);
+ sprintf(buf, "Restoring bad calling ident: 0x%" PRIx64, token);
jniThrowException(env, "java/lang/IllegalStateException", buf);
return;
}
@@ -965,8 +966,8 @@
jint len = strlen(str);
int space_needed = 1 + sizeof(len) + len;
if (end - *pos < space_needed) {
- ALOGW("not enough space for string. remain=%d; needed=%d",
- (end - *pos), space_needed);
+ ALOGW("not enough space for string. remain=%" PRIdPTR "; needed=%d",
+ end - *pos, space_needed);
return false;
}
**pos = EVENT_TYPE_STRING;
@@ -981,8 +982,8 @@
static bool push_eventlog_int(char** pos, const char* end, jint val) {
int space_needed = 1 + sizeof(val);
if (end - *pos < space_needed) {
- ALOGW("not enough space for int. remain=%d; needed=%d",
- (end - *pos), space_needed);
+ ALOGW("not enough space for int. remain=%" PRIdPTR "; needed=%d",
+ end - *pos, space_needed);
return false;
}
**pos = EVENT_TYPE_INT;
@@ -1068,7 +1069,7 @@
return JNI_FALSE;
}
- ALOGV("Java code calling transact on %p in Java object %p with code %d\n",
+ ALOGV("Java code calling transact on %p in Java object %p with code %" PRId32 "\n",
target, obj, code);
#if ENABLE_BINDER_SAMPLE
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b8fe0ff..57e845f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2071,6 +2071,14 @@
android:description="@string/permdesc_bindTvInput"
android:protectionLevel="signature|system" />
+ <!-- Must be required by a {@link android.media.routeprovider.RouteProviderService}
+ to ensure that only the system can interact with it.
+ -->
+ <permission android:name="android.permission.BIND_ROUTE_PROVIDER"
+ android:label="@string/permlab_bindRouteProvider"
+ android:description="@string/permdesc_bindRouteProvider"
+ android:protectionLevel="signature" />
+
<!-- Must be required by device administration receiver, to ensure that only the
system can interact with it. -->
<permission android:name="android.permission.BIND_DEVICE_ADMIN"
diff --git a/core/res/res/color/primary_text_quantum_dark.xml b/core/res/res/color/primary_text_quantum_dark.xml
new file mode 100644
index 0000000..1fcd0e3
--- /dev/null
+++ b/core/res/res/color/primary_text_quantum_dark.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false" android:alpha="0.5" android:color="@color/primary_text_default_quantum_dark"/>
+ <item android:color="@color/primary_text_default_quantum_dark"/>
+</selector>
diff --git a/core/res/res/color/primary_text_quantum_light.xml b/core/res/res/color/primary_text_quantum_light.xml
new file mode 100644
index 0000000..1ec1634
--- /dev/null
+++ b/core/res/res/color/primary_text_quantum_light.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false" android:alpha="0.5" android:color="@color/primary_text_default_quantum_light"/>
+ <item android:color="@color/primary_text_default_quantum_light"/>
+</selector>
diff --git a/core/res/res/drawable/btn_borderless_quantum.xml b/core/res/res/drawable/btn_borderless_quantum.xml
index 2e3c515..69a891a 100644
--- a/core/res/res/drawable/btn_borderless_quantum.xml
+++ b/core/res/res/drawable/btn_borderless_quantum.xml
@@ -16,6 +16,7 @@
<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/core/res/res/drawable/btn_check_quantum_anim.xml b/core/res/res/drawable/btn_check_quantum_anim.xml
index d68d512..0600522 100644
--- a/core/res/res/drawable/btn_check_quantum_anim.xml
+++ b/core/res/res/drawable/btn_check_quantum_anim.xml
@@ -27,24 +27,27 @@
<group>
<path
- android:name="check"
- android:pathData="M 232.1,80.6 L 248.5,92.1 L 145.2,239.5 L 71.5,187.8 L 83,171.5 L 140.3,211.7 z"
- android:fill="?attr/colorControlActivated" />
- </group>
- <group>
- <path
android:name="box1"
- android:pathData="M 160,216.5 L 143.5,240 L 120,223.5 L 136.5,200 L 160,216.5 L 160,216.5 z"
- android:fill="?attr/colorControlActivated"
- android:stroke="?attr/colorControlActivated"
+ android:pathData="M 240,80 L 240,240 L 80,240 L 80,80 L 240,80 L 240,80 z"
+ android:stroke="?attr/colorControlNormal"
+ android:strokeWidth="20"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
</group>
<group>
<path
android:name="box2"
+ android:pathData="M 160,200 L 160,240 L 120,240 L 120,200 L 160,200 L 160,200 z"
+ android:stroke="?attr/colorControlNormal"
+ android:strokeWidth="10"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round" />
+ </group>
+ <group>
+ <path
+ android:name="box3"
android:pathData="M 160,216.5 L 143.5,240 L 120,223.5 L 136.5,200 L 160,216.5 L 160,216.5 z"
- android:rotation="-35"
+ android:rotation="35"
android:pivotX="140"
android:pivotY="220"
android:fill="?attr/colorControlNormal"
@@ -55,25 +58,22 @@
</group>
<group>
<path
- android:name="box3"
- android:pathData="M 160,200 L 160,240 L 120,240 L 120,200 L 160,200 L 160,200 z"
- android:stroke="?attr/colorControlNormal"
- android:strokeWidth="10"
+ android:name="box4"
+ android:pathData="M 160,216.5 L 143.5,240 L 120,223.5 L 136.5,200 L 160,216.5 L 160,216.5 z"
+ android:fill="?attr/colorControlActivated"
+ android:stroke="?attr/colorControlActivated"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
</group>
<group>
<path
- android:name="box4"
- android:pathData="M 240,80 L 240,240 L 80,240 L 80,80 L 240,80 L 240,80 z"
- android:stroke="?attr/colorControlNormal"
- android:strokeWidth="20"
- android:strokeLineCap="round"
- android:strokeLineJoin="round" />
+ android:name="check"
+ android:pathData="M 232.1,80.6 L 248.5,92.1 L 145.2,239.5 L 71.5,187.8 L 83,171.5 L 140.3,211.7 z"
+ android:fill="?attr/colorControlActivated" />
</group>
<animation
android:durations="300,100,0,300"
- android:sequence="check,box1,box2,box3,box4" />
+ android:sequence="box1,box2,box3,box4,check" />
</vector>
diff --git a/core/res/res/drawable/btn_color_quantum.xml b/core/res/res/drawable/btn_color_quantum.xml
index e923003..2da9226 100644
--- a/core/res/res/drawable/btn_color_quantum.xml
+++ b/core/res/res/drawable/btn_color_quantum.xml
@@ -19,7 +19,8 @@
<selector>
<item android:state_enabled="false">
<nine-patch android:src="@drawable/btn_qntm_alpha"
- android:tint="?attr/colorButtonNormal" />
+ android:tint="?attr/colorButtonNormal"
+ android:alpha="?attr/disabledAlpha" />
</item>
<item>
<nine-patch android:src="@drawable/btn_qntm_alpha"
diff --git a/core/res/res/drawable/btn_default_quantum.xml b/core/res/res/drawable/btn_default_quantum.xml
index 2919621..c6a3a33 100644
--- a/core/res/res/drawable/btn_default_quantum.xml
+++ b/core/res/res/drawable/btn_default_quantum.xml
@@ -17,7 +17,18 @@
<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="?attr/colorButtonPressed">
<item>
- <nine-patch android:src="@drawable/btn_qntm_alpha"
- android:tint="?attr/colorButtonNormal" />
+ <selector>
+ <item android:state_enabled="false">
+ <nine-patch
+ android:src="@drawable/btn_qntm_alpha"
+ android:tint="?attr/colorButtonNormal"
+ android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item>
+ <nine-patch
+ android:src="@drawable/btn_qntm_alpha"
+ android:tint="?attr/colorButtonNormal" />
+ </item>
+ </selector>
</item>
</touch-feedback>
diff --git a/core/res/res/drawable/edit_text_quantum.xml b/core/res/res/drawable/edit_text_quantum.xml
index d1f9831..c42c7b7 100644
--- a/core/res/res/drawable/edit_text_quantum.xml
+++ b/core/res/res/drawable/edit_text_quantum.xml
@@ -14,29 +14,26 @@
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_window_focused="false" android:state_enabled="true">
- <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:state_window_focused="false" android:state_enabled="false">
- <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:state_enabled="true" android:state_focused="true">
- <nine-patch android:src="@drawable/textfield_activated_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:state_enabled="true" android:state_activated="true">
- <nine-patch android:src="@drawable/textfield_activated_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:state_enabled="true">
- <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="?attr/colorControlActivated">
<item>
- <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
+ <selector>
+ <item android:state_window_focused="false">
+ <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item android:state_enabled="false">
+ <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item android:state_focused="false" android:state_activated="false">
+ <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item>
+ <nine-patch android:src="@drawable/textfield_activated_qntm_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ </selector>
</item>
-</selector>
+</touch-feedback>
diff --git a/core/res/res/layout/alert_dialog_quantum.xml b/core/res/res/layout/alert_dialog_quantum.xml
index 98b68797..537162a 100644
--- a/core/res/res/layout/alert_dialog_quantum.xml
+++ b/core/res/res/layout/alert_dialog_quantum.xml
@@ -91,32 +91,32 @@
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layoutDirection="locale"
- android:measureWithLargestChild="true">
+ android:layoutDirection="locale">
<Button android:id="@+id/button3"
+ style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="start"
android:layout_marginRight="8dip"
android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?android:attr/buttonBarButtonStyle" />
+ android:minHeight="@dimen/alert_dialog_button_bar_height" />
+ <View
+ android:layout_width="0dp"
+ android:layout_height="@dimen/alert_dialog_button_bar_height"
+ android:layout_weight="1"
+ android:visibility="invisible" />
<Button android:id="@+id/button2"
+ style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="end"
- android:layout_marginRight="8dip"
android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?android:attr/buttonBarButtonStyle" />
+ android:minHeight="@dimen/alert_dialog_button_bar_height" />
<Button android:id="@+id/button1"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_marginLeft="8dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="end"
android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?android:attr/buttonBarButtonStyle" />
+ android:minHeight="@dimen/alert_dialog_button_bar_height" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index bf8037a..2988f06 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sinkroniseer"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Te veel <xliff:g id="CONTENT_TYPE">%s</xliff:g> uitgevee."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Tablet se berging is vol. Vee \'n aantal lêers uit om spasie vry te maak."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Horlosieberging is vol! Vee \'n paar lêers uit om plek te maak."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Foon se berging is vol. Vee \'n aantal lêers uit om spasie vry te maak."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Netwerk kan dalk gemonitor word"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Deur \'n onbekende derde party"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Luitoestel aan"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Sit tans af…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Jou tablet gaan nou afskakel."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Jou horlosie gaan nou afskakel."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Jou foon gaan nou afsit."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Wil jy afskakel?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Herlaai na veilige modus"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Laat die program toe om \'n kennisgewing uit te saai dat \'n SMS-boodskap ontvang is. Kwaadwillige programme kan dit dalk gebruik om inkomende SMS-boodskappe na te maak."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"stuur WAP-PUSH-ontvange uitsending"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Laat die program toe om \'n kennisgewing uit te saai dat \'n WAP PUSH-boodskap ontvang is. Kwaadwillige programme kan dit dalk gebruik om MMS-boodskap-ontvangsbewyse na te maak of die inhoud van enige webbladsy stilweg te vervang met kwaadwillige variante."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"stuur uitsending vir netwerktelling"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Laat die program toe om \'n kennisgewing uit te saai dat netwerke tellings moet kry. Nooit vir normale programme nodig nie."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"beperk hoeveelheid lopende prosesse"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Laat die program toe om die maksimum getal prosesse te beheer wat sal loop. Nooit nodig vir normale programme nie."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"dwing agtergrondprogramme om te sluit"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Dit laat die houer toe om aan die topvlak-koppelvlak van \'n legstuk-diens te bind. Dit moet nooit vir normale programme nodig wees nie."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"bind aan \'n roeteverskafferdiens"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Laat die houer toe om aan enige geregistreerde roeteverskaffers te bind. Behoort nooit vir normale programme nodig te wees nie."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"skakel met \'n toestel-admin"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Laat die houer toe om bedoelings na \'n toesteladministrateur te stuur. Dit moet nooit vir normale programme nodig wees nie."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"bind aan \'n TV-invoer"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Verander WiMAX-status"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Laat die program toe om die tablet aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Laat die program toe om die foon aan WiMAX-netwerke te koppel en daarvan te ontkoppel."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"gee telling vir netwerke"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Laat die program toe om netwerke te gradeer en beïnvloed watter netwerke die tablet moet verkies."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Laat die program toe om netwerke te gradeer en beïnvloed watter netwerke die foon moet verkies."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"bind saam met Bluetooth-toestelle"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Laat die program toe om die opstelling van Bluetooth op die tablet te sien, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Laat die program toe om die opstelling van die Bluetooth op die foon te sien, en om verbindings met saamgebinde toestelle te maak en te aanvaar."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 585857b..a32c834 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"ሥምሪያ"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"በጣም ብዙ <xliff:g id="CONTENT_TYPE">%s</xliff:g> ስርዞች።"</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"የጡባዊ ተኮ ማከማቻ ሙሉ ነው! ቦታ ነፃ ለማድረግ አንዳንድ ፋይሎች ሰርዝ።"</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"የእጅ ሰዓት ማከማቻ ሙሉ ነው። ቦታ ለማስለቀቅ አንዳንድ ፋይሎችን ይሰርዙ።"</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"የስልክ ማከማቻ ሙሉ ነው! ቦታ ነፃ ለማድረግ አንዳንድ ፋይሎች ሰርዝ።"</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"አውታረ መረብ በክትትል ውስጥ ሊሆን ይችላል"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"ባልታወቀ ሶስተኛ ወገን"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"መጥሪያ በርቷል"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"በመዝጋት ላይ..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"ጡባዊዎ ይዘጋል።"</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"የእርስዎ የእጅ ሰዓት ይዘጋል።"</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"ስልክዎ ይዘጋል።"</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"ዘግተህ መውጣት ትፈልጋለህ?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"በአስተማማኝ ኹነታ ውስጥ ዳግም አስጀምር"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"ኤስ ኤም ኤስ መልዕክት መቀበሉን ማሳወቂያ እንዲያሰራጭ ለመተግበሪያው ይፈቅዳሉ፡፡ መጪ ኤስ ኤም ኤስ መልዕክቶችን አመሳስሎ በማቅረብ ተንኮል አዘል መተግበሪያዎች ይሄንን ሊጠቀሙበት ይችላሉ፡፡"</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"የWAP-PUSH ደርስዋል ስርጭት ላክ"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"የWAP PUSH መልዕክት እንደተቀበለ ማሳወቂያ ለማሰራጨት ለመተግበሪያው ይፈቅዳሉ፡፡ ኤም ኤም ኤስ መልዕክት መቀበልን ለማስመሰል ወይም በተንኮል አዘል መሰሎች ለማንኛውም የድር ገፅ ይዘት በዝምታ ለመተካት ተንኮል አዘል መተግበሪያዎች ሊጠቀሙበት ይችላሉ፡፡"</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"ለአውታረ መረቦች የነጥብ ስጥ ስርጭት ይልካል"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"መተግበሪያው አውታረ መረቦች ነጥብ እንዲያዝለት የሚፈልጉት አንድ ማሳወቂያ እንዲያሰራጭ ያስችለዋል። ለመደበኛ መተግበሪያዎች በጭራሽ አያስፈልግም።"</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"የአሂድ ሂደቶችን ቁጥር ወስን"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"የሚሄዱ ሂደቶችን የመጨረሻ ቁጥር ለመቆጣጠር ለመተግበሪያው ይፈቅዳሉ፡፡ ለመደበኛ መተግበሪያዎች መቼም አያስፈልግም፡፡"</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"የጀርባ መተግበሪያዎች እንዲዘጉ አስገድዳቸው"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"ያዢው ከአንድ የርቀት ማሳያ ከፍተኛ-ደረጃ በይነገጽ ጋር እንዲጠርዝ ይፈቅድለታል። ለመደበኛ መተግበሪያዎች በጭራሽ አያስፈልግም።"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ወደ ፍርግም አገልግሎት አያይዝ"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ያዡ ግቤት ስልቱን ወደ ከፍተኛ-ደረጃ ፍርግም አገልግሎት ለመጠረዝ ይፈቅዳሉ። ለመደበኛ ትግበራዎች በፍፁም አያስፈልግም።"</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"ከመንገድ አቅራቢዎች አገልግሎት ጋር ያስተሳስሩ"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"አቃፊው ከማናቸውም የተመዘገቡ የመንገድ አቅራቢዎች ጋር እንዲተሳሰር ይፈቅድለታል። ለመደበኛ መተግበሪያዎች በጭራሽ ሊያስፈልግ አይገባም።"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"ከመሣሪያ አስተዳደር ጋር ተገናኝ"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"ያዡ በይነመረብን ለመሣሪያ አስተዳዳሪ ለመላክ ይፈቅዳሉ። ለመደበኛ መተግበሪያዎች በፍፁም አያስፈልግም።"</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"ከአንድ የቴሌቪዥን ግብዓት ጋር እሰር"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"የWiMAX ሁኔታ ለውጥ"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"መተግበሪያው ጡባዊ ተኮውን ከWiMAX አውታረ መረብ ጋር እንዲያገናኝና እንዲያለያይ ይፈቅድለታል።"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"መተግበሪያው ስልኩን ከWiMAX አውታረ መረብ ጋር እንዲያገናኝና እንዲያለያይ ይፈቅድለታል።"</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"ለአውታረ መረቦች ነጥብ ይሰጣል"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"መተግበሪያው ለአውታረ መረቦች ደረጃ እንዲሰጥ እና ጡባዊው የትኛዎቹን አውታረ መረቦች እንደሚመርጥ ላይ ተጽዕኖ እንዲያሳርፍ ያስችለዋል።"</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"መተግበሪያው ለአውታረ መረቦች ደረጃ እንዲሰጥ እና ስልኩ የትኛዎቹን አውታረ መረቦች እንደሚመርጥ ላይ ተጽዕኖ እንዲያሳርፍ ያስችለዋል።"</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"ከብሉቱዝ መሣሪያዎች ጋር ተጣመር"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"መተግበሪያው በጡባዊ ተኮው ላይ ያለውን የብሉቱዝ ውቅር እንዲያይ እና ከተጣመሩ መሳሪያዎች ጋር ግንኙነቶችን እንዲያደርግና እንዲቀበል ይፈቅድለታል።"</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"መተግበሪያው በስልኩ ላይ ያለውን የብሉቱዝ ውቅር እንዲያይ እና ከተጣመሩ መሳሪያዎች ጋር ግንኙነቶችን እንዲያደርግና እንዲቀበል ይፈቅድለታል።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 468c14b..86d2be3 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -337,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"للسماح للتطبيق ببث إشعار باستلام رسالة قصيرة SMS. قد تستخدم التطبيقات الضارة هذا لتزييف الرسائل القصيرة SMS الواردة."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"إرسال بث WAP-PUSH المستلم"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"للسماح للتطبيق ببث إشعار باستلام رسالة WAP PUSH. يمكن أن تستخدم التطبيقات الضارة هذا لتزيف استلام رسالة وسائط متعددة أو لاستبدال محتوى أي صفحة ويب بمتغيرات ضارة بشكل غير ملحوظ."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"إرسال بث الشبكات التي تم تقييمها"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"السماح للتطبيق لكي يبث إشعارًا يفيد بأنه يلزم تقييم الشبكات. لا يلزم ذلك مطلقًا مع التطبيقات العادية."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"تحديد عدد العمليات قيد التشغيل"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"للسماح للتطبيق بالتحكم في الحد الأقصى لعدد العمليات التي سيتم تشغيلها. غير مطلوب على الإطلاق للتطبيقات العادية."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"فرض إغلاق تطبيقات الخلفية"</string>
@@ -392,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"للسماح للمالك بالالتزام بواجهة المستوى العلوي للعرض عن بُعد. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"الالتزام بخدمة أداة"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"للسماح للمالك بالالتزام بواجهة المستوى العلوي لخدمة الأداة. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"الربط مع خدمة مزود طريق"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"للسماح لحامل البطاقة الربط مع أي مزود طريق مسجل. لا يجب استخدامه على الإطلاق مع التطبيقات العادية."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"التفاعل مع مشرف الجهاز"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"للسماح للمالك بإرسال الأهداف إلى أحد مشرفي الجهاز. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"الالتزام بإدخال التلفزيون"</string>
@@ -642,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"تغيير حالة WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"للسماح للتطبيق بتوصيل الجهاز اللوحي بشبكات WiMAX وقطع اتصاله بها."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"للسماح للتطبيق بتوصيل الهاتف بشبكات WiMAX وقطع اتصاله بها."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"تقييم الشبكات"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"للسماح للتطبيق بترتيب الشبكات وتحديد تلك الشبكات التي من المفضل أن يستخدمها الجهاز اللوحي."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"للسماح للتطبيق بترتيب الشبكات وتحديد تلك الشبكات التي من المفضل أن يستخدمها الهاتف."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"الاتصال بأجهزة بلوتوث"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"للسماح للتطبيق بعرض تهيئة البلوتوث على الجهاز اللوحي وإجراء اتصالات وقبولها مع الأجهزة المقترنة."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"للسماح للتطبيق بعرض تهيئة البلوتوث على الهاتف وإجراء اتصالات وقبولها مع الأجهزة المقترنة."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index bb2bf43..fcf3e1b 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Синхронизиране"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Изтриванията за <xliff:g id="CONTENT_TYPE">%s</xliff:g> са твърде много."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Хранилището на таблета е пълно. Изтрийте файлове, за да освободите място."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Хранилището на часовника е пълно. Изтрийте файлове, за да освободите място."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Хранилището на телефона е пълно. Изтрийте файлове, за да освободите място."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Мрежата може да се наблюдава"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"От неизвестна трета страна"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Звъненето е включено"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Изключва се..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Таблетът ви ще се изключи."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Часовникът ви ще се изключи."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Телефонът ви ще се изключи."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Искате ли да изключите?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Рестартиране в безопасен режим"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Разрешава на приложението да излъчва известие, че е получен SMS. Злонамерените приложения могат да използват това, за да фалшифицират входящите SMS съобщения."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"изпращане на излъчване при получено WAP PUSH"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Разрешава на приложението да излъчва известие, че е получено WAP PUSH съобщение. Злонамерените приложения могат да използват това, за да фалшифицират получаването на MMS или скрито да заменят съдържанието на произволна уеб страница със злонамерен вариант."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"изпращане на излъчване за оценяване на мрежите"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Разрешава на приложението да излъчи известие, че мрежите трябва да бъдат оценени. Не е необходимо за нормалните приложения."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"ограничаване на броя изпълнявани процеси"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Разрешава на приложението да контролира максималния брой изпълнявани процеси. Нормалните приложения никога не се нуждаят от това."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"принудително затваряне на приложенията на заден план"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Разрешава на притежателя да се свърже с интерфейса от първо ниво на отдалечен екран. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"обвързване с услуга за приспособления"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Разрешава на притежателя да се обвърже с интерфейса от най-високото ниво на услуга за приспособления. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"свързване с услуга за предоставяне на маршрути"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Разрешава на собственика да се свързва с всички регистрирани доставчици на маршрути. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"взаимодействие с администратор на устройството"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Разрешава на притежателя да изпраща намерения до администратор на устройството. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"свързване към вход на телевизор"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Промяна на състоянието на WiMAX мрежата"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Разрешава на приложението да свързва таблета към WiMAX мрежа и да прекратява връзката му с нея."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Разрешава на приложението да свързва телефона към WiMAX мрежа и да прекратява връзката му с нея."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"оценяване на мрежите"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Разрешава на приложението да класира мрежите и да повлияе върху това, кои да са предпочитаните за таблета."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Разрешава на приложението да класира мрежите и да повлияе върху това, кои да са предпочитаните за телефона."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"сдвояване с устройства с Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Разрешава на приложението да вижда конфигурацията на Bluetooth на таблета и да изгражда и приема връзки със сдвоени устройства."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Разрешава на приложението да вижда конфигурацията на Bluetooth на телефона и да изгражда и приема връзки със сдвоени устройства."</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 11601bb..b422a6d 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sincronització"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Massa supressions de <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"L\'emmagatzematge de la tauleta és ple. Suprimeix uns quants fitxers per alliberar espai."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"L\'emmagatzematge del rellotge està ple. Suprimeix uns quants fitxers per alliberar espai."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"L\'emmagatzematge del telèfon és ple. Suprimeix uns quants fitxers per alliberar espai."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"És possible que la xarxa estigui supervisada"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Per un tercer desconegut"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Timbre activat"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"S\'està apagant..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"La tauleta s\'apagarà."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"El rellotge s\'apagarà."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"El telèfon s\'apagarà."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Vols apagar-lo?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Reinicia en mode segur"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Permet que l\'aplicació difongui una notificació en què s\'indiqui que s\'ha rebut un missatge SMS. Les aplicacions malicioses poden fer servir aquesta funció per falsificar els missatges SMS entrants."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"enviar una difusió de tipus WAP-PUSH-received"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Permet que l\'aplicació difongui una notificació que indica que s\'ha rebut un missatge d\'inserció WAP. Les aplicacions malicioses poden utilitzar-ho per falsificar la recepció dels missatges MMS o per substituir silenciosament el contingut d\'una pàgina web per variants malicioses."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"enviar l\'emissió de la puntuació de les xarxes"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Permet que l\'aplicació emeti una notificació necessària perquè les xarxes rebin una puntuació. No es necessita mai per a les aplicacions normals."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"limitar el nombre de processos en execució"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Permet que l\'aplicació controli el nombre màxim de processos que s\'executaran. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"com fer que es tanquin les aplicacions en segon pla"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Permet que el titular vinculi a la interfície de nivell superior d\'un servei de widget. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"establir vincles amb un servei d\'aprovisionament de rutes"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Permet que el titular estableixi vincles amb els proveïdors de rutes registrats. No hauria de ser mai necessari per a les aplicacions normals."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interactuar amb un administrador del dispositiu"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Permet que el titular enviï intents a un administrador del sistema. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"Vinculació a una entrada de televisor"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Canvia l\'estat de WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permet que l\'aplicació connecti i desconnecti la tauleta de les xarxes WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permet que l\'aplicació connecti i desconnecti el telèfon de les xarxes WiMAX."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"puntuar les xarxes"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Permet que l\'aplicació classifiqui les xarxes i indiqui quines han de ser les xarxes preferides de la tauleta."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Permet que l\'aplicació classifiqui les xarxes i indiqui quines han de ser les xarxes preferides del telèfon."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"emparella amb dispositius Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Permet que l\'aplicació visualitzi la configuració de Bluetooth de la tauleta i que estableixi i accepti connexions amb dispositius sincronitzats."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Permet que una aplicació visualitzi la configuració de Bluetooth del telèfon i que estableixi i accepti connexions amb els dispositius sincronitzats."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index ef6c7e9..416eca9 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -337,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Umožňuje aplikaci vysílat oznámení o přijetí zprávy SMS. Škodlivé aplikace mohou toto oprávnění použít k vytváření falešných příchozích zpráv SMS."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"odeslání vysílání typu WAP-PUSH-received"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Umožňuje aplikaci vysílat oznámení o přijetí zprávy WAP PUSH. Škodlivé aplikace mohou toto oprávnění použít k vytváření falešných přijatých zpráv MMS nebo utajenému nahrazení obsahu libovolné webové stránky jejich škodlivými variantami."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"odeslání skóre vysílaných sítěmi"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Umožňuje aplikaci vysílat oznámení, že je třeba zadat skóre sítí. Běžné aplikace toto oprávnění nepotřebují."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"omezení počtu spuštěných procesů"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Umožňuje aplikaci řídit maximální počet spuštěných procesů. Běžné aplikace toto oprávnění nikdy nepotřebují."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"vynucení zavření aplikací na pozadí"</string>
@@ -392,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Umožňuje držiteli navázat se na nejvyšší úroveň služby widgetu. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"navázání na službu poskytovatele tras"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Umožňuje držiteli navázat se na libovolného poskytovatele registrovaných tras. Běžné aplikace by toto oprávnění neměly nikdy potřebovat."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"komunikovat se správcem zařízení"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Umožňuje držiteli oprávnění odesílat informace správci zařízení. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"navázání na televizní vstup"</string>
@@ -642,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Změnit stav připojení WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Umožňuje aplikaci připojovat tablet k sítím WiMAX a odpojovat jej od nich."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Umožňuje aplikaci připojovat telefon k sítím WiMAX a odpojovat jej od nich."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"zadání skóre sítí"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Umožňuje aplikaci hodnotit sítě a ovlivňovat, které sítě by měl tablet preferovat."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Umožňuje aplikaci hodnotit sítě a ovlivňovat, které sítě by měl telefon preferovat."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"párování se zařízeními Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Umožňuje aplikaci zobrazit konfiguraci tabletu s rozhraním Bluetooth, vytvářet připojení ke spárovaným zařízením a přijímat tato připojení."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Umožňuje aplikaci zobrazit konfiguraci telefonu s rozhraním Bluetooth, vytvářet připojení ke spárovaným zařízením a přijímat tato připojení."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index be6e28e..7e08df5 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synkroniser"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"For mange <xliff:g id="CONTENT_TYPE">%s</xliff:g> sletninger"</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Din tablets lager er fuldt. Slet nogle filer for at frigøre plads."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Urets lager er fuldt. Slet nogle filer for at frigøre plads."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Telefonens lager er fuldt. Slet nogle filer for at frigøre plads."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Netværket kan være overvåget"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Af en ukendt tredjepart"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Ringeren er aktiveret"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Lukker ned..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Din tablet slukkes nu."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Dit ur lukkes ned."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Din telefon slukkes nu."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Vil du slukke?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Genstart i sikker tilstand"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Tillader, at appen kan udsende en underretning om, at der er modtaget en sms-besked. Ondsindede apps kan bruge dette til at simulere indgående sms-beskeder."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"send WAP-PUSH-modtaget udsendelse"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Tillader, at appen kan udsende en underretning om, at der er modtaget en WAP PUSH-besked. Ondsindede apps kan bruge dette til at simulere modtagelse af mms-beskeder eller i det skjulte erstatte indholdet på en webside med ondsindede varianter."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"sende underretninger om bedømmelse af netværk"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Tillader, at appen kan udsende en underretning om, at netværket skal bedømmes. Dette er aldrig nødvendigt for almindelige apps."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"begræns antallet af kørende processer"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Tillader, at appen kan kontrollere det maksimale antal kørende processer. Dette er aldrig nødvendigt til normale apps."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"tvinge baggrundsapps til at lukke"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Tillader, at brugeren kan forpligte sig til en grænseflade for en widgettjeneste på øverste niveau. Bør aldrig være nødvendigt til almindelige apps."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"oprette tilknytning til en ruteudbydertjeneste"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Tillader, at indehaveren opretter tilknytninger til registrerede ruteudbydere. Dette bør aldrig være nødvendigt for normale apps."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"kommunikere med en enhedsadministrator"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Tillader, at brugeren kan sende hensigter til en enhedsadministrator. Dette bør aldrig være nødvendigt for almindelige apps."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"knyt til en tv-indgang"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Skift WiMAX-tilstand"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Tillader, at appen kan oprette forbindelse fra tabletten og afbryde forbindelsen til tabletten på WiMAX-netværk."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Tillader, at appen kan oprette forbindelse fra telefonen og afbryde forbindelsen til telefonen på WiMAX-netværk."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"bedømme netværk"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Tillader, at appen rangerer netværk og påvirker, hvilke netværk tabletten bør foretrække."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Tillader, at appen rangerer netværk og påvirker, hvilke netværk telefonen bør foretrække."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"parre med Bluetooth-enheder"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Tillader, at appen kan læse konfigurationen af Bluetooth på tabletten samt kan oprette og acceptere forbindelser med parrede enheder."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Tillader, at appen kan læse konfigurationen af Bluetooth på telefonen samt kan oprette og acceptere forbindelser med parrede enheder."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 3ba4c6f..162d0d8 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synchronisierung"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Zu viele <xliff:g id="CONTENT_TYPE">%s</xliff:g> gelöscht."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Der Tablet-Speicher ist voll. Löschen Sie Dateien, um Speicherplatz freizugeben."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Der Speicher Ihrer Uhr ist voll. Löschen Sie Dateien, um Speicherplatz freizugeben."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Der Handyspeicher ist voll! Löschen Sie Dateien, um Speicherplatz freizugeben."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Das Netzwerk wird möglicherweise überwacht."</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Von einem unbekannten Dritten"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Klingelton ein"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Wird heruntergefahren..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Ihr Tablet wird heruntergefahren."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Ihre Uhr wird heruntergefahren."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Telefon wird heruntergefahren."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Möchten Sie das Gerät herunterfahren?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Im abgesicherten Modus starten"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Ermöglicht der App, eine Benachrichtigung zu senden, dass eine SMS empfangen wurde. Schädliche Apps können so eingehende SMS fälschen."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"Von WAP-PUSH empfangenen Broadcast senden"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Ermöglicht der App, eine Benachrichtigung zu senden, dass eine WAP PUSH-Nachricht empfangen wurde. Schädliche Apps können so den Empfang von MMS vortäuschen oder unbemerkt den Inhalt einer beliebigen Webseite durch schädliche Inhalte ersetzen."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"Netzwerkbewertungen senden"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Ermöglicht der App, Benachrichtigungen zu senden, dass Netzwerke bewertet werden müssen. Für normale Apps sollte dies nie erforderlich sein."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"Anzahl der laufenden Prozesse beschränken"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Ermöglicht der App, die maximale Anzahl an aktiven Prozessen zu steuern. Wird nie für normale Apps benötigt."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"Apps im Hintergrund schließen"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Ermöglicht dem Halter, sich an die Oberfläche eines Widget-Dienstes auf oberster Ebene zu binden. Sollte nie für normale Apps benötigt werden."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"An Routenanbieterdienst binden"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Ermöglicht dem Inhaber die Bindung an registrierte Routenanbieter. Sollte für normale Apps nicht erforderlich sein"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"Interaktion mit einem Geräteadministrator"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Ermöglicht dem Halter, Intents an einen Geräteadministrator zu senden. Sollte nie für normale Apps benötigt werden."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"An eine TV-Eingabe binden"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX-Status ändern"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Ermöglicht der App, eine Verbindung zwischen dem Tablet und WiMAX-Netzwerken herzustellen und solche zu trennen."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Ermöglicht der App, eine Verbindung zwischen dem Telefon und WiMAX-Netzwerken herzustellen und solche zu trennen."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"Netzwerke bewerten"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Ermöglicht der App, Netzwerke zu bewerten und die Auswahl des jeweiligen Netzwerks für das Tablet zu beeinflussen"</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Ermöglicht der App, Netzwerke zu bewerten und die Auswahl des jeweiligen Netzwerks für das Telefon zu beeinflussen"</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"Pairing mit Bluetooth-Geräten durchführen"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Ermöglicht der App, die Bluetooth-Konfiguration eines Tablets einzusehen und Verbindungen zu gekoppelten Geräten herzustellen und zu akzeptieren."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Ermöglicht der App, die Bluetooth-Konfiguration des Telefons einzusehen und Verbindungen mit gekoppelten Geräten herzustellen und zu akzeptieren."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index b951897..6f013b2 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Συγχρονισμός"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Πάρα πολλές <xliff:g id="CONTENT_TYPE">%s</xliff:g> διαγραφές."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Ο αποθηκευτικός χώρος του tablet είναι πλήρης. Διαγράψτε μερικά αρχεία για να δημιουργήσετε ελεύθερο χώρο."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Ο αποθηκευτικός χώρος παρακολούθησης είναι πλήρης! Διαγράψτε μερικά αρχεία για να απελευθερώσετε χώρο."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Ο αποθηκευτικός χώρος του τηλεφώνου είναι πλήρης. Διαγράψτε μερικά αρχεία για να δημιουργήσετε ελεύθερο χώρο."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Το δίκτυο ενδέχεται να παρακολουθείται"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Από ένα άγνωστο τρίτο μέρος"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Ειδοποίηση ήχου ενεργή"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Απενεργοποίηση..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Το tablet σας θα απενεργοποιηθεί."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Η παρακολούθησή σας θα τερματιστεί."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Το τηλέφωνό σας θα απενεργοποιηθεί."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Θέλετε να γίνει τερματισμός λειτουργίας;"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Επανεκκίνηση στην ασφαλή λειτουργία"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Επιτρέπει στην εφαρμογή την εκπομπή ειδοποίησης σχετικά με τη λήψη μηνύματος SMS. Τυχόν κακόβουλες εφαρμογές ενδέχεται να χρησιμοποιήσουν αυτήν τη δυνατότητα για τη δημιουργία πλαστών εισερχόμενων μηνυμάτων SMS."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"αποστολή εκπομπής που έχει ληφθεί με WAP-PUSH"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Επιτρέπει στην εφαρμογή τη μετάδοση μιας ειδοποίησης ότι έχει ληφθεί κάποιο μήνυμα WAP PUSH. Τυχόν κακόβουλες εφαρμογές ενδέχεται να χρησιμοποιήσουν αυτήν τη δυνατότητα για τη λήψη πλαστών μηνυμάτων MMS ή την εν αγνοία του χρήστη αντικατάσταση του περιεχομένου οποιασδήποτε ιστοσελίδας με κακόβουλες παραλλαγές."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"αποστολή μετάδοσης κατάταξης δικτύων"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Επιτρέπει στην εφαρμογή να μεταδίδει μια ειδοποίηση ότι απαιτείται κατάταξη των δικτύων. Δεν απαιτείται ποτέ για τις συνήθεις εφαρμογές."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"περιορισμός αριθμού εκτελούμενων διαδικασιών"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Επιτρέπει στην εφαρμογή τον έλεγχο του μέγιστου αριθμού διαδικασιών που θα εκτελούνται. Δεν είναι απαραίτητο για συνήθεις εφαρμογές."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"αναγκαστικός τερματισμός εφαρμογών στο παρασκήνιο"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας απομακρυσμένης οθόνης. Δεν απαιτείται ποτέ για κανονικές εφαρμογές."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"δέσμευση σε υπηρεσία γραφικών στοιχείων"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας υπηρεσίας γραφικών στοιχείων. Δεν απαιτείται για κανονικές εφαρμογές."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"σύνδεση σε μια υπηρεσία παρόχου δρομολογητή"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Δίνει στον κάτοχο τη δυνατότητα σύνδεσης με οποιονδήποτε εγγεγραμμένο πάροχο δρομολογητή. Δεν απαιτείται ποτέ για κανονικές εφαρμογές."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"επικοινωνία με έναν διαχειριστή συσκευής"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Επιτρέπει στον κάτοχο την αποστολή στόχων σε έναν διαχειριστή συσκευής. Δεν είναι απαραίτητο για συνήθεις εφαρμογές."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"σύνδεση σε μία είσοδο τηλεόρασης"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Αλλαγή κατάστασης WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Επιτρέπει στην εφαρμογή τη σύνδεση στο tablet και την αποσύνδεση από αυτό, από δίκτυα WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Επιτρέπει στην εφαρμογή τη σύνδεση στο τηλέφωνο και την αποσύνδεση από αυτό, από δίκτυα WiMAX."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"κατάταξη δικτύων"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Επιτρέπει στην εφαρμογή να κατατάσσει τα δίκτυα και να επιλέγει τα προτιμώμενα δίκτυα του tablet."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Επιτρέπει στην εφαρμογή να κατατάσσει τα δίκτυα και να επιλέγει τα προτιμώμενα δίκτυα του τηλεφώνου."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"σύζευξη με συσκευές Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Επιτρέπει στην εφαρμογή να προβάλλει τη διαμόρφωση του Bluetooth στο tablet, καθώς και να πραγματοποιεί και να αποδέχεται συνδέσεις με συζευγμένες συσκευές."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Επιτρέπει στην εφαρμογή να προβάλλει τη διαμόρφωση του Bluetooth στο τηλέφωνο, καθώς και να πραγματοποιεί και να αποδέχεται συνδέσεις με συζευγμένες συσκευές."</string>
@@ -1382,7 +1377,7 @@
<string name="vpn_lockdown_error" msgid="6009249814034708175">"Σφάλμα πάντα ενεργοποιημένου VPN"</string>
<string name="vpn_lockdown_config" msgid="6415899150671537970">"Αγγίξτε για διαμόρφωση"</string>
<string name="upload_file" msgid="2897957172366730416">"Επιλογή αρχείου"</string>
- <string name="no_file_chosen" msgid="6363648562170759465">"Δεν έχει επιλεγεί αρχείο"</string>
+ <string name="no_file_chosen" msgid="6363648562170759465">"Δεν επιλέχθηκε κανένα αρχείο."</string>
<string name="reset" msgid="2448168080964209908">"Επαναφορά"</string>
<string name="submit" msgid="1602335572089911941">"Υποβολή"</string>
<string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Η λειτουργία αυτοκινήτου είναι ενεργοποιημένη"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index c13bb1e..9e94287 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sync"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Too many <xliff:g id="CONTENT_TYPE">%s</xliff:g> deletions."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Tablet storage is full. Delete some files to free space."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Watch storage is full. Delete some files to free up space."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Phone storage is full. Delete some files to free space."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Network may be monitored"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"By an unknown third party"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Ringer on"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Shutting down…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Your tablet will shut down."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Your watch will shut down."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Your phone will shut down."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Do you want to shut down?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Reboot to safe mode"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Allows the app to broadcast a notification that an SMS message has been received. Malicious apps may use this to forge incoming SMS messages."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"send WAP-PUSH-received broadcast"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Allows the app to broadcast a notification that a WAP PUSH message has been received. Malicious apps may use this to forge MMS message receipt or to silently replace the content of any web page with malicious variants."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"send score networks broadcast"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Allows the app to broadcast a notification that networks need to be scored. Never needed for normal apps."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"limit number of running processes"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Allows the app to control the maximum number of processes that will run. Never needed for normal apps."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"force background apps to close"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Allows the holder to bind to the top-level interface of a widget service. Should never be needed for normal apps."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"bind to a route provider service"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Allows the holder to bind to any registered route providers. Should never be needed for normal apps."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interact with device admin"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Allows the holder to send intents to a device administrator. Should never be needed for normal apps."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"bind to a TV input"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"change WiMAX state"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Allows the app to connect the tablet to and disconnect the tablet from WiMAX networks."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Allows the app to connect the phone to and disconnect the phone from WiMAX networks."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"score networks"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Allows the app to rank networks and influence which networks the tablet should prefer."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Allows the app to rank networks and influence which networks the phone should prefer."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"pair with Bluetooth devices"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Allows the app to view the configuration of Bluetooth on the tablet and to make and accept connections with paired devices."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Allows the app to view the configuration of the Bluetooth on the phone and to make and accept connections with paired devices."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index c13bb1e..9e94287 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sync"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Too many <xliff:g id="CONTENT_TYPE">%s</xliff:g> deletions."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Tablet storage is full. Delete some files to free space."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Watch storage is full. Delete some files to free up space."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Phone storage is full. Delete some files to free space."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Network may be monitored"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"By an unknown third party"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Ringer on"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Shutting down…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Your tablet will shut down."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Your watch will shut down."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Your phone will shut down."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Do you want to shut down?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Reboot to safe mode"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Allows the app to broadcast a notification that an SMS message has been received. Malicious apps may use this to forge incoming SMS messages."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"send WAP-PUSH-received broadcast"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Allows the app to broadcast a notification that a WAP PUSH message has been received. Malicious apps may use this to forge MMS message receipt or to silently replace the content of any web page with malicious variants."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"send score networks broadcast"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Allows the app to broadcast a notification that networks need to be scored. Never needed for normal apps."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"limit number of running processes"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Allows the app to control the maximum number of processes that will run. Never needed for normal apps."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"force background apps to close"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Allows the holder to bind to the top-level interface of a widget service. Should never be needed for normal apps."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"bind to a route provider service"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Allows the holder to bind to any registered route providers. Should never be needed for normal apps."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interact with device admin"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Allows the holder to send intents to a device administrator. Should never be needed for normal apps."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"bind to a TV input"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"change WiMAX state"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Allows the app to connect the tablet to and disconnect the tablet from WiMAX networks."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Allows the app to connect the phone to and disconnect the phone from WiMAX networks."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"score networks"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Allows the app to rank networks and influence which networks the tablet should prefer."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Allows the app to rank networks and influence which networks the phone should prefer."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"pair with Bluetooth devices"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Allows the app to view the configuration of Bluetooth on the tablet and to make and accept connections with paired devices."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Allows the app to view the configuration of the Bluetooth on the phone and to make and accept connections with paired devices."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index b999506..337ab64 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -337,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Permite que la aplicación transmita una notificación acerca de la recepción de un mensaje SMS. Las aplicaciones maliciosas pueden utilizar este permiso para falsificar mensajes SMS entrantes."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"enviar emisiones WAP-PUSH-recibido"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Permite que la aplicación transmita una notificación acerca de la recepción de un mensaje WAP PUSH. Las aplicaciones maliciosas pueden utilizar este permiso para falsificar la recepción de mensajes MMS o para reemplazar sin aviso el contenido de cualquier página web con variantes maliciosas."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"enviar transmisión de puntuación de redes"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Permite que la aplicación transmita una notificación que las redes necesitan para recibir una puntuación. Las aplicaciones normales no necesitan nunca este permiso."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"limitar la cantidad de procesos en ejecución"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Permite que la aplicación controle la cantidad máxima de procesos que se ejecutarán. Las aplicaciones normales no deben utilizar este permiso."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"forzar el cierre de aplicaciones de fondo"</string>
@@ -392,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Permite al propietario vincularse a la interfaz de nivel superior del servicio de widget. Las aplicaciones normales no deberían necesitar este permiso."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"vincular con un servicio de proveedor de rutas"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Permite al propietario vincular con proveedores de rutas registrados. No debe ser necesario para las aplicaciones normales."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interactuar con un administrador de dispositivos"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Permite enviar intentos a un administrador de dispositivos. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"vincular a una entrada de TV"</string>
@@ -642,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Cambiar el estado de WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permite que la aplicación conecte la tablet a una red WiMAX y que la desconecte de ella."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permite que la aplicación conecte el dispositivo a una red WiMAX y que lo desconecte de ella."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"puntuar redes"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Permite que la aplicación clasifique redes e influya en las redes que la tablet debería preferir."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Permite que la aplicación clasifique redes e influya en las redes que el teléfono debería preferir."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"vincular con dispositivos Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Permite que la aplicación vea la configuración de Bluetooth de la tablet y que cree y acepte conexiones con los dispositivos sincronizados."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Permite que la aplicación vea la configuración de Bluetooth del dispositivo y que cree y acepte conexiones con los dispositivos sincronizados."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 24a919f..920bdbc 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sincronización"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Demasiadas eliminaciones de <xliff:g id="CONTENT_TYPE">%s</xliff:g>"</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Se ha agotado el espacio de almacenamiento del tablet. Elimina algunos archivos para liberar espacio."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"El almacenamiento del reloj está lleno. Elimina algunos archivos para liberar espacio."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Se ha agotado el espacio de almacenamiento del teléfono. Elimina algunos archivos para liberar espacio."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Es posible que la red esté supervisada"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Por un tercero desconocido"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Timbre activado"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Apagando..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"El tablet se apagará."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"El reloj se apagará."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"El teléfono se apagará."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"¿Seguro que quieres apagar el teléfono?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Reiniciar en modo seguro"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Permite que la aplicación emita una notificación cuando se haya recibido un mensaje SMS. Las aplicaciones malintencionadas pueden usar este permiso para falsificar mensajes SMS entrantes."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"enviar emisión recibida mediante mensaje WAP PUSH"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Permite que la aplicación envíe una notificación cuando se haya recibido un mensaje WAP PUSH. Las aplicaciones malintencionadas pueden usar este permiso para falsificar la recepción de un mensaje MMS o para reemplazar sin aviso el contenido de cualquier página web con variantes malintencionadas."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"enviar notificaciones sobre la puntuación de las redes"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Permite que la aplicación emita una notificación que la red necesita para recibir una puntuación. Las aplicaciones normales no necesitan nunca este permiso."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"limitar el número de procesos en ejecución"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Permite que la aplicación controle el número máximo de procesos que se ejecutarán. No es necesario nunca para las aplicaciones normales."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"forzar el cierre de aplicaciones en segundo plano"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Permite enlazar con la interfaz de nivel superior de un servicio de widget. Las aplicaciones normales no deberían necesitar este permiso."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"enlazar con un servicio de proveedor de rutas"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Permite enlazar con proveedores de rutas registrados. No debe ser necesario para las aplicaciones normales."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interactuar con el administrador de un dispositivo"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Permite que se envíen intentos a un administrador de dispositivos. Las aplicaciones normales nunca deberían necesitar este permiso."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"enlazar a una entrada de TV"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Cambiar estado de WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permite que la aplicación conecte el tablet a redes WiMAX y lo desconecte de ellas."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permite que la aplicación conecte el teléfono a redes WiMAX y lo desconecte de ellas."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"puntuar redes"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Permite que la aplicación clasifique redes e influya en las redes que el tablet debe preferir."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Permite que la aplicación clasifique una red e influya en las redes que el teléfono debe preferir."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"vincular con dispositivos Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Permite que la aplicación acceda a la configuración de Bluetooth del tablet y que establezca y acepte conexiones con los dispositivos sincronizados."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Permite que la aplicación acceda a la configuración de Bluetooth del teléfono y que establezca y acepte conexiones con los dispositivos sincronizados."</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 92b7acb..222a06c 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sünkroonimine"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Liiga palju üksuse <xliff:g id="CONTENT_TYPE">%s</xliff:g> kustutusi."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Tahvelarvuti mäluruum on täis. Ruumi vabastamiseks kustutage mõned failid."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Kella talletusruum on täis. Ruumi vabastamiseks kustutage mõned failid."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Telefonimälu on täis. Ruumi vabastamiseks kustutage mõned failid."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Võrku võidakse jälgida"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Tundmatu kolmas osapool:"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Helin on sees"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Väljalülitamine ..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Teie tahvelarvuti lülitub välja."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Teie kell lülitub välja."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Teie telefon lülitub välja."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Kas soovite välja lülitada?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Ohutus režiimis taaskäivitamine"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Võimaldab rakendusel edastada teatise SMS-sõnumi vastuvõtmise kohta. Pahatahtlikud rakendused võivad seda kasutada sissetulevate SMS-sõnumite võltsimiseks."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"saada WAP-PUSH-vastuvõetud saateid"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Võimaldab rakendusel edastada teatise WAP PUSH-sõnumi vastuvõtmise kohta. Pahatahtlikud rakendused võivad seda kasutada MMS-sõnumite vastuvõtmise võltsimiseks või mis tahes veebilehe sisu salaja asendamiseks pahatahtlikuga."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"võrkude levi hinnangu saatmine"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Lubab rakendusel levitada märguannet, et võrke tuleb hinnata. Seda ei ole kunagi vaja tavapäraste rakenduste puhul."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"piira töötavate protsesside arvu"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Võimaldab rakendusel juhtida töötavate protsesside maksimaalset arvu. Tavarakenduste puhul pole seda vaja."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"sundige taustarakendused sulguma"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Lubab omanikul siduda vidina teenuse ülataseme liidesega. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"sidumine marsruudi pakkumisteenusega"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Lubab õiguste omajal luua seosed kõikide registreeritud marsruutide pakkujatega. Pole kunagi vajalik tavaliste rakenduste korral."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"seadme administraatoriga suhtlemine"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Võimaldab omanikul saata kavatsusi seadme administraatorile. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"sidumine TV-sisendiga"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX-i oleku muutmine"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Võimaldab rakendusel luua ja katkestada tahvelarvuti ühenduse WiMAX-i võrkudega."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Võimaldab rakendusel luua ja katkestada telefoni ühenduse WiMAX-i võrkudega."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"võrkude hindamine"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Lubab rakendusel võrke hinnata ja mõjutada seda, milliseid võrke peaks tahvelarvuti eelistama."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Lubab rakendusel võrke hinnata ja mõjutada seda, milliseid võrke peaks telefon eelistama."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"siduge Bluetoothi seadmetega"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Võimaldab rakendusel vaadata tahvelarvuti Bluetooth-konfiguratsiooni ning luua ja heaks kiita ühendusi seotud seadmetega."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Võimaldab rakendusel vaadata telefoni Bluetooth-konfiguratsiooni ning luua ja heaks kiita ühendusi seotud seadmetega."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index cf2f957..300dfcd 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"همگامسازی"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"تعداد موارد حذف شده <xliff:g id="CONTENT_TYPE">%s</xliff:g> بسیار زیاد است."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"حافظه رایانهٔ لوحی پر است! برخی از فایلها را حذف کنید تا فضا آزاد شود."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"حافظه ساعت پر است. برای آزادسازی فضا، چند فایل را حذف کنید."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"حافظه تلفن پر است. بعضی از فایلها را حذف کنید تا فضا آزاد شود."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"ممکن است شبکه نظارت شده باشد"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"توسط یک شخص ثالث ناشناس"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"زنگ روشن"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"در حال خاموش شدن…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"رایانهٔ لوحی شما خاموش میشود."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"ساعت شما خاموش میشود."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"گوشی شما خاموش میشود."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"آیا میخواهید تلفن خاموش شود؟"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"راهاندازی مجدد در حالت امن"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"به برنامه اجازه میدهد تا اعلان دریافت پیام کوتاه را پخش کند. برنامههای مخرب میتوانند از این برای جعل پیامهای کوتاه ورودی استفاده کنند."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"ارسال پخش دریافت شده توسط WAP-PUSH"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"به برنامه اجازه میدهد تا اعلانی را پخش کند که پیام WAP PUSH دریافت کرده است. برنامههای مخرب میتوانند از آن استفاده کنند تا دریافت پیام MMS را جعل کنند یا محتوای هر صفحهٔ وب را با انواع مخرب جایگزین کنند."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"ارسال اعلان پخش برای امتیازبندی شبکهها"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"به برنامه اجازه میدهد تا اعلانی پخش کند که شبکهها باید امتیازبندی شوند. هرگز برای برنامههای عادی مورد نیاز نیست."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"محدود کردن تعداد فرآیندهای در حال اجرا"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"به برنامه اجازه میدهد تا حداکثر تعداد پردازشهایی را که اجرا خواهد شد کنترل کند. هرگز برای برنامههای عادی لازم نیست."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"بستن اجباری برنامههای پسزمینه"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"به دارنده امکان میدهد تا به رابط سطح بالای نمایشگر راه دور وصل شود. نباید هرگز برای برنامههای عادی لازم باشد."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"اتصال به یک سرویس ابزارک"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"به دارنده اجازه میدهد که به رابط سطح بالای سرویس ابزارک متصل شود. هرگز برای برنامههای معمولی مورد نیاز نیست."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"اتصال به یک سرویس ارائهدهنده مسیر"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"به دارنده امکان میدهد به هر ارائهدهنده مسیر ثبت شدهای متصل شود. هرگز برای برنامههای عادی مورد نیاز نیست."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"تعامل با یک سرپرست دستگاه"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"به دارنده اجازه میدهد اهداف خود را به سرپرست دستگاه ارسال کند. برنامههای معمولی هیچگاه به این ویژگی نیازی ندارند."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"اتصال به ورودی تلویزیون"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"تغییر وضعیت WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"به برنامه امکان میدهد رایانهٔ لوحی را به شبکههای وایمکس متصل کرده یا اتصال آن را از این شبکهها قطع کند."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"به برنامه امکان میدهد تا تلفن را به شبکههای وایمکس متصل کرده یا اتصال آنرا از این شبکهها قطع کند."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"امتیازبندی شبکهها"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"به برنامه اجازه میدهد که شبکهها را درجهبندی کند و روی اینکه رایانه لوحی باید کدام شبکه را در اولویت قرار دهد تأثیر میگذارد."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"به برنامه اجازه میدهد که شبکهها را درجهبندی کند و روی اینکه تلفن باید کدام شبکه را در اولویت قرار دهد تأثیر میگذارد."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"جفت کردن با دستگاههای بلوتوث"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"به برنامه اجازه میدهد تا پیکربندی بلوتوث در رایانهٔ لوحی را مشاهده کند و اتصال با دستگاههای مرتبط را برقرار کرده و بپذیرد."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"به برنامه اجازه میدهد تا پیکربندی بلوتوث در تلفن را مشاهده کند، و اتصالات دستگاههای مرتبط را برقرار کرده و بپذیرد."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 4d11ad4..9202e0d 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -337,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Sallii sovelluksen lähettää ilmoituksen tekstiviestin vastaanotosta. Haitalliset sovellukset voivat käyttää tätä saapuvien tekstiviestien väärentämiseen."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"lähetä WAP-PUSH-vastaanotettu lähetys"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Antaa sovelluksen lähettää ilmoituksen WAP PUSH -viestin vastaanotosta. Haitalliset sovellukset voivat käyttää tätä MMS-viestien vastaanoton väärentämiseen tai sivujen sisällön korvaamiseen huomaamattomasti haitallisella sisällöllä."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"lähetä verkkojen pisteet"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Sallii sovelluksen lähettää ilmoituksen verkon pisteytystarpeesta. Ei tarvita tavallisissa sovelluksissa."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"rajoita käynnissä olevien prosessien määrää"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Antaa sovelluksen hallita suoritettavien sovellusten enimmäismäärää. Ei tavallisten sovellusten käyttöön."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"pakota taustasovelluksia sulkeutumaan"</string>
@@ -392,6 +390,10 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Antaa sovelluksen sitoutua widget-palvelun ylemmän tason käyttöliittymään. Ei tavallisten sovelluksien käyttöön."</string>
+ <!-- no translation found for permlab_bindRouteProvider (4869394607915096847) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRouteProvider (4703804520859960329) -->
+ <skip />
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"kommunikoi laitteen järjestelmänvalvojan kanssa"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Antaa sovelluksen lähettää aikomuksia laitteen järjestelmänvalvojalle. Ei tavallisten sovellusten käyttöön."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"sido TV-tuloon"</string>
@@ -642,12 +644,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Vaihda WiMAX-verkon tilaa"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Antaa sovelluksen muodostaa tablet-laitteella yhteyden WiMAX-verkkoon ja katkaista yhteyden."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Antaa sovelluksen muodostaa puhelimella yhteyden WiMAX-verkkoon ja katkaista yhteyden."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"pisteytä verkot"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Sallii sovelluksen asettaa verkkoja paremmuusjärjestykseen ja vaikuttaa siihen, mikä verkko tablet-laitteen kannattaa valita."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Sallii sovelluksen asettaa verkkoja paremmuusjärjestykseen ja vaikuttaa siihen, mikä verkko puhelimen kannattaa valita."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"muodosta laitepari Bluetooth-laitteiden kanssa"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Antaa sovelluksen tarkastella tablet-laitteen Bluetooth-asetuksia sekä muodostaa ja hyväksyä laitepariyhteyksiä."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Antaa sovelluksen tarkastella puhelimen Bluetooth-asetuksia sekä muodostaa ja hyväksyä laitepariyhteyksiä muihin laitteisiin."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index d2f4c9b..5e2cb28 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synchroniser"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Trop de contenus supprimés (<xliff:g id="CONTENT_TYPE">%s</xliff:g>)."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"La mémoire de la tablette est pleine. Supprimez des fichiers pour libérer de l\'espace."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"La mémoire de la montre est pleine. Supprimez des fichiers pour libérer de l\'espace."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"La mémoire du téléphone est pleine. Veuillez supprimer des fichiers pour libérer de l\'espace."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Le réseau peut être surveillé"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Par un tiers inconnu"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Sonnerie activée"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Arrêt en cours..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Votre tablette va s\'éteindre."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Votre montre va s\'éteindre."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Votre téléphone va s\'éteindre."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Voulez-vous éteindre l\'appareil?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Redémarrer en mode sans échec"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Permet à l\'application d\'envoyer une notification indiquant la réception d\'un message texte. Des applications malveillantes peuvent utiliser cette fonctionnalité pour créer de faux messages entrants."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"envoyer une diffusion de réception de WAP par poussée"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Permet à l\'application d\'envoyer une notification indiquant la réception d\'un message WAP par poussée. Des applications malveillantes peuvent utiliser cette fonctionnalité pour créer de faux messages multimédias entrants ou pour remplacer le contenu d\'une page Web par du contenu malveillant."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"diffuser le classement des réseaux"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Autorise l\'application à diffuser une notification signalant que les réseaux doivent être évalués. Cela n\'est jamais nécessaire pour les applications normales."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"restreindre le nombre de processus en cours d\'exécution"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Permet à l\'application de définir le nombre maximal de processus devant s\'exécuter. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"forcer la fermeture des applications en arrière-plan"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un service de widget. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"associer à un fournisseur d\'itinéraires enregistré"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Permet à l\'application autorisée de s\'associer à des fournisseurs d\'itinéraires enregistrés. Cette fonctionnalité ne devrait pas être nécessaire pour les applications standards."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interagir avec l\'administrateur d\'un périphérique"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Permet à l\'application autorisée d\'envoyer des intentions à l\'administrateur de l\'appareil. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"s\'associer à une entrée de téléviseur"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Modifier l\'état du WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permet à l\'application de connecter la tablette aux réseaux WiMAX et de l\'en déconnecter."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permet à l\'application de connecter le téléphone aux réseaux WiMAX et de l\'en déconnecter."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"classer les réseaux"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Autorise l\'application à classer les réseaux et à influencer la sélection du réseau par la tablette."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Autorise l\'application à classer les réseaux et à influencer la sélection du réseau par le téléphone."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"s\'associer à des appareils Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Permet à l\'application d\'accéder à la configuration du Bluetooth sur la tablette, et d\'établir et accepter des connexions avec les appareils associés."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Permet à l\'application d\'accéder à la configuration du Bluetooth sur le téléphone, et d\'établir et accepter des connexions avec les appareils associés."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 7a42a6d..6435652 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synchronisation"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Trop de contenus supprimés (<xliff:g id="CONTENT_TYPE">%s</xliff:g>)."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"La mémoire de la tablette est pleine. Supprimez des fichiers pour libérer de l\'espace."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"La mémoire de la montre est saturée. Veuillez supprimer des fichiers pour libérer de l\'espace."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"La mémoire du téléphone est pleine. Veuillez supprimer des fichiers pour libérer de l\'espace."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Il est possible que le réseau soit surveillé."</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Par un tiers inconnu"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Sonnerie activée"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Arrêt en cours..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Votre tablette va s\'éteindre."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"La montre va s\'éteindre."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Votre téléphone va s\'éteindre."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Voulez-vous éteindre l\'appareil ?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Redémarrer en mode sans échec"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Permet à l\'application d\'envoyer une notification indiquant la réception d\'un SMS. Des applications malveillantes peuvent exploiter cette fonctionnalité pour créer de faux SMS entrants."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"Envoi de diffusion de réception de WAP PUSH"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Permet à l\'application d\'envoyer une notification indiquant la réception d\'un message WAP PUSH. Des applications malveillantes peuvent exploiter cette fonctionnalité pour créer de faux MMS entrants ou pour remplacer le contenu d\'une page Web par du contenu malveillant."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"diffuser des notifications pour l\'évaluation des réseaux"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Autoriser l\'application à diffuser une notification signalant que les réseaux doivent être évalués. Cette autorisation n\'est pas nécessaire pour les applications standards."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"Nombre maximal de processus en cours d\'exécution"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Permet à l\'application de contrôler le nombre maximal de processus devant s\'exécuter. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"forcer la fermeture des applications en arrière-plan"</string>
@@ -361,7 +357,7 @@
<string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"Permet à l\'application de lancer l\'interface utilisateur de confirmation de sauvegarde complète. Seules certaines applications peuvent bénéficier de cette permission."</string>
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"Affichage de fenêtres non autorisées"</string>
<string name="permdesc_internalSystemWindow" msgid="7458387759461466397">"Permet à l\'application de créer des fenêtres destinées à être utilisées par l\'interface utilisateur du système interne. Les applications standards ne doivent pas utiliser cette fonctionnalité."</string>
- <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"S\'afficher en surimpression dans les autres applis"</string>
+ <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"Se superposer aux autres applis"</string>
<string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Permet à l\'application d\'ignorer d\'autres applications ou certaines parties de l\'interface utilisateur. Cela peut altérer votre utilisation de l\'interface de n\'importe quelle application, ou modifier ce que vous pensez voir dans d\'autres applications."</string>
<string name="permlab_setAnimationScale" msgid="2805103241153907174">"Réglage de la vitesse des animations"</string>
<string name="permdesc_setAnimationScale" msgid="7690063428924343571">"Permet à l\'application de modifier à tout moment la vitesse générale des animations pour les ralentir ou les accélérer."</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un service widget. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"s\'associer à un fournisseur d\'itinéraires"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Permettre à l\'application autorisée de s\'associer à n\'importe quel fournisseur d\'itinéraires. Ne devrait pas être nécessaire pour les applications standards."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interagir avec l\'administrateur du périphérique"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Permet à l\'application autorisée d\'envoyer des intentions à l\'administrateur de l\'appareil. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"s\'associer à une entrée TV"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Modifier l\'état du WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permet à l\'application de connecter la tablette aux réseaux WiMAX et de l\'en déconnecter."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permet à l\'application de connecter le téléphone aux réseaux WiMAX et de l\'en déconnecter."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"évaluer les réseaux"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Autoriser l\'application à classer les réseaux et à influencer la sélection du réseau sur la tablette"</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Autoriser l\'application à classer les réseaux et à influencer la sélection du réseau sur le téléphone"</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"associer à des appareils Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Permet à l\'application d\'accéder à la configuration du Bluetooth sur la tablette, et d\'établir et accepter des connexions avec les appareils associés."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Permet à l\'application d\'accéder à la configuration du Bluetooth sur le téléphone, et d\'établir et accepter des connexions avec les appareils associés."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index dc298de..831b327 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"समन्वयन"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"बहुत से <xliff:g id="CONTENT_TYPE">%s</xliff:g> हटाए जाते हैं."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"टेबलेट संग्रहण भर गया है. स्थान रिक्त करने के लिए कुछ फ़ाइलें हटाएं."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"घड़ी संग्रहण भर गया है. स्थान रिक्त करने के लिए कुछ फ़ाइलें हटाएं."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"फ़ोन संग्रहण भर गया है. स्थान रिक्त करने के लिए कुछ फ़ाइलें हटाएं."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"नेटवर्क को मॉनिटर किया जा सकता है"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"किसी अज्ञात तृतीय पक्ष के द्वारा"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"रिंगर चालू"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"शट डाउन हो रहा है..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"आपकी टेबलेट शट डाउन हो जाएगी."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"आपकी घड़ी बंद हो जाएगी."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"आपका फ़ोन शट डाउन हो जाएगा."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"क्या आप शट डाउन करना चाहते हैं?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"सुरक्षित मोड में रीबूट करें"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"ऐप्स को वह सूचना प्रसारित करने देता है जो SMS संदेश ने प्राप्त की है. दुर्भावनापूर्ण ऐप्स इसका उपयोग नकली इनकमिंग संदेश गढ़ने के लिए कर सकते हैं."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"WAP-PUSH-प्राप्त प्रसारण भेजें"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"ऐप्स को वह सूचना प्रसारित करने देता है जो WAP PUSH संदेश को प्राप्त हुआ है. दुर्भावनापूर्ण ऐप्स इसका उपयोग नकली MMS संदेश प्राप्त करने या किसी वेबपृष्ठ की सामग्री को दुर्भावनापूर्ण दूसरे रूप से चुपचाप प्रतिस्थापित करने के लिए कर सकते हैं."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"स्कोर नेटवर्क प्रसारण भेजें"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"ऐप्स को यह सूचना प्रसारित करने देती है कि नेटवर्क को स्कोर किए जाने की आवश्यकता है. सामान्य ऐप्स के लिए कभी भी आवश्यक नहीं."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"चल रही प्रक्रियाओं की संख्या सीमित करें"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"ऐप्स को चलाई जाने वाली अधिकतम प्रक्रियाओं को नियंत्रित करने देता है. सामान्य ऐप्स के लिए कभी आवश्यक नहीं होती."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"पृष्ठभूमि ऐप्स को बलपूर्वक बंद करें"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"धारक को किसी रिमोट डिस्प्ले के शीर्ष-स्तरीय इंटरफ़ेस से आबद्ध होने देती है. सामान्य ऐप्स के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"किसी विजेट सेवा से आबद्ध करें"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"धारक को किसी विजेट सेवा के शीर्ष-स्तर इंटरफ़ेस से आबद्ध होने देता है. सामान्य ऐप्स के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"किसी रूट प्रदाता सेवा से आबद्ध हों"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"धारक को किसी भी पंजीकृत रूट प्रदाता से आबद्ध रहने देती है. सामान्य ऐप्स के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"किसी उपकरण व्यवस्थापक के साथ सहभागिता करें"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"धारक को किसी उपकरण व्यवस्थापक को उद्देश्य भेजने देता है. सामान्य ऐप्स के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"टीवी इनपुट से आबद्ध करें"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX स्थिति बदलें"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"ऐप्स को WiMAX नेटवर्क से टेबलेट को कनेक्ट और डिस्कनेक्ट करने देता है."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"ऐप्स को WiMAX नेटवर्क से फ़ोन को कनेक्ट और डिस्कनेक्ट करने देता है."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"स्कोर नेटवर्क"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"ऐप्स को नेटवर्क को रैंक करने देती है और इस बात पर ज़ोर देती है कि टेबलेट को किस नेटवर्क को प्राथमिकता देनी चाहिए."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"ऐप्स को नेटवर्क को रैंक करने देती है और इस बात पर ज़ोर देती है कि फ़ोन को किस नेटवर्क को प्राथमिकता देनी चाहिए."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"Bluetooth उपकरणों के साथ युग्मित करें"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"ऐप्स को टेबलेट पर Bluetooth का कॉन्फ़िगरेशन देखने, और युग्मित उपकरणों के साथ कनेक्शन बनाने और स्वीकार करने देता है."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"ऐप्स को फ़ोन पर Bluetooth का कॉन्फ़िगरेशन देखने, और युग्मित उपकरणों के साथ कनेक्शन बनाने और स्वीकार करने देता है."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 96872e8..40b194c 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sinkronizacija"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Previše brisanja stavki <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Prostor za pohranu tabletnog računala pun je. Izbrišite nekoliko datoteka kako biste oslobodili prostor."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Pohrana sata puna je. Izbrišite neke datoteke da biste oslobodili prostor."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Prostor za pohranu na telefonu je pun. Izbrišite nekoliko datoteka kako biste oslobodili prostor."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Mreža se možda nadzire"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Od strane nepoznate treće strane"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Zvono uključeno"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Isključivanje..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Vaš tabletni uređaj će se isključiti."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Sat će se isključiti."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Vaš će se telefon isključiti."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Želite li isključiti uređaj?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Ponovno pokretanje u sigurnom načinu rada"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Omogućuje aplikaciji emitiranje obavijesti da je primljena SMS poruka. Zlonamjerne aplikacije mogu to upotrijebiti za krivotvorenje dolaznih SMS poruka."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"slanje WAP-PUSH-primljenih prijenosa"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Omogućuje aplikaciji emitiranje obavijesti da je primljena WAP PUSH poruka. Zlonamjerne aplikacije mogu to upotrijebiti da bi krivotvorile prijem MMS poruka ili da bi potajno zamijenile sadržaj bilo koje web-stranice zlonamjernim varijantama."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"slanje obavijesti za ocjenjivanje mreža"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Aplikaciji omogućuje emitiranje obavijesti da se mreža treba ocijeniti. Nije potrebno za normalne aplikacije."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"ograničavanje broja pokrenutih postupaka"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Omogućuje aplikaciji upravljanje maksimalnim brojem postupaka koji će biti pokrenuti. Nikada nije potrebno za uobičajene aplikacije."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"prisilno zatvaranje pozadinskih aplikacija"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Nositelju omogućuje vezanje uz sučelje najviše razine usluge widgeta. Ne bi smjelo biti potrebno za normalne aplikacije."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"povezivanje s davateljem usluge usmjeravanja poziva"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Nositelju omogućuje povezivanje s registriranim davateljem usluga usmjeravanja poziva. Nije potrebno za normalne aplikacije."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interakcija s administratorom uređaja"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Nositelju omogućuje slanje namjera administratoru uređaja. Ne bi smjelo biti potrebno za normalne aplikacije."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"povezivanje s TV ulazom"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Promjena stanja WiMAX mreže"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Aplikaciji omogućuje povezivanje tabletnog računala s WiMAX mrežama i prekidanje veze tabletnog računala s njima."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Aplikaciji omogućuje povezivanje telefona s WiMAX mrežama i prekidanje veze telefona s njima."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"ocjenjivanje mreža"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Aplikaciji omogućuje rangiranje mreža i utjecanje na odabir preferiranih mreža na tabletu."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Aplikaciji omogućuje rangiranje mreža i utjecanje na odabir preferiranih mreža na telefonu."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"uparivanje s Bluetooth uređajima"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Aplikaciji omogućuje pregled konfiguracije Bluetootha na tabletnom računalu te uspostavljanje i prihvaćanje veza s uparenim uređajima."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Aplikaciji omogućuje pregled konfiguracije Bluetootha na telefonu te uspostavljanje i prihvaćanje veza s uparenim uređajima."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index aded23b..7c95b53 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Szinkronizálás"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Túl sok <xliff:g id="CONTENT_TYPE">%s</xliff:g> törlés."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"A táblagép tárhelye tele van. Szabadítson fel helyet néhány fájl törlésével."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Az óra tárhelye megtelt. Szabadítson fel helyet néhány fájl törlésével."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"A telefon tárhelye megtelt. Hely felszabadításához töröljön néhány fájlt."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Lehet, hogy a hálózat felügyelt"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Ismeretlen harmadik fél által"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Csengő bekapcsolva"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Leállítás..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"A táblagép ki fog kapcsolni."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Az óra ki fog kapcsolni."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"A telefon le fog állni."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Kikapcsolja?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Újraindítás csökkentett módban"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Lehetővé teszi az alkalmazás számára értesítés küldését SMS érkezéséről. A rosszindulatú alkalmazások beérkező SMS-ek hamisítására használhatják fel ezt."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"WAP-PUSH alapú üzenetek küldése"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Lehetővé teszi az alkalmazás számára értesítés küldését WAP PUSH üzenet érkezése esetén. A rosszindulatú alkalmazások arra használhatják ezt, hogy MMS-kézbesítési jelentést hamisítsanak, vagy hogy a háttérben rosszindulatú variánssal cseréljék le bármelyik weboldal tartalmát."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"hálózatpontozási értesítés küldése"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Lehetővé teszi, hogy az alkalmazás szétküldjön egy értesítést, amely szerint a hálózatokat pontozni kell. A normál alkalmazásoknak erre soha nincs szükségük."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"futó folyamatok számának korlátozása"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Lehetővé teszi az alkalmazás számára a futtatható folyamatok maximális számának vezérlését. Soha nem lehet rá szüksége a normál alkalmazásoknak."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"háttéralkalmazások leállításának kényszerítése"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Lehetővé teszi a használó számára, hogy csatlakozzon egy modulszolgáltatás legfelső szintű kezelőfelületéhez. A normál alkalmazásoknak erre soha nincs szüksége."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"csatlakozás egy útvonal-szolgáltatóhoz"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Az eszköz kezelője csatlakozhat bármely regisztrált útvonal-szolgáltatóhoz. A normál alkalmazások esetében erre nincs szükség."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"az eszközkezelő használata"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Lehetővé teszi a tulajdonos számára, hogy célokat küldjön egy eszközkezelőnek. A normál alkalmazásoknak erre soha nincs szüksége."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"csatlakozás tévébemenethez"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX-állapot módosítása"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Lehetővé teszi az alkalmazás számára, hogy a táblagépet csatlakoztassa WiMAX-hálózathoz vagy leválassza azt róla."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Lehetővé teszi az alkalmazás számára, hogy a telefont csatlakoztassa WiMAX-hálózathoz vagy leválassza azt róla."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"hálózatok pontozása"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Lehetővé teszi, hogy az alkalmazás rangsorolja a hálózatokat, illetve befolyásolja, hogy a táblagép mely hálózatokat részesítse előnyben."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Lehetővé teszi, hogy az alkalmazás rangsorolja a hálózatokat, illetve befolyásolja, hogy a telefon mely hálózatokat részesítse előnyben."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"Bluetooth-eszközök párosítása"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Lehetővé teszi az alkalmazás számára a táblagépen lévő Bluetooth beállításainak megtekintését, valamint kapcsolatok kezdeményezését és fogadását a párosított eszközökkel."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Lehetővé teszi az alkalmazás számára a telefonon lévő Bluetooth beállításainak megtekintését, valamint kapcsolatok kezdeményezését és fogadását a párosított eszközökkel."</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 9edccf8..c788068 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -337,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Թույլ է տալիս հավելվածին հաղորդել ծանուցում, որ ստացվել է SMS հաղորդագրություն: Վնասարար հավելվածները կարող են օգտագործել սա` կեղծելու մուտքային SMS հաղորդագրությունները:"</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"ուղարկել ստացված WAP-PUSH-ի հաղորդում"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Թույլ է տալիս հավելվածին հաղորդել ծանուցում, որ ստացվել է WAP PUSH հաղորդագրություն: Վնասարար հավելվածները կարող են օգտագործել սա` կեղծելու MMS հաղորդագրության ստացումը կամ աննկատ փոխարինելու ցանկացած կայքի բովանդակությունը վնասարար տարբերակներով:"</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"ուղարկել ցանցերի գնահատականի հեռարձակում"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Ծրագրին թույլ է տալիս հեռարձակել ծանուցում, որ ցանցերը պետք է հաշվարկվեն: Նորմալ ծրագրերում երբեք պետք չի գալիս:"</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"սահմանափակել աշխատող գործընթացների թիվը"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Թույլ է տալիս հավելվածին վերահսկել գործընթացների առավելագույն թիվը, որ աշխատելու են: Երբևէ անհրաժեշտ չէ սովորական հավելվածների համար:"</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"ստիպել, որ առաջին պլանի հավելվածները փակվեն"</string>
@@ -392,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Թույլ է տալիս սեփականատիրոջը միանալ հեռակա էկրանի վերին մակարդակի ինտերֆեյսին: Սովորական ծրագրերի համար երբևէ չպետք է անհրաժեշտ լինի:"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"միանալ վիջեթ ծառայությանը"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Թույլ է տալիս սեփականատիրոջը միանալ վիջեթ ծառայության վերին մակարդակի ինտերֆեյսին: Սովորական հավելվածների համար երբևէ չպետք է անհրաժեշտ լինի:"</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"կապվել երթուղու մատակարարի ծառայությանը"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Թույլ է տալիս տիրոջը կապվել երթուղու մատակարարներից ցանկացածին: Սովորական ծրագրերի համար երբեք անհրաժեշտ չէ:"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"փոխգործակցել սարքի կառավարչի հետ"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Թույլ է տալիս սեփականատիրոջը ուղարկել մտադրություններ սարքի կառավարչին: Սովորական հավելվածների համար երբևէ չպետք է անհրաժեշտ լինի:"</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"միանալ հեռուստացույցի մուտքին"</string>
@@ -642,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Փոխել WiMAX-ի կարգավիճակը"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Թույլ է տալիս հավելվածին գրասալիկը միացնել WiMAX ցանցին և անջատվել այդ ցանցից:"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Թույլ է տալիս հավելվածին հեռախոսը միացնել WiMAX ցանցին և անջատել այդ ցանցից:"</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"ցանցերի գնահատական"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Ծրագրին թույլ է տալիս դասակարգել ցանցերը և ազդել գրասալիկի նախընտրելի ցանցի ընտրության գործընթացի վրա:"</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Ծրագրին թույլ է տալիս դասակարգել ցանցերը և ազդել հեռախոսի նախընտրելի ցանցի ընտրության գործընթացի վրա:"</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"զուգակցվել Bluetooth սարքերի հետ"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Թույլ է տալիս հավելվածին տեսնել Bluetooth-ի կարգավորումը գրասալիկի վրա և կապվել ու կապեր ընդունել զուգակցված սարքերի հետ:"</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Թույլ է տալիս հավելվածին տեսնել Bluetooth-ի կարգավորումը հեռախոսի վրա և կապվել ու կապեր ընդունել զուգակցված սարքերի հետ:"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index be0730a..a7ede78 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sinkron"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Terlalu banyak <xliff:g id="CONTENT_TYPE">%s</xliff:g> penghapusan."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Penyimpanan tablet penuh. Hapus beberapa file untuk mengosongkan ruang."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Penyimpanan arloji penuh. Hapus beberapa file untuk mengosongkan ruang."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Penyimpanan di ponsel penuh. Hapus sebagian file untuk mengosongkan ruang."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Jaringan mungkin dipantau"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Oleh pihak ketiga yang tidak dikenal"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Pendering nyala"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Sedang mematikan..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Tablet Anda akan dimatikan."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Arloji Anda akan dimatikan."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Ponsel Anda akan dimatikan."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Anda ingin mematikannya?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Reboot ke mode aman"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Mengizinkan apl menyiarkan pemberitahuan bahwa pesan SMS telah diterima. Apl berbahaya dapat menggunakan ini untuk memalsukan pesan SMS masuk."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"kirim siaran WAP-PUSH-diterima"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Mengizinkan apl menyiarkan pemberitahuan bahwa pesan WAP PUSH telah diterima. Apl berbahaya dapat menggunakan ini untuk memalsukan penerimaan pesan MMS atau diam-diam mengganti konten laman web apa pun dengan varian berbahaya."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"mengirim siaran beri skor jaringan"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Memungkinkan aplikasi menyiarkan pemberitahuan bahwa jaringan perlu diberi skor. Tidak pernah diperlukan untuk aplikasi normal."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"batasi jumlah dari proses yang berjalan"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Mengizinkan apl mengontrol jumlah maksimum proses yang akan berjalan. Tidak pernah diperlukan oleh apl normal."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"paksa aplikasi latar belakang agar menutup"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Mengizinkan pemegang mengikat antarmuka tingkat tinggi dari suatu layanan widget. Tidak pernah diperlukan oleh apl normal."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"mengikat ke layanan penyedia rute"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Memungkinkan pemegang mengikat ke penyedia rute terdaftar mana pun. Tidak pernah dibutuhkan untuk aplikasi normal."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"berinteraksi dengan admin perangkat"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Mengizinkan pemegang mengirimkan tujuan kepada administrator perangkat. Tidak pernah diperlukan oleh apl normal."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"mengikat ke masukan TV"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Ubah status WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Memungkinkan aplikasi menyambungkan tablet ke dan memutus tablet dari jaringan WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Memungkinkan aplikasi menyambungkan ponsel ke dan memutus ponsel dari jaringan WiMAX."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"memberi skor jaringan"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Memungkinkan aplikasi menilai jaringan dan memengaruhi jaringan mana yang sebaiknya dipilih tablet."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Memungkinkan aplikasi menilai jaringan dan memengaruhi jaringan mana yang sebaiknya dipilih ponsel."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"sandingkan dengan perangkat Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Memungkinkan aplikasi melihat konfigurasi Bluetooth di tablet, dan membuat serta menerima sambungan dengan perangkat yang disandingkan."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Memungkinkan aplikasi melihat konfigurasi Bluetooth di ponsel, dan membuat serta menerima sambungan dengan perangkat yang disandingkan."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 1ef77de..4c3d8a3 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sincronizzazione"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Troppe eliminazioni di <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Spazio di archiviazione del tablet esaurito. Elimina alcuni file per liberare spazio."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"La memoria dell\'orologio è piena. Elimina alcuni file per liberare spazio."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Spazio di archiviazione del telefono esaurito. Elimina alcuni file per liberare spazio."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"La rete potrebbe essere monitorata"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Da una terza parte sconosciuta"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Suoneria attiva"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Spegnimento..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Il tablet verrà spento."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"L\'orologio verrà spento."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Il telefono verrà spento."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Spegnere?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Riavvia in modalità provvisoria"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Consente all\'applicazione di trasmettere una notifica che informa della ricezione di un messaggio SMS. Le applicazioni dannose potrebbero farne uso per creare messaggi SMS in arrivo."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"invio broadcast ricevuti tramite WAP-PUSH"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Consente all\'applicazione di trasmettere una notifica che informa della ricezione di un messaggio WAP PUSH. Le applicazioni dannose potrebbero farne uso per simulare la ricezione di messaggi MMS o per sostituire di nascosto i contenuti di qualsiasi pagina web con varianti dannose."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"invio trasmissione classificazione reti"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Consente all\'app di trasmettere una notifica indicante che le reti devono essere classificate. Opzione non necessaria per le app normali."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"numero limite di processi in esecuzione"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Consente all\'applicazione di controllare il numero massimo di processi che verranno eseguiti. Mai necessaria per le applicazioni normali."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"chiusura forzata applicazioni di background"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Consente l\'associazione all\'interfaccia principale di un servizio widget. Non dovrebbe mai essere necessario per le normali applicazioni."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"collegamento a un servizio provider di routing"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Consente al titolare di collegarsi a qualsiasi provider di routing registrato. Non dovrebbe mai essere necessario per le normali applicazioni."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interazione con un amministratore dispositivo"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Consente l\'invio di intent a un amministratore del dispositivo. L\'autorizzazione non dovrebbe mai essere necessaria per le normali applicazioni."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"collegamento a ingresso TV"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Modifica stato WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Consente all\'applicazione di connettere/disconnettere il tablet dalle reti WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Consente all\'applicazione di connettere/disconnettere il telefono dalle reti WiMAX."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"valutazione reti"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Consente all\'app di stilare una classifica delle reti e determinare quali di queste il tablet deve prediligere."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Consente all\'app di stilare una classifica delle reti e determinare quali di queste il telefono deve prediligere."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"accoppiamento con dispositivi Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Consente all\'applicazione di visualizzare la configurazione del Bluetooth sul tablet e di stabilire e accettare connessioni con dispositivi accoppiati."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Consente all\'applicazione di visualizzare la configurazione del Bluetooth sul telefono e di stabilire e accettare connessioni con dispositivi accoppiati."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index f343f4a..f818574 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"סינכרון"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"יש מחיקות רבות מדי של <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"שטח האחסון של הטאבלט מלא. מחק קבצים כדי לפנות מקום."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"שטח האחסון של השעון מלא. מחק כמה קבצים כדי לפנות שטח."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"שטח האחסון של הטלפון מלא. מחק חלק מהקבצים כדי לפנות שטח."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"ייתכן שהרשת מנוטרת"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"על ידי צד שלישי לא מוכר"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"צלצול מופעל"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"מכבה..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"הטאבלט שלך יכבה."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"השעון יכבה."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"הטלפון שלך יכובה."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"האם ברצונך לבצע כיבוי?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"אתחל למצב בטוח"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"מאפשר לאפליקציה לשדר התראה על כך שהתקבלה הודעת SMS. אפליקציות זדוניות עלולות להשתמש בכך כדי לזייף הודעות SMS נכנסות."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"שלח שידור שהתקבל באמצעות WAP-PUSH"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"מאפשר לאפליקציה לשדר התראה על כך שהתקבלה הודעה מסוג WAP PUSH. אפליקציות זדוניות עלולות להשתמש בכך כדי לזייף קבלה של הודעות MMS או כדי להחליף בחשאי את התוכן של דף אינטרנט כלשהו בגירסאות זדוניות."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"שלח שידור לדירוג רשתות"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"מאפשר לאפליקציה לשדר התראה על כדי שיש לדרג את הרשתות. לא בשימוש עבור אפליקציות רגילות."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"הגבל את מספר התהליכים הפועלים"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"מאפשר לאפליקציה לשלוט על המספר המרבי של תהליכים שיפעלו. הרשאה זו לעולם אינה נחוצה לאפליקציות רגילים."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"אילוץ סגירה של אפליקציות ברקע"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"הרשאה זו מאפשרת למשתמש לבצע איגוד לממשק הרמה העליונה של צג רחוק. לעולם אינה אמורה להיות נחוצה לאפליקציות רגילות."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"הכפפה לשירות Widget"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"מאפשר למשתמש לבצע איגוד לממשק הרמה העליונה של שירות Widget. הרשאה זו לעולם אינה נחוצה לאפליקציות רגילים."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"איגוד לשירות של ספק ניתוב"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"מאפשרת לבעלים לאגד לספקי ניתוב רשומים. לעולם לא אמורה להיות נחוצה עבור אפליקציות רגילות."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"קיים אינטראקציה עם מנהל המכשיר"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"מאפשר למשתמש לשלוח כוונות למנהל התקנים. הרשאה זו לעולם אינה נחוצה לאפליקציות רגילים."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"איגוד לקלט טלוויזיה"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"שנה את מצב WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"מאפשר לאפליקציה לחבר את הטאבלט לרשתות WiMAX ולהתנתק מהן."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"מאפשר לאפליקציה לחבר את הטלפון לרשתות WiMAX ולהתנתק מהן."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"דרג רשתות"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"מאפשר ליישום לדרג רשתות ולהשפיע על הרשתות שאותן הטאבלט יעדיף."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"מאפשר ליישום לדרג רשתות ולהשפיע על הרשתות שאותן הטלפון יעדיף."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"התאמה למכשירי Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"מאפשר לאפליקציה להציג את תצורת ה-Bluetooth בטאבלט, וכן ליצור ולקבל חיבורים עם מכשירים מותאמים."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"מאפשר לאפליקציה להציג את תצורת ה-Bluetooth בטלפון, וכן ליצור ולקבל חיבורים עם מכשירים מותאמים."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 501fb81..159a19e 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"同期"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"<xliff:g id="CONTENT_TYPE">%s</xliff:g>での削除が多すぎます。"</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"タブレットのストレージに空き領域がありません。ファイルを削除して空き領域を確保してください。"</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"ウォッチのストレージに空き領域がありません。ファイルを削除して空き領域を確保してください。"</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"端末のストレージに空き領域がありません。ファイルを削除して空き領域を確保してください。"</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"ネットワークが監視される場合があります"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"不明な第三者"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"着信音オン"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"シャットダウン中..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"タブレットの電源をOFFにします。"</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"ウォッチの電源をOFFにします。"</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"携帯電話の電源を切ります。"</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"シャットダウンしますか?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"再起動してセーフモードに変更"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"SMSメッセージの受信通知の配信をアプリに許可します。この許可を悪意のあるアプリケーションに利用されると、受信SMSメッセージが偽造される恐れがあります。"</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"WAP-PUSH受信ブロードキャストの送信"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"WAP PUSHメッセージの受信通知を配信することをアプリに許可します。この許可を悪意のあるアプリに利用されると、MMSメッセージの受信確認が偽造されたりウェブページのコンテンツが悪意のあるコンテンツに密かに置き換えられたりする恐れがあります。"</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"ネットワークスコアのブロードキャスト送信"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"ネットワークのスコアリングが必要であるという通知をブロードキャスト送信することをアプリに許可します。通常のアプリでは不要です。"</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"実行中のプロセスの数を制限"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"実行するプロセスの上限数を制御することをアプリに許可します。通常のアプリでは不要です。"</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"バックグラウンドのアプリの強制終了"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"リモートディスプレイのトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ウィジェットサービスにバインド"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ウィジェットサービスのトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"ルートプロバイダサービスへのバインド"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"登録済みのルートプロバイダにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"デバイス管理者との通信"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"デバイス管理者へのintentの送信を所有者に許可します。通常のアプリでは不要です。"</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"テレビの入力へのバインド"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX状態の変更"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"タブレットのWiMAXネットワークへの接続と切断をアプリに許可します。"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"携帯端末のWiMAXネットワークへの接続と切断をアプリに許可します。"</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"ネットワークスコア"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"ネットワークを順位付けし、タブレットでのネットワークの優先順位に反映することをアプリに許可します。"</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"ネットワークを順位付けし、携帯電話でのネットワークの優先順位に反映することをアプリに許可します。"</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"Bluetoothデバイスのペアの設定"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"タブレットのBluetooth設定を表示すること、ペアの端末に接続すること/ペアの端末からの接続を受け入れることをアプリに許可します。"</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"携帯端末のBluetooth設定を表示すること、ペアの端末に接続すること/ペアの端末からの接続を受け入れることをアプリに許可します。"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 5f7fa89..db9088c 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"სინქრონიზაცია"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"<xliff:g id="CONTENT_TYPE">%s</xliff:g>-ის ძალიან ბევრი წაშლილები."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"ტაბლეტის მეხსიერება გავსებულია. ადგილის გასათავისუფლებლად წაშალეთ ფაილების ნაწილი."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"საათის მეხსიერება გავსებულია. ადგილის გასათავისუფლებლად წაშალეთ ფაილების ნაწილი."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"ტელეფონის მეხსიერება გავსებულია. ადგილის გასათავისუფლებლად წაშალეთ ფაილების ნაწილი."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"შესაძლოა ქსელი მონიტორინგის ქვეშ იმყოფება"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"უცნობი მესამე მხარის მიერ"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"ზარი ჩართულია"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"გამორთვა…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"თქვენი ტაბლეტი გაითიშება."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"თქვენი საათი გაითიშება."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"თქვენი ტელეფონი გაითიშება."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"გსურთ გამორთვა?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"უსაფრთხო რეჟიმის ჩატვირთვა"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"აპს საშუალებას აძლევს გააგზავნოს შეტყობინება SMS შეტყობინების მიღების თაობაზე. მავნე აპლიკაციებში ეს ფუნქცია შეიძლება გამოყენებული იქნას SMS შეტყობინებების მიღების იმიტაციიისათვის."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"WAP-PUSH-ით მიღებული სამაუწყებლო შეტყობინების გაგზავნა"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"აპს შეეძლება, გაგზავნოს შეტყობინება WAP PUSH შეტყობინების მიღების თაობაზე. მავნე აპებმა ეს შეიძლება გამოიყენონ MMS შეტყობინების მიღების გასაყალბებლად ან ნებისმიერი ვებგვერდის კონტენტის სახიფათო ვარიანტებით ჩუმად ჩასანაცვლებლად."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"ქსელის შეფასებების მაუწყებლობის გაგზავნა"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"აპს ნებას რთავს გადასცეს შეტყობინება, რომ ქსელები შეფასებას საჭიროებს. ჩვეულებრივ აპებს ეს არასოდეს ჭირდება."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"მიმდინარე პროცესების რაოდენობის ლიმიტი"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"აპს შეეძლება, გააკონტროლოს მიმდინარე პროცესების მაქსიმალური რაოდენობა. ჩვეულებრივ აპებში არასდროს არის საჭირო."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"უკანა ფონის აპის იძულებით დახურვა"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"მფლობელს შეეძლება მიებას დისტანციურ მონიტორის ზედა დონის ინტერფეისს. ჩვეულებრივ აპს ეს წესით არასოდეს უნდა დაჭირდეს."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ვიჯეტ სერვისთან დაკავშირება"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"აპს შეეძლება ზედა დონის ინტერფეისის ვიჯეტთან დაკავშირება. არასდროს გამოიყენება ჩვეულებრივ აპებში."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"მარშრუტის სერვისის პროვაიდერთან შეკავშირება"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"საშუალებას აძლევს მფლობელს შეკავშირდეს მარშრუტების ნებისმიერ პროვაიდერთან. ჩვეულებრივ აპებს უმეტეს შემთხვევაში არ დაჭირდება."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"მოწყობილობის ადმინთან ინტერაქცია"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"აპს შეეძლება მოწყობილობის ადმინისტრატორისთვის intent ობიექტების გაგზავნა. არასდროს გამოიყენება ჩვეულებრივ აპებში."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"TV შეყვანასთან მიბმა"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX მდგომარეობის შეცვლა"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"აპს შეეძლება, დაუკავშიროს და გამოაერთოს ტაბლეტი WiMAX ქსელებიდან."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"აპს შეეძლება, დაუკავშიროს და გამოაერთოს ტელეფონი WiMAX ქსელებიდან."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"ქსელების შეფასება"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"აპს ნებას რთავს შეაფასოს ქსელები და იქონიოს ზეგავლენა, თუ რომელი ქსელი ამჯობინოს ტაბლეტმა."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"აპს ნებას რთავს შეაფასოს ქსელები და იქონიოს ზეგავლენა, თუ რომელი ქსელი ამჯობინოს ტელეფონმა."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"Bluetooth მოწყობილობებთან დაწყვილება"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"აპს შეეძლება, ნახოს Bluetooth-ის კონფიგურაცია ტაბლეტზე, შექმნას და მიიღოს კავშირები დაწყვილებულ მოწყობილობებთან."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"აპს შეეძლება, ნახოს Bluetooth-ის კონფიგურაცია ტელეფონზე და შექმნას და მიიღოს კავშირები დაწყვილებულ მოწყობილობებთან."</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 45389f7..7eb674a 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"ធ្វើសមកាលកម្ម"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"មានការលុប <xliff:g id="CONTENT_TYPE">%s</xliff:g> ច្រើនពេក។"</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"ឧបករណ៍ផ្ទុកនៃកុំព្យូទ័របន្ទះពេញ។ លុបឯកសារមួយចំនួន។"</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"ឧបករណ៍របស់នាឡិកាពេញ។ លុបឯកសារមួយចំនួន។"</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"ឧបករណ៍ផ្ទុកទូរស័ព្ទពេញ! លុបឯកសារមួយចំនួនដើម្បីបង្កើនទំហំ។"</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"បណ្ដាញអាចត្រូវបានតាមដាន"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"ដោយភាគីទីបីដែលមិនស្គាល់"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"បើកកម្មវិធីរោទ៍"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"កំពុងបិទ..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"កុំព្យូទ័របន្ទះរបស់អ្នកនឹងបិទ។"</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"នាឡិការបស់អ្នកនឹងបិទ។"</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"ទូរស័ព្ទរបស់អ្នកនឹងបិទ។"</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"តើអ្នកចង់បិទ?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"ចាប់ផ្ដើមឡើងវិញដើម្បីចូលរបៀបសុវត្ថិភាព"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"ឲ្យកម្មវិធីប្រកាសការជូនដំណឹងការទទួលសារ SMS ។ កម្មវិធីព្យាបាទអាចប្រើវាដើម្បីបន្លំសារ SMS ចូល។"</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"ផ្ញើការប្រកាសបានទទួល WAP-PUSH"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"ឲ្យកម្មវិធីប្រកាសការជូនដំណឹងថាបានទទួលសារ WAP PUSH ។ កម្មវិធីព្យាបាទអាចប្រើវាដើម្បីក្លែងបង្កាន់ដៃសារ MMS ឬជំនួសមាតិកាទំព័របណ្ដាញណាមួយស្ងាត់ៗដោយអ្វីដែលក្លែងក្លាយ។"</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"ផ្ញើពិន្ទុការប្រកាសបណ្ដាញ"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"ឲ្យកម្មវិធីប្រកាសការជូនដំណឹងដែលបណ្ដាញតម្រូវឲ្យដាក់ពិន្ទុ។ មិនចាំបាច់សម្រាប់កម្មវិធីធម្មតា។"</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"កំណត់ចំនួនដំណើរការដែលកំពុងដំណើរការ"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"ឲ្យកម្មវិធីពិនិត្យចំនួនដំណើរការអតិបរមាដែលនឹងដំណើរការ។ មិនចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"បង្ខំឲ្យបិទកម្មវិធីក្នុងផ្ទៃខាងក្រោយ"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅចំណុចប្រទាក់កម្រិតកំពូលនៃការបង្ហាញពីចម្ងាយ។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចងសេវាកម្មធាតុក្រាហ្វិក"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មធាតុក្រាហ្វិក។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"ភ្ជាប់ទៅសេវាកម្មក្រុមហ៊ុនផ្ដល់ច្រក"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅក្រុមហ៊ុនផ្ដល់ច្រកដែលបានចុះឈ្មោះ។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"ទាក់ទងជាមួយអ្នកគ្រប់គ្រងឧបករណ៍"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"ឲ្យម្ចាស់ផ្ញើគោលបំណងទៅអ្នកគ្រប់គ្រងឧបករណ៍។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"ភ្ជាប់ទៅការបញ្ចូលទូរទស្សន៍"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"ប្ដូរស្ថានភាព WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"ឲ្យកម្មវិធីតភ្ជាប់ និងផ្ដាច់កុំព្យូទ័របន្ទះពីបណ្ដាញ WiMAX ។"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"ឲ្យកម្មវិធីភ្ជាប់ទូរស័ព្ទ និងផ្ដាច់ពីបណ្ដាញ WiMAX ។"</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"ដាក់ពិន្ទុបណ្ដាញ"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"ឲ្យកម្មវិធីចាត់ថ្នាក់បណ្ដាញ និងមានឥទ្ធិពលលើបណ្ដាញណាមួយដែលកុំព្យូទ័របន្ទះប្រើ។"</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"ឲ្យកម្មវិធីចាត់ថ្នាក់បណ្ដាញ និងមានឥទ្ធិពលលើបណ្ដាញណាមួយដែលទូរស័ព្ទគួរប្រើ។"</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"ផ្គូផ្គងជាមួយឧបករណ៍ប៊្លូធូស"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"ឲ្យកម្មវិធីមើលការកំណត់រចនាសម្ព័ន្ធប៊្លូធូសលើកុំព្យូទ័របន្ទះ ព្រមទាំងធ្វើការតភ្ជាប់ និងទទួលជាមួយឧបករណ៍បានផ្គូផ្គង។"</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"ឲ្យកម្មវិធីមើលការកំណត់រចនាសម្ព័ន្ធប៊្លូធូសក្នុងទូរស័ព្ទ ដើម្បីទទួល និងតភ្ជាប់ជាមួយឧបករណ៍បានផ្គូផ្គង។"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 5b2747f..f73469a 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"동기화"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"<xliff:g id="CONTENT_TYPE">%s</xliff:g> 삭제가 너무 많습니다."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"태블릿 저장공간이 꽉 찼습니다. 일부 파일을 삭제하여 저장 여유 공간을 늘리세요."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"시계 저장공간이 가득 찼습니다. 일부 파일을 삭제하여 저장 여유 공간을 늘리세요."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"휴대전화 저장공간이 꽉 찼습니다. 일부 파일을 삭제하여 저장공간을 늘리세요."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"네트워크가 모니터링될 수 있음"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"알 수 없는 제3자의 모니터링"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"벨소리가 켜져 있습니다."</string>
<string name="shutdown_progress" msgid="2281079257329981203">"종료 중..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"태블릿이 종료됩니다."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"시계가 종료됩니다."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"휴대전화가 종료됩니다."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"종료하시겠습니까?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"안전 모드로 다시 부팅"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"앱이 SMS 메시지를 받았다는 알림을 브로드캐스트할 수 있도록 허용합니다. 이 경우 악성 앱이 수신된 SMS 메시지를 위조할 수 있습니다."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"WAP-PUSH-수신 브로드캐스트 보내기"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"앱이 WAP PUSH 메시지를 받았다는 알림을 브로드캐스트할 수 있도록 허용합니다. 이 경우 악성 앱이 MMS 메시지를 받은 것처럼 위장하거나 웹페이지의 콘텐츠를 악성 콘텐츠로 몰래 바꿀 수 있습니다."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"네트워크 점수화 브로드캐스트 전송"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"네트워크를 점수화할 필요가 있다는 알림을 앱이 브로드캐스트할 수 있도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"실행 중인 프로세스 수 제한"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"앱이 실행할 최대 프로세스 수를 제어할 수 있도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"백그라운드 앱 강제 종료"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"권한을 가진 프로그램이 원격 디스플레이에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"위젯 서비스와 연결"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"권한을 가진 프로그램이 위젯 서비스에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"경로 제공업체 서비스 사용"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"권한을 가진 프로그램이 등록된 경로 제공업체를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"기기 관리자와 상호 작용"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"권한을 가진 프로그램이 기기 관리자에게 인텐트를 보낼 수 있도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"TV 입력 사용"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX 상태 변경"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"앱이 태블릿을 WiMAX 네트워크에 연결하거나 연결을 끊을 수 있도록 허용합니다."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"앱이 휴대전화를 WiMAX 네트워크에 연결하거나 연결을 끊을 수 있도록 허용합니다."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"네트워크 점수화"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"앱이 네트워크 순위를 정하고 태블릿에서 어떤 네트워크를 선호할지 영향을 주도록 허용합니다."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"앱이 네트워크 순위를 정하고 휴대전화에서 어떤 네트워크를 선호할지 영향을 주도록 허용합니다."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"블루투스 기기와 페어링"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"앱이 태블릿의 블루투스 설정을 확인하고 페어링된 기기에 연결하며 연결을 수락할 수 있도록 허용합니다."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"앱이 휴대전화의 블루투스 설정을 확인하고 페어링된 기기에 연결하며 연결을 수락할 수 있도록 허용합니다."</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index efe3354..80964b6 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -337,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"ອະນຸຍາດໃຫ້ແອັບຯ ກະຈາຍສັນຍານການແຈ້ງເຕືອນວ່າຂໍ້ຄວາມ SMS ໄດ້ຮັບແລ້ວ. ແອັບຯທີ່ເປັນອັນຕະລາຍອາດຈະໃຊ້ສິ່ງນີ້ໃນການປອມແປງຂໍ້ຄວາມ SMS ຂາເຂົ້າ."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"ສົ່ງການກະຈາຍ WAP-PUSH ທີ່ໄດ້ຮັບ"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"ອະນຸຍາດໃຫ້ແອັບຯສົ່ງການແຈ້ງເຕືອນໃນເວລາທີ່ໄດ້ຮັບຂໍ້ມຄວາມ WAP PUSH. ແອັບຯທີ່ເປັນອັນຕະລາຍ ອາດໃຊ້ການກະທຳນີ້ເພື່ອປອມການໄດ້ຮັບຂໍ້ຄວາມ MMS ຫຼືລັກປ່ຽນເນື້ອຫາຂອງໜ້າເວັບຕ່າງໆ ດ້ວຍສິ່ງອັນຕະລາຍທັງຫຼາຍຢ່າງງຽບໆ."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"ສົ່ງການກະຈາຍເຄືອຂ່າຍຄະແນນ"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"ອະນຸຍາດໃຫ້ແອັບຯກະຈາຍການແຈ້ງເຕືອນທີ່ເຄືອຂ່າຍຈຳເປັນໃຊ້ນັບຄະແນນ. ບໍ່ເຄີຍໄດ້ໃຊ້ໃນແອັບຯທົ່ວໄປ."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"ຈຳກັດຈຳນວນຂອງໂປຣເຊສທີ່ເຮັດວຽກຢູ່"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"ອະນຸຍາດໃຫ້ແອັບຯຄວບຄຸມຈຳນວນສູງສຸດ ຂອງໂປຣເຊສທີ່ຈະເຮັດວຽກ. ບໍ່ຄວນຖືກໃຊ້ກັບແອັບພລິເຄຊັນທົ່ວໄປ."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"ບັງຄັບໃຫ້ແອັບຯທີ່ເຮັດວຽກຢູ່ພື້ນຫຼັງປິດໂຕລົງ"</string>
@@ -392,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"ອະນຸຍາດໃຫ້ຜູ່ຖືຜູກກັບສ່ວນຕິດຕໍ່ລະດັບສູງສຸດ ຂອງການສະແດງຜົນທາງໄກ. ບໍ່ຈຳເປັນສຳລັບແອັບຯທົ່ວໄປ."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ເຊື່ອມໂຍງໄປຫາບໍລິການວິດເຈັດ"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ອະນຸຍາດໃຫ້ຜູ່ຖືຜູກກັບອິນເຕີເຟດລະດັບສູງສຸດ ຂອງບໍລິການວິເຈັດ. ບໍ່ຈຳເປັນສຳລັບແອັບຯທົ່ວໄປ."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"ເຊື່ອມໂຍງກັບການບໍລິການຂອງຜູ່ໃຫ້ບໍລິການເສັ້ນທາງ"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"ອະນຸຍາດໃຫ້ເຈົ້າຂອງສາມາດເຊື່ອມໂຍງກັບທຸກໆຜູ່ໃຫ້ບໍລິການເສັ້ນທາງທີ່ລົງທະບຽນ. ບໍ່ຄວນຈະໄດ້ໃຊ້ໃນແອັບຯທົ່ວໄປ."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"ຕິດຕໍ່ກັບຜູ່ເບິ່ງແຍງອຸປະກອນ"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"ອະນຸຍາດໃຫ້ເຈົ້າຂອງສົ່ງເຈດຕະນາຫາຜູ່ເບິ່ງແຍງລະບົບອຸປະກອນ. ແອັບຯທົ່ວໄປບໍ່ຄວນຈຳເປັນຕ້ອງໃຊ້."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"ຜູກກັບການປ້ອນຂໍ້ມູນເຂົ້າໂທລະທັດ"</string>
@@ -642,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"ປ່ຽນສະຖານະ WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"ອະນຸຍາດໃຫ້ແອັບຯເຊື່ອມຕໍ່ ແລະຕັດການເຊື່ອມຕໍ່ແທັບເລັດຈາກເຄືອຂ່າຍ WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"ອະນຸຍາດໃຫ້ແອັບຯເຊື່ອມຕໍ່ ແລະຕັດການເຊື່ອມຕໍ່ຂອງໂທລະສັບຈາກເຄືອຂ່າຍ WiMax ໄດ້."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"ເຄືອຂ່າຍຄະແນນ"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"ອະນຸຍາດໃຫ້ແອັບຯຈັດລຳດັບເຄືອຂ່າຍ ແລະ ຊ່ວຍຕັດສິນໃຈວ່າເຄືອຂ່າຍໃດທີ່ແທັບເລັດຄວນນຳໃຊ້."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"ອະນຸຍາດໃຫ້ແອັບຯຈັດລຳດັບເຄືອຂ່າຍ ແລະ ຊ່ວຍຕັດສິນໃຈວ່າເຄືອຂ່າຍໃດທີ່ໂທລະສັບຄວນນຳໃຊ້."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"ຈັບຄູ່ກັບອຸປະກອນ Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"ອະນຸຍາດໃຫ້ແອັບຯເບິ່ງການຕັ້ງຄ່າຂອງ Bluetooth ໃນແທັບເລັດ ຕະຫຼອດຈົນເຊື່ອມຕໍ່ ແລະຍອມຮັບການເຊື່ອມຕໍ່ກັບອຸປະກອນທີ່ຈັບຄູ່ກັນແລ້ວ."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"ອະນຸຍາດໃຫ້ແອັບຯເບິ່ງການຕັ້ງຄ່າຂອງ Bluetooth ໃນໂທລະສັບ, ຮວມທັງໃຫ້ສ້າງ ແລະຮັບການເຊື່ອມຕໍ່ຈາກອຸປະກອນທີ່ຈັບຄູ່ກັນ."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 2828dc2..f10135b 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sinchronizuoti"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Per daug <xliff:g id="CONTENT_TYPE">%s</xliff:g> trynimo."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Planšetinio kompiuterio atmintis pilna. Kad atlaisvintumėte vietos, ištrinkite kelis failus."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Laikrodžio saugykla pilna. Ištrinkite kelis failus, kad atlaisvintumėte vietos."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Telefono atmintis pilna. Ištrinkite kai kuriuos failus, kad atlaisvintumėte vietos."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Tinklas gali būti stebimas"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Nežinoma trečioji šalis"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Skambutis įjungtas"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Išsijungia..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Planšetinio kompiuterio veikimas bus sustabdytas."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Laikrodis išsijungs."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Telefonas bus išjungtas."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Ar norite išjungti?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Iš naujo įkelti operacinę sistemą saugos režimu"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Leidžiama programai pateikti pranešimą, kad buvo gautas SMS pranešimas. Kenkėjiškos programos gali tai naudoti, kad klastotų gaunamuosius SMS pranešimus."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"siųsti „WAP-PUSH-received“ perdavimą"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Leidžiama programai pateikti pranešimą, kai gaunamas WAP PUSH pranešimas. Kenkėjiškos programos gali tai naudoti, kad klastotų MMS pranešimo gavimą ar kad nepastebimai pakeistų bet kurio tinklalapio turinį kenkėjiškais variantais."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"siųsti tinklų transliavimo įvertinimą"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Programai leidžiama transliuoti pranešimą, kad reikia įvertinti tinklus. Niekada nereikia įprastoms programoms."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"riboti vykdomų procesų skaičių"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Leidžiama programai valdyti didžiausią vykdomų procesų skaičių. Nereikalinga įprastoms programoms."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"priverstinai uždaryti fonines programas"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Leidžiama savininkui susisaistyti su aukščiausio lygio valdiklio paslaugos sąsaja. Įprastoms programoms to neturėtų prireikti."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"susisaistyti su maršruto parinkimo paslauga"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Savininkui leidžiama susisaistyti su bet kokiomis registruotomis maršrutų parinkimo paslaugomis. To niekada neturėtų prireikti naudojant įprastas programas."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"sąveikauti su įrenginio administratoriumi"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Leidžiama savininkui siųsti tikslus įrenginio administratoriui. Įprastoms programoms to neturėtų prireikti."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"susisaistyti su TV įvestimi"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Keisti „WiMAX“ būseną"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Leidžia programai prijungti planšetinį kompiuterį prie „WiMAX“ ryšio tinklų ir nuo jų atjungti."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Leidžia programai prijungti telefoną prie „WiMAX“ ryšio tinklų ir nuo jų atjungti."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"įvertinti tinklus"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Programai leidžiama įvertinti tinklus ir nustatyti, kuriems tinklams planšetiniame kompiuteryje turėtų būti taikoma pirmenybė."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Programai leidžiama įvertinti tinklus ir nustatyti, kuriems tinklams telefone turėtų būti taikoma pirmenybė."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"susieti su „Bluetooth“ įrenginiais"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Leidžiama programai peržiūrėti „Bluetooth“ konfigūraciją planšetiniame kompiuteryje ir užmegzti bei priimti ryšius iš susietų įrenginių."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Leidžiama programai peržiūrėti „Bluetooth“ konfigūraciją telefone ir užmegzti bei priimti ryšius iš susietų įrenginių."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index c51cf90..f53f0ca 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sinhronizācija"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Pārāk daudz <xliff:g id="CONTENT_TYPE">%s</xliff:g> dzēsto vienumu."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Planšetdatora atmiņa ir pilna. Dzēsiet dažus failus, lai atbrīvotu vietu."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Pulksteņa atmiņa ir pilna. Dzēsiet dažus failus, lai atbrīvotu vietu."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Tālruņa atmiņa ir pilna! Dzēsiet dažus failus, lai atbrīvotu vietu."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Iespējams, tīklā veiktās darbības tiek pārraudzītas."</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Nezināma trešā puse"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Zvanītājs ieslēgts"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Notiek izslēgšana..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Planšetdators tiks beidzēts."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Pulkstenis tiks izslēgts."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Tālrunis tiks izslēgts."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Vai vēlaties izslēgt?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Atsāknēšana drošajā režīmā"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Ļauj lietotnei pārraidīt paziņojumu par saņemtu īsziņu. Ļaunprātīgas lietotnes to var izmantot, lai viltotu ienākošas īsziņas."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"sūtīt WAP-PUSH-saņemto apraidi"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Ļauj lietotnei pārraidīt paziņojumu par to, ka ir saņemts WAP PUSH ziņojums. Ļaunprātīgas lietotnes to var izmantot, lai viltotu multiziņas saņemšanu vai jebkuras tīmekļa lapas saturu nemanāmi nomainītu ar ļaunprātīgiem variantiem."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"apraidīt ziņojumu par tīklu vērtēšanu"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Ļauj lietotnei apraidīt paziņojumu, ka tīkli ir jānovērtē. Parastām lietotnēm tas nekad nav nepieciešams."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"ierobežot aktīvo procesu skaitu"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Ļauj lietotnei kontrolēt izpildāmo procesu maksimālo skaitu. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"veikt fonā darbojošos lietotņu piespiedu aizvēršanu"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Ļauj īpašniekam izveidot saiti ar logrīka pakalpojuma augšējā līmeņa saskarni. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"Saistīšana ar maršruta nodrošinātāja pakalpojumu"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Ļauj īpašniekam saistīt jebkādus reģistrētus maršrutēšanas nodrošinātājus. Parastām lietotnēm šī atļauja nekad nav nepieciešama."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"mijiedarboties ar ierīces administratoru"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Ļauj īpašniekam nosūtīt informāciju par nodomiem ierīces administratoram. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"Izveidot saiti ar TV ieeju"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX statusa mainīšana"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Ļauj lietotnei izveidot un pārtraukt planšetdatora savienojumu ar WiMAX tīkliem."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Ļauj lietotnei izveidot un pārtraukt tālruņa savienojumu ar WiMAX tīkliem."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"vērtēt tīklus"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Ļauj lietotnei ranžēt tīklus un ietekmēt to, kuriem tīkliem planšetdators dos priekšroku."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Ļauj lietotnei ranžēt tīklus un ietekmēt to, kuriem tīkliem tālrunis dos priekšroku."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"savienot pārī ar Bluetooth ierīcēm"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Ļauj lietotnei skatīt Bluetooth konfigurāciju planšetdatorā, kā arī veidot un pieņemt savienojumus ar pārī savienotām ierīcēm."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Ļauj lietotnei skatīt Bluetooth konfigurāciju tālrunī, kā arī veidot un pieņemt savienojumus ar pārī savienotām ierīcēm."</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index c8c8fac..f5f8fd3 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Синк"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Хэт олон <xliff:g id="CONTENT_TYPE">%s</xliff:g> устгах."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Таблетийн сан дүүрсэн. Зай чөлөөлөх бол зарим файлыг устгана уу."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Цагны сан дүүрсэн. Зай чөлөөлөх бол зарим файлыг устгана уу."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Утасны сан дүүрсэн. Зай чөлөөлөх бол зарим файлыг устгана уу."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Сүлжээ хянагдаж байж болзошгүй"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Тодорхойгүй гуравдагч талаас"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Хонх ассан"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Унтрааж байна…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Таны таблет унтрах болно."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Таны цаг унтрах болно."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Таны утас унтрах болно."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Та унтраах уу?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Аюулгүй горимоор дахин асаах"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Апп нь SMS мессеж хүлээн авсан талаарх мэдэгдлийг өргөн дамжуулах боломжтой. Хортой апп энийг ашиглан ирсэн SMS мессежийг хуурамчаар хийх боломжтой."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"WAP-PUSH-хүлээн авав өргөн дамжууллыг илгээх"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Аппликешн нь WAP PUSH мессеж хүлээж авсан мэдэгдлийг өргөн дамжуулах боломжтой. Хортой апп нь энийг ашиглан MMS мессеж хүлээн авсан гэж хуурамчаар мэдэгдэх эсвэл хортой хувьсагч агуулсан веб хуудасны контентыг чимээгүй орлуулах боломжтой."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"бүртгэгдсэн сүлжээнүүд рүү түгээх"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Апп-д сүлжээнүүдэд бүртгэгдсэн байх шаардлагатай мэдэгдлийг түгээх боломжийг олгоно. Энгийн апп-д хэзээ ч шаардагдахгүй."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"ажиллаж байгаа процессийн тоог хязгаарлах"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Апп нь нэг зэрэг ажиллах процессийн тооны дээд утгыг удирдах боломжтой. Энгийн апп-д хэзээ ч ашиглагдахгүй."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"арын апп-г хүчээр хаах"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Эзэмшигчид алсын дэлгэц дэх дээд давхаргын интерфэйстэй холбогдох боломж олгоно. Энгийн апп-д шаардагдахгүй."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"виджет үйлчилгээтэй холбох"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Эзэмшигч нь виджет үйлчилгээний дээд-төвшиний интерфейстэй холбох боломжтой. Энгийн апп-д шаардлагагүй."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"маршрут нийлүүлэгчийн үйлчилгээтэй холбогдох"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Эзэмшигчид бүртгэгдсэн маршрут нийлүүлэгчтэй холбогдох боломж олгоно. Энгийн апп-уудад хэзээ ч шаардагдахгүй."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"төхөөрөмжийн админтай харилцан үйлчлэх"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Эзэмшигч нь төхөөрөмжийн админруу интент илгээх боломжтой. Энгийн апп-д шаардлагагүй."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"ТВ оролт холбох"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX статусыг өөрчлөх"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Апп нь WiMAX сүлжээнд таблетыг холбох болон салгах боломжтой."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Апп нь WiMAX сүлжээнд утсыг холбох болон салгах боломжтой."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"бүртгэгдсэн сүлжээ"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Апп-д сүлжээнүүдийг эрэмбэлж, аль сүлжээнд таблетыг холбоход нөлөөлөх боломж олгоно."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Апп-д сүлжээнүүдийг эрэмбэлж, аль сүлжээнд утсыг холбоход нөлөөлөх боломж олгоно."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"Блютүүт төхөөрөмжтэй хос үүсгэх"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Апп нь таблет дээрх блютүүт тохиргоог харах боломжтой ба хос болох төхөөрөмжтэй холболтыг зөвшөөрөх болон хийх боломжтой."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Апп нь утсан дээрх Блютүүт тохиргоог харах боломжтой ба хос болох төхөөрөмжтэй холболтыг зөвшөөрөх болон хийх боломжтой."</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 0e15840..aee4cdf 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Penyegerakan"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Terlalu banyak pemadaman <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Storan tablet penuh. Padamkan beberapa fail untuk mengosongkan ruang."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Storan tontonan penuh. Padamkan beberapa fail untuk mengosongkan ruang."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Storan telefon penuh. Padamkan beberapa fail untuk mengosongkan ruang."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Rangkaian mungkin dipantau"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Oleh pihak ketiga yang tidak diketahui"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Pendering dihidupkan"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Mematikan..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Tablet anda akan dimatikan."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Tontonan anda akan dimatikan."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Telefon anda akan dimatikan."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Adakah anda mahu menutup?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"But semula ke mod selamat"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Membenarkan apl untuk menyiarkan pemberitahuan bahawa mesej SMS telah diterima. Apl hasad boleh menggunakannya untuk memalsukan mesej SMS masuk."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"hantar siaran WAP-TOLAK-diterima"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Membenarkan apl untuk menyiarkan pemberitahuan bahawa mesej WAP PUSH telah diterima. Apl hasad boleh menggunakannya untuk memalsukan penerimaan mesej MMS atau secara diam-diam menggantikan kandungan mana-mana laman web dengan varian hasad."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"hantar siaran markah rangkaian"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Membenarkan apl menyiarkan pemberitahuan bahawa rangkaian perlu diberi markah. Tidak sekali-kali diperlukan untuk apl biasa."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"hadkan bilangan proses yang dijalankan"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Membenarkan apl untuk mengawal bilangan maksimum proses yang akan berlangsung. Tidak sekali-kali diperlukan untuk apl biasa."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"memaksa apl latar belakang untuk menutup"</string>
@@ -394,6 +390,10 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi perkhidmatan widget. Tidak sekali-kali diperlukan untuk apl biasa."</string>
+ <!-- no translation found for permlab_bindRouteProvider (4869394607915096847) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRouteProvider (4703804520859960329) -->
+ <skip />
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"berinteraksi dengan pentadbir peranti"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Membenarkan pemegang menghantar tujuan kepada pentadbir peranti. Tidak sekali-kali diperlukan untuk apl biasa."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"ikat kepada input TV"</string>
@@ -644,12 +644,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Tukar keadaan WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Membenarkan apl untuk menyambungkan tablet ke dan menyahsambungkan tablet dari rangkaian WiMaX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Membenarkan apl untuk menyambungkan telefon ke dan menyahsambung telefon dari rangkaian WiMaX."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"beri markah kepada rangkaian"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Membenarkan apl menilai rangkaian dan mempengaruhi rangkaian yang harus dipilih oleh tablet."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Membenarkan apl menilai rangkaian dan mempengaruhi rangkaian yang harus dipilih oleh telefon."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"berpasangan dengan peranti Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Membenarkan apl melihat konfigurasi Bluetooth pada tablet dan untuk membuat serta menerima sambungan dengan peranti yang dipasangkan."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Membenarkan apl melihat konfigurasi Bluetooth pada telefon dan membuat serta menerima sambungan dengan peranti yang dipasangkan."</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 781f7d1..029ceb5 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synkronisering"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"For mange slettinger av <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Nettbrettlageret er fullt. Slett noen filer for å frigjøre lagringsplass."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Klokkens lagringsplass er full. Slett filer for å frigjøre plass."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Telefonlageret er fullt. Slett noen filer for å frigjøre lagringsplass."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Nettverket blir muligens overvåket"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Av en ukjent tredjepart"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Ringelyd på"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Avslutter…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Nettbrettet slås av."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Klokken slås av."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Telefonen kommer til å slås av."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Vil du slå av?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Start på nytt i sikker modus"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Lar appen kringkaste et varsel om at en SMS-melding er mottatt. Ondsinnede apper kan bruke dette til å forfalske innkommende SMS-meldinger."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"kringkaste melding om mottatt WAP-PUSH"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Lar appen kringkaste et varsel om at en WAP-PUSH-melding er mottatt. Ondsinnede apper kan bruke dette til å forfalske MMS-meldingskvitteringer, eller ubemerket erstatte innholdet av alle slags nettsider med ondsinnede varianter."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"sende kringkasting om nettverksvurdering"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Gir appen tillatelse til å kringkaste et varsel om at nettverk må vurderes. Denne tillatelsen bør aldri være nødvendig for vanlige apper."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"begrense antallet kjørende prosesser"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Lar appen kontrollere det maksimale antallet prosesser som kjører. Aldri nødvendig for vanlige apper."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"tvinge bakgrunnsapper til å lukkes"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Lar innehaveren binde seg til det øverste nivået av grensesnittet for en modultjeneste. Skal aldri være nødvendig for vanlige apper."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"binde seg til en ruteleverandørtjeneste"</string>
+ <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>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Endre WiMAX-status"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Lar appen koble nettbrettet til og fra WiMAX-nettverk."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Lar appen koble telefonen til og fra WiMAX-nettverk."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"vurdere nettverk"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Gir appen tillatelse til å rangere nettverk, og påvirke hvilket nettverk nettbrettet skal foretrekke."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Gir appen tillatelse til å rangere nettverk, og påvirke hvilket nettverk telefonen skal foretrekke."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"koble til Bluetooth-enheter"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Lar appen se Bluetooth-konfigurasjonen på nettbrettet, samt opprette og godta tilkoblinger med sammenkoblede enheter."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Lar appen se Bluetooth-konfigurasjonen på telefonen, samt opprette og godta tilkoblinger med sammenkoblede enheter."</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index c45f65d..5010748 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synchroniseren"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Te veel verwijderen voor <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Tabletgeheugen is vol. Verwijder enkele bestanden om ruimte vrij te maken."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Horlogegeheugen is vol. Verwijder enkele bestanden om ruimte vrij te maken."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Telefoongeheugen is vol. Verwijder enkele bestanden om ruimte vrij te maken."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Netwerk kan worden gecontroleerd"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Door een onbekende derde partij"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Belsoftware aan"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Uitschakelen..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Uw tablet wordt uitgeschakeld."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Uw horloge wordt uitgeschakeld."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Uw telefoon wordt uitgeschakeld."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Wilt u afsluiten?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Opnieuw opstarten in veilige modus"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Hiermee kan de app een melding verzenden dat een sms\'je is ontvangen. Schadelijke apps kunnen dit gebruiken om inkomende sms\'jes te vervalsen."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"melding over ontvangen WAP-PUSH-bericht verzenden"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Hiermee kan de app een melding verzenden dat een WAP PUSH-bericht is ontvangen. Schadelijke apps kunnen dit gebruiken om de ontvangst van MMS-berichten te vervalsen of de inhoud van een webpagina ongemerkt te vervangen door schadelijke varianten."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"melding verzenden dat netwerken een score moeten krijgen"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Hiermee kan de app een melding uitzenden dat netwerken een score moeten krijgen. Nooit vereist voor normale apps."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"aantal actieve processen beperken"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Hiermee kan de app het maximale aantal processen beheren dat kan worden uitgevoerd. Nooit nodig voor normale apps."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"achtergrondapps gedwongen stoppen"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Hiermee wordt de houder toegestaan verbinding te maken met de hoofdinterface van een widgetservice. Nooit vereist voor normale apps."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"binden aan de service van een routeprovider"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Hiermee kan de houder binden aan geregistreerde routeproviders. Nooit gebruikt voor normale apps."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interactie met apparaatbeheer"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Hiermee kan de houder intenties verzenden naar een apparaatbeheerder. Nooit vereist voor normale apps."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"binden aan een tv-ingang"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX-status wijzigen"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Hiermee kan de app de tablet verbinden met WiMAX-netwerken en de verbinding daarmee verbreken."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Hiermee kan de app de telefoon verbinden met WiMAX-netwerken en de verbinding daarmee verbreken."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"score toekennen aan netwerken"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Hiermee kan de app netwerken rangschikken en beïnvloeden aan welke netwerken de tablet de voorkeur moet geven."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Hiermee kan de app netwerken rangschikken en beïnvloeden aan welke netwerken de telefoon de voorkeur moet geven."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"koppelen met Bluetooth-apparaten"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Hiermee kan de app de Bluetooth-configuratie van de tablet bekijken en verbindingen met gekoppelde apparaten maken en accepteren."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Hiermee kan de app de Bluetooth-configuratie van de telefoon bekijken en verbindingen met gekoppelde apparaten maken en accepteren."</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 3ebe3f1..c8d7298 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synchronizuj"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Zbyt wiele usuwanych <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Pamięć tabletu jest pełna. Usuń niektóre pliki, aby zwolnić miejsce."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Pamięć w zegarku jest pełna. Usuń niektóre pliki, by zwolnić miejsce."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Pamięć telefonu jest pełna. Usuń niektóre pliki, aby zwolnić miejsce."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Sieć może być monitorowana"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Przez nieznany podmiot zewnętrzny"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Dzwonek włączony"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Wyłączanie..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Tablet zostanie wyłączony."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Zegarek zostanie wyłączony."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Telefon zostanie wyłączony"</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Czy chcesz wyłączyć?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Uruchom w trybie awaryjnym"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Pozwala aplikacji na wysyłanie powiadomienia, że została odebrana wiadomość SMS. Złośliwe aplikacje mogą to wykorzystać do fałszowania przychodzących wiadomości SMS."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"wysyłanie transmisji informującej o otrzymaniu wiadomości WAP-PUSH"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Pozwala aplikacji na nadanie powiadomienia o odebraniu wiadomości WAP PUSH. Złośliwe aplikacje mogą to wykorzystać do fałszowania potwierdzenia odbioru wiadomości MMS lub do niezauważalnego podmieniania zawartości dowolnej strony internetowej jej szkodliwymi wariantami."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"rozsyłanie informacji o ocenie sieci"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Pozwala aplikacji na wysłanie powiadomienia, że sieci wymagają oceny. Nieprzeznaczone dla zwykłych aplikacji."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"ograniczanie liczby uruchomionych procesów"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Pozwala aplikacji na kontrolowanie maksymalnej liczby uruchamianych procesów. Nigdy niewykorzystywane przez normalne aplikacje."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"wymuszanie zamknięcia aplikacji w tle"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Zezwala na tworzenie powiązania z interfejsem najwyższego poziomu usługi widżetów. Nie powinno być nigdy potrzebne w przypadku zwykłych aplikacji."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"powiązanie z usługą dostawcy tras"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Umożliwia właścicielowi powiązanie z dowolnymi zarejestrowanymi dostawcami tras. Nie powinno być nigdy potrzebne w normalnych aplikacjach."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interakcja z administratorem urządzenia"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Zezwala na wysyłanie intencji do administratora urządzenia. Nie powinno być nigdy potrzebne w przypadku zwykłych aplikacji."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"powiązanie z wejściem TV"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"zmienianie stanu WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Pozwala aplikacji na nawiązywanie i kończenie połączeń z sieciami WiMAX w tablecie."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Pozwala aplikacji na nawiązywanie i kończenie połączeń z sieciami WiMAX w telefonie."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"ocenianie sieci"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Pozwala aplikacji na ocenę sieci i wybieranie sieci preferowanych przez tablet."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Pozwala aplikacji na ocenę sieci i wybieranie sieci preferowanych przez telefon."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"parowanie z urządzeniami Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Pozwala aplikacji na dostęp do konfiguracji Bluetooth na tablecie oraz na nawiązywanie i akceptowanie połączeń ze sparowanych urządzeń."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Pozwala aplikacji na dostęp do konfiguracji Bluetooth na telefonie oraz na nawiązywanie i akceptowanie połączeń ze sparowanych urządzeń."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 7267cf2..718c8d9 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sincronização"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Demasiadas eliminações de <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"O armazenamento do tablet está cheio. Elimine alguns ficheiros para libertar espaço."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"O armazenamento de visualizações está cheio. Elimine alguns ficheiros para libertar espaço."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"O armazenamento do telemóvel está cheio. Elimine alguns ficheiros para libertar espaço."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"A rede pode ser monitorizada"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Por um terceiro desconhecido"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Campainha ativada"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"A encerrar..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"O seu tablet irá encerrar."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"As suas visualizações vão ser encerradas."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"O seu telefone será encerrado."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Pretende encerrar?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Reiniciar no modo de segurança"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Permite que a aplicação difunda uma notificação de que foi recebida uma mensagem SMS. As aplicações maliciosas podem utilizar este recurso para forjar mensagens SMS recebidas."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"enviar difusão recebida através de PUSH WAP"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Permite que a aplicação difunda uma notificação de que foi recebida uma mensagens PUSH WAP. As aplicações maliciosas podem utilizar isto para forjar um recibo de mensagem MMS ou substituir, de forma silenciosa, o conteúdo de qualquer página Web por variantes maliciosas."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"enviar transmissão de pontuação de redes"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Permite à aplicação transmitir uma notificação de que é necessário pontuar as redes. Nunca é necessário para aplicações normais."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"número limite de processos em execução"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Permite a uma aplicação controlar o número máximo de processos que será executado. Nunca é necessário para aplicações normais."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"forçar as aplicações em segundo plano a fechar"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Permite que o titular vincule a interface de nível superior de um serviço de widget. Nunca deverá ser necessário para aplicações normais."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"vincular a serviço de fornecedor de trajeto"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Permite ao titular vincular a quaisquer fornecedores de trajeto registado. Nunca deverá ser necessário para aplicações normais."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interagir com um administrador do dispositivo"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Permite ao titular enviar intenções para um administrador do aparelho. Nunca deverá ser necessário para aplicações normais."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"vincular a uma entrada de TV"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Alterar estado do WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permite que a aplicação ligue e desligue o tablet de redes WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permite que a aplicação ligue e desligue o telemóvel de redes WiMAX."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"pontuar redes"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Permite à aplicação classificar redes e influenciar as redes que o tablet deve preferir."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Permite à aplicação classificar redes e influenciar as redes que o telemóvel deve preferir."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"sincronizar com dispositivos Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Permite que a aplicação visualize a configuração do Bluetooth no tablet e que estabeleça e aceite ligações com dispositivos emparelhados."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Permite que a aplicação visualize a configuração do Bluetooth no telemóvel e que estabeleça e aceite ligações com dispositivos emparelhados."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 690d047..29a294a7 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sincronizar"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Muitas exclusões de <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"O armazenamento do tablet está cheio. Exclua alguns arquivos para liberar espaço."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Armazenamento do relógio cheio. Exclua alguns arquivos para liberar espaço."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"O armazenamento do telefone está cheio. Exclua alguns arquivos para liberar espaço."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"A rede pode ser monitorada"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Por terceiros desconhecidos"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Campainha ligada"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Encerrando…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Seu tablet será desligado."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Seu relógio será desligado."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"O seu telefone será desligado."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Deseja desligar?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Reiniciar no modo de segurança"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Permite que o aplicativo transmita uma notificação quando uma mensagem SMS foi recebida. Aplicativos maliciosos podem usar esse recurso para forjar mensagens SMS recebidas."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"enviar transmissão WAP-PUSH recebida"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Permite que o aplicativo transmita uma notificação quando uma mensagem WAP PUSH for recebida. Aplicativos maliciosos podem usar esse recurso para forjar o recebimento de mensagens MMS ou substituir o conteúdo de qualquer página da web com variantes maliciosas."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"enviar transmissão de avaliação de redes"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Permite que o aplicativo transmita uma notificação informando que as redes devem ser avaliadas. Não deve ser necessário para aplicativos comuns."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"limitar número de processos em execução"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Permite que o aplicativo controle o máximo de processos que serão executados. Nunca é necessário para aplicativos normais."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"forçar encerramento de aplicativos em segundo plano"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Permite que o proprietário utilize a interface de nível superior de um serviço de widget. Nunca deve ser necessário para aplicativos normais."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"usar um serviço provedor de rotas"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Permite que o proprietário use qualquer provedor de rotas registrado. Não deve ser necessário para aplicativos comuns."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interagir com o administrador de um dispositivo"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Permite que o proprietário envie tentativas ao administrador de um aparelho. Nunca deve ser necessário para aplicativos normais."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"associar a uma entrada de TV"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Alterar estado do WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permite que o aplicativo conecte e desconecte o tablet de redes WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permite que o aplicativo conecte e desconecte o telefone de redes WiMAX."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"avaliar redes"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Permite que o aplicativo classifique as redes e influencie a escolha de redes pelo tablet."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Permite que o aplicativo classifique as redes e influencie a escolha de redes pelo smartphone."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"parear com dispositivos Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Permite que o aplicativo acesse a configuração do Bluetooth no tablet, além de fazer e aceitar conexões com dispositivos pareados."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Permite que o aplicativo acesse a configuração do Bluetooth no telefone, além de fazer e aceitar conexões com dispositivos pareados."</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index a9e0d4e..e362590 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -614,6 +614,10 @@
<skip />
<!-- no translation found for permdesc_bindRemoteViews (4717987810137692572) -->
<skip />
+ <!-- no translation found for permlab_bindRouteProvider (4869394607915096847) -->
+ <skip />
+ <!-- no translation found for permdesc_bindRouteProvider (4703804520859960329) -->
+ <skip />
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interacziun cun in administratur dad apparats"</string>
<!-- no translation found for permdesc_bindDeviceAdmin (569715419543907930) -->
<skip />
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index ebcf7d6..c173eef 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sincronizare"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Prea multe ştergeri <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Stocarea pe tabletă este plină. Ștergeţi câteva fişiere pentru a elibera spaţiu."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Spațiul de stocare de pe ceas este plin! Ștergeți câteva fișiere pentru a elibera spațiu."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Stocarea pe telefon este plină. Ștergeţi câteva fişiere pentru a elibera spaţiu."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Rețeaua poate fi monitorizată"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"De o terță parte necunoscută"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Sonerie activată"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Se închide..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Computerul dvs. tablet PC se va închide."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Ceasul dvs. se va închide."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Telefonul dvs. se va închide."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Doriţi să închideţi?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Reporniţi în modul sigur"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Permite aplicaţiei să difuzeze o notificare de primire a unui mesaj SMS. Aplicaţiile rău intenţionate pot să utilizeze această permisiune pentru a deturna primirea mesajelor SMS."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"trimitere mesaj difuzat primit prin WAP-PUSH"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Permite aplicaţiei să difuzeze o notificare de primire a unui mesaj WAP PUSH. Aplicaţiile rău intenţionate pot să utilizeze această permisiune pentru a deturna primirea mesajelor MMS sau pentru a înlocui fără a vă înştiinţa conţinutul oricărei pagini web cu variante rău intenţionate."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"trimiteți transmisia cu rețelele punctate"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Permite aplicației să transmită o notificare de care rețelele au nevoie pentru a fi punctate. Nu este necesară pentru aplicațiile obișnuite."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"limitare număr de procese în derulare"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Permite aplicaţiei să controleze numărul maxim de procese care vor rula. Nu este niciodată necesară pentru aplicaţiile obişnuite."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"forţează închiderea aplicaţiilor de fundal"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Permite proprietarului să se conecteze la interfaţa de nivel superior a unui serviciu widget. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"se conectează la un serviciu de furnizare a traseelor"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Permite titularului să se conecteze la furnizorii de trasee înregistrați. Nu este necesară pentru aplicațiile obișnuite."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interacţionare cu administratorul unui dispozitiv"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Permite proprietarului să trimită intenţii către un administrator al dispozitivului. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"se conectează la o intrare TV"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Schimbaţi starea WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Permite aplicaţiei să conecteze şi să deconecteze tableta la şi de la reţelele WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Permite aplicaţiei să conecteze şi să deconecteze telefonul la şi de la reţelele WiMAX."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"rețele punctate"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Permite aplicației să clasifice rețelele și să stabilească ce rețele preferă tableta."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Permite aplicației să clasifice rețelele și să stabilească ce rețele preferă telefonul."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"conectează dispozitive Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Permite aplicaţiei să vadă configuraţia tabletei Bluetooth, să efectueze şi să accepte conexiuni cu dispozitive împerecheate."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Permite aplicaţiei să vadă configuraţia telefonului Bluetooth, să efectueze şi să accepte conexiuni cu dispozitive împerecheate."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 25fe679..a3d5d92 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Синхр."</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Слишком много удалений <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Память планшетного ПК заполнена. Удалите какие-нибудь файлы, чтобы освободить место."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Память устройства заполнена. Удалите файлы, чтобы освободить место."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Память телефона заполнена. Удалите какие-нибудь файлы, чтобы освободить место."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Сеть может отслеживаться"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"администратором"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Звонок включен"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Выключение..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Планшетный ПК будет отключен."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Устройство будет отключено."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Телефон будет выключен."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Завершить работу?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Переход в безопасный режим"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Приложение сможет уведомлять о получении SMS. Вредоносные программы смогут таким образом подделывать входящие SMS."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"Отправка уведомлений о доставке SMS с ссылкой на WAP-страницу"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Приложение сможет уведомлять о получении сообщений WAP PUSH. Вредоносные программы смогут таким образом фальсифицировать получение MMS или незаметно подменять содержание любой страницы вредоносными данными."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"Отправка уведомлений о рейтинге сетей"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Приложение сможет отправлять уведомления о том, что сети необходимо присвоить рейтинг. Это разрешение обычно используется только специальными приложениями."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"Ограничение количества запущенных процессов"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Приложение сможет управлять максимальным количеством процессов, которые могут быть запущены. Это разрешение не используется обычными приложениями."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"Закрытие фоновых приложений"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Приложение сможет подключаться к базовому интерфейсу удаленного дисплея. Это разрешение обычно используется только специальными приложениями."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"Подключение к службе виджетов"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Приложение сможет подключаться к базовому интерфейсу службы виджетов. Это разрешение не используется обычными приложениями."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"Подключение к серверам поставщиков маршрутов"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Приложение сможет подключаться к серверам зарегистрированных поставщиков маршрутов. Это разрешение не используется обычными приложениями."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"Взаимодействие с администратором устройства"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Приложение сможет отправлять объекты intent администратору устройства. Это разрешение не используется обычными приложениями."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"Подключение к ТВ-входу"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Изменение статуса WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Приложение сможет подключать устройство к сетям WiMAX и отключать его от них."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Приложение сможет подключать устройство к сетям WiMAX и отключать его от них."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"Определение рейтинга сетей"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Приложение сможет присваивать сетям рейтинг и решать, к каким из них устройство должно подключаться в первую очередь."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Приложение сможет присваивать сетям рейтинг и решать, к каким из них устройство должно подключаться в первую очередь."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"Установление связи с устройствами Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Приложение сможет просматривать конфигурацию Bluetooth на планшетном ПК, а также запрашивать и подтверждать соединение с другими устройствами."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Приложение сможет просматривать конфигурацию Bluetooth на телефоне, а также запрашивать и подтверждать соединение с другими устройствами."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 229e9f3..e9f3adc 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synchronizovať"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Príliš veľa odstránených položiek služby <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Ukladací priestor tabletu je plný. Odstráňte niektoré súbory a uvoľnite miesto."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Ukladací priestor hodiniek je plný. Uvoľnite miesto odstránením niektorých súborov."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Pamäť telefónu je plná. Odstráňte niektoré súbory a uvoľnite miesto."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Sieť môže byť monitorovaná"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Neznámou treťou stranou"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Zvonenie je zapnuté"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Prebieha vypínanie..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Váš tablet bude vypnutý."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Hodinky sa vypnú."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Váš telefón bude vypnutý."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Chcete zariadenie vypnúť?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Reštartovať do núdzového režimu"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Umožňuje aplikácii vysielať oznámenie, že správa SMS bola doručená. Škodlivé aplikácie môžu toto nastavenie použiť na falšovanie prichádzajúcich správ SMS."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"odoslanie vysielania typu WAP-PUSH-received"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Umožňuje aplikácii vysielať oznámenie, že správa WAP PUSH bola doručená. Škodlivé aplikácie môžu použiť toto nastavenie na vytvorenie potvrdenia o doručení správy MMS alebo na utajené nahradenie obsahu akejkoľvek stránky škodlivými variantmi."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"odoslanie skóre vysielaných sieťami"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Umožňuje aplikácii vysielať upozornenie, že je potrebné zadať skóre sietí. Bežné aplikácie toto povolenie nepotrebujú."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"obmedzenie počtu spustených procesov"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Umožňuje aplikácii kontrolovať maximálny počet spustených procesov. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"vynútiť zavretie aplikácií na pozadí"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Umožňuje držiteľovi viazať sa na najvyššiu úroveň rozhrania služby miniaplikácií. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"viazanie na službu poskytovateľa cesty"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Umožňuje držiteľovi viazať sa na akýchkoľvek registrovaných poskytovateľov cesty. Normálne aplikácie by toto povolenie nemali nikdy nepotrebovať."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"komunikovať so správcom zariadenia"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Umožňuje držiteľovi odosielať informácie správcovi zariadenia. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"viazanie na televízny vstup"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Zmeniť stav siete WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Umožňuje aplikácii pripojiť tablet k sieťam WiMAX a odpojiť ho od nich."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Umožňuje aplikácii pripojiť telefón k sieťam WiMAX a odpojiť ho od nich."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"zadanie skóre sietí"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Umožňuje aplikácii hodnotiť siete a ovplyvňovať, ktoré siete by mal tablet preferovať."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Umožňuje aplikácii hodnotiť siete a ovplyvňovať, ktoré siete by mal telefón preferovať."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"párovať so zariadeniami Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Umožňuje aplikácii zobraziť informácie o konfigurácii Bluetooth na tablete. Taktiež jej umožňuje nadväzovať a akceptovať spojenia so spárovanými zariadeniami."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Umožňuje aplikácii zobraziť informácie o konfigurácii Bluetooth na telefóne. Taktiež jej umožňuje nadväzovať a akceptovať spojenia so spárovanými zariadeniami."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index de57325..6d4f21c 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sinhronizacija"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Preveč izbrisov vsebine <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Pomnilnik tabličnega računalnika je poln. Izbrišite nekaj datotek, da sprostite prostor."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Shramba ure je polna. Izbrišite nekaj datotek, da sprostite prostor."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Pomnilnik telefona je poln. Izbrišite nekaj datotek, da sprostite prostor."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Omrežje je lahko nadzorovano"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Neznana tretja oseba"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Vklopi zvonjenje"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Se zaustavlja ..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Tablični računalnik se bo zaustavil."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Ura se bo izklopila."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Telefon bo zaustavljen."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Ali želite izklopiti napravo?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Vnovičen zagon v varnem načinu"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Programu omogoča oddajo obvestila o prejetih sporočilih SMS. Zlonamerni programi lahko to uporabijo za ponarejanje dohodnih SMS-ov."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"pošiljanje oddaje, prejete s potisnim sporočilom WAP"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Programu omogoča oddajo obvestila, da je bilo potisno sporočilo WAP prejeto. Zlonamerni programi lahko to uporabijo za ponarejanje potrdila o prejemu sporočila MMS ali za neopazno menjavo vsebine poljubne spletne strani z zlonamernimi različicami."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"pošiljanje oddaj o ocenjevanju omrežij"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Aplikaciji dovoli oddajo obvestila, da je treba omrežja oceniti. Tega nikoli ni treba uporabiti za navadne aplikacije."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"omejevanje števila izvajajočih se procesov"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Programu omogoča nadzor največjega števila postopkov, ki se bodo izvajali. Tega nikoli ni treba uporabiti za navadne programe."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"vsiljeno zapiranje aplikacij v ozadju"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Lastniku omogoča povezovanje z vmesnikom storitve pripomočka najvišje ravni. Tega ni treba nikoli uporabiti za navadne programe."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"Povezava s storitvijo ponudnika poti"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Omogoča imetniku, da se povezuje z registriranimi ponudniki poti. Tega ni treba nikoli uporabiti za navadne aplikacije."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"interakcija s skrbnikom naprave"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Omogoča lastniku, da pošlje namere skrbniku naprave. Nikoli se ne uporablja za navadne programe."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"povezava s TV-vhodom"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Sprememba stanja omrežja WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Aplikaciji omogoča, da vzpostavi povezavo med tabličnim računalnikom in omrežjem WiMAX ter jo prekine."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Aplikaciji omogoča, da vzpostavi povezavo med telefonom in omrežjem WiMAX ter jo prekine."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"ocenjevanje omrežij"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Aplikaciji dovoli, da omrežja razvršča in vpliva na to, katera naj tablični računalnik prednostno izbere."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Aplikaciji dovoli, da omrežja razvršča in vpliva na to, katera naj telefon prednostno izbere."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"seznanitev z napravami Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Aplikaciji omogoča ogled konfiguracije Bluetootha tabličnega računalnika ter vzpostavljanje in sprejemanje povezave s seznanjenimi napravami."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Aplikaciji omogoča ogled konfiguracije Bluetootha telefona ter ustvarjanje in sprejemanje povezave s seznanjenimi napravami."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 4bf015f..1a3a235 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Синхронизација"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Превише <xliff:g id="CONTENT_TYPE">%s</xliff:g> избрисаних ставки."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Меморија таблета је пуна! Избришите неке датотеке да бисте ослободили простор."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Меморија сата је пуна. Избришите неке датотеке да бисте ослободили простор."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Складиште телефона је пуно! Избришите неке датотеке како бисте ослободили простор."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Мрежа се можда надгледа"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Од стране непознате треће стране"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Звоно је укључено"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Искључивање…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Таблет ће се искључити."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Сат ће се угасити."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Телефон ће се искључити."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Да ли желите да искључите телефон?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Поново покрени систем у безбедном режиму"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Дозвољава апликацији да емитује обавештење да је SMS порука примљена. Злонамерне апликације на тај начин могу да фалсификују долазне SMS поруке."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"слање примљених PUSH емитовања преко WAP-а"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Дозвољава апликацији да емитује обавештење да је примљена PUSH порука преко WAP-а. Злонамерне апликације то могу да искористе да фалсификују пријем MMS порука или да кришом замене садржај било које веб-странице уносом злонамерног садржаја."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"шаљи обавештења о тестирању мрежа"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Омогућава апликацији да емитује обавештење да је потребно тестирање мрежа. Никада није потребно за стандардне апликације."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"ограничење броја покренутих процеса"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Дозвољава апликацији да управља максималним бројем процеса који ће моћи да се покрену. Никада није потребна уобичајеним апликацијама."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"принудно затварање позадинских апликација"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Дозвољава власнику да се повеже са интерфејсом удаљеног екрана највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"обавезивање на услугу виџета"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Дозвољава власнику да се обавеже на интерфејс услуге виџета највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"повежи са услугом добављача путања"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Дозвољава власнику да се повеже са добављачима путања. Никада не би требало да буде потребно за обичне апликације."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"интеракција са администратором уређаја"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Омогућава да власник шаље своје намере администратору уређаја. Уобичајене апликације никада не би требало да је користе."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"повезивање са ТВ улазом"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Промени WiMAX статус"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Дозвољава апликацији да повезује таблет са WiMAX мрежама и прекида везе са њима."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Дозвољава апликацији да повезује телефон са WiMAX мрежама и прекида везе са њима."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"тестирај мреже"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Омогућава апликацији да рангира мреже и утиче на то које су мреже примарне на таблету."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Омогућава апликацији да рангира мреже и утиче на то које су мреже примарне на телефону."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"упаривање са Bluetooth уређајима"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Дозвољава апликацији да прегледа конфигурацију Bluetooth-а на таблету, као и да успоставља и прихвата везе са упареним уређајима."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Дозвољава апликацији да прегледа конфигурацију Bluetooth-а на телефону, као и да успоставља и прихвата везе са упареним уређајима."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 24b032e..03dc691 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synkronisera"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"För många <xliff:g id="CONTENT_TYPE">%s</xliff:g>-borttagningar."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Pekdatorns lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Klockans lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Mobilens lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Nätverket kan vara övervakat"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Av en okänd tredje part"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Ringsignal på"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Avslutar…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Din surfplatta stängs av."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Klockan stängs av."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Din telefon stängs av."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Vill du stänga av?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Starta om i felsäkert läge"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Tillåter att appen sänder ut en avisering när SMS tas emot. Skadliga appar kan använda detta för att förfalska inkommande SMS."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"skicka WAP-PUSH-mottagen sändning"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Tillåter att appen skickar ett meddelande om att ett WAP PUSH-meddelande har tagits emot. Skadliga appar kan använda detta för att förfalska mottagning av MMS eller för att obemärkt byta ut innehållet på en webbsida mot skadligt innehåll."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"skicka betyg som nätverk skickar"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Tillåter att appen skickar ett meddelande om att nätverket måste betygsättas. Ska inte behövas för vanliga appar."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"begränsa antalet processer som körs"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Tillåter att appen styr högsta antalet processer som körs. Behövs inte för vanliga appar."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"tvinga bakgrundsappar att avslutas"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Innehavaren tillåts att binda till den översta nivåns gränssnitt för en widget. Ska inte behövas för vanliga appar."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"bind till en ruttleverantörstjänst"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Tillåter att innehavaren binds till en registrerad ruttleverantör. Detta ska inte behövas för vanliga appar."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"arbeta med en enhetsadministratör"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Tillåter att innehavaren skickar avsikter till en enhetsadministratör. Vanliga appar behöver aldrig göra detta."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"binda till en tv-insignal"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"ändra WiMAX-status"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Tillåter att appen ansluter surfplattan till eller kopplar från WiMAX-nätverk."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Tillåter att appen ansluter mobilen till eller kopplar från WiMAX-nätverk."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"betygsätt nätverk"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Tillåter att appen betygsätter nätverk och påverkar vilka nätverk som ska användas i första hand av surfplattan."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Tillåter att appen betygsätter nätverk och påverkar vilka nätverk som ska användas i första hand av mobilen."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"koppla till Bluetooth-enheter"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Tillåter att appen kommer åt pekdatorns Bluetooth-konfiguration och upprättar och godkänner anslutningar till parkopplade enheter."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Tillåter att appen kommer åt mobilens Bluetooth-konfiguration och upprättar och godkänner anslutningar till parkopplade enheter."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 233bd7a..71f5003 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sawazisha"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Ufutaji mwingi sana <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Hifadhi ya kompyuta kibao imejaa. Futa baadhi ya faili ili kupata nafasi."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Hifadhi ya saa imejaa. Futa baadhi ya faili ili uweze kupata nafasi."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Hifadhi ya simu imejaa. Futa baadhi ya faili ili uweze kupata nafasi."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Huenda mtandao unafuatiliwa"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Na mtu mwingine asiyejulikana"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Programu ya milio imewashwa"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Inafunga..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Kompyuta kibao yako itazima."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Saa yako itajizima."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Simu yako itazima."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Unataka kuzima?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Washa upya kwa hali salama"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Huruhusu programu kutangaza taarifa kwamba ujumbe wa SMS umeingia. Programu hasidi zinaweza kutumia hii kubuni SMS zinazoingia."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"tuma tangazo lililopokewa la MSUKUMO WA WAP"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Inaruhusu programu kutangaza taarifa kwamba ujumbe wa WAP PUSH umepokewa. Programu hasidi zinaweza kutumia hii kubuni risiti ya ujumbe wa MMS au polepole kubadilisha maudhui yoyote ya ukurasa wa tovuti na vibadala vibovu."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"tuma tangazo la alama za mitandao"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Huruhusu programu kutangaza arifa kuwa mitandao inafaa kupewa alama. Haihitajiki kamwe kwa programu za kawaida."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"zuia idadi ya michakato inayoendeshwa"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Inaruhusu programu kudhibiti upeo wa idadi ya michakato ambayo itaendeshwa. Kamwe hazihitajiki kwa programu za kwaida."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"lazimisha programu za usuli kufunga"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Inaruhusu mmiliki kushurutisha kusano ya kiwango cha juu ya huduma ya wijeti. Haipaswi kuhitajika kwa programu za kawaida."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"shurutisha kwenye huduma ya mtoa huduma wa njia"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Huruhusu mmiliki kushurutisha kwenye watoa huduma wa njia waliosajiliwa. Haipaswi kuhitajika kwa programu za kawaida."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"jiunge na msimamizi wa kifaa"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Inamruhusu mmiliki kutuma malengo kwa msimamizi wa kifaa. Haipaswi kuhitajika kwa programu za kawaida."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"bandika kwenye zana za data ya runinga"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Badilisha hali ya WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Inaruhusu programu kuunganisha kompyuta kibao, na kukata kompyuta kibao kutoka mitandao ya WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Inaruhusu programu kuunganisha simu kwenye, na kukata simu kutoka mitandao ya WiMAX."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"ipe mitandao alama"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Huruhusu programu kupanga mitandao kwa alama na kushawishi mitandao ambayo kompyuta kibao inapaswa kupendelea."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Huruhusu programu kupanga mitandao kwa alama na kushawishi mitandao ambayo simu inapaswa kupendelea."</string>
<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>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 452c02d..b8d99b0 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"ซิงค์"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"มีการลบ <xliff:g id="CONTENT_TYPE">%s</xliff:g> มากเกินไป"</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"ที่จัดเก็บข้อมูลของแท็บเล็ตเต็ม ลบไฟล์บางไฟล์เพื่อเพิ่มพื้นที่ว่าง"</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"ที่เก็บข้อมูลนาฬิกาเต็ม โปรดลบไฟล์บางไฟล์เพื่อเพิ่มพื้นที่ว่าง"</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"ที่เก็บข้อมูลโทรศัพท์เต็ม ลบบางไฟล์เพื่อเพิ่มที่ว่าง"</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"เครือข่ายอาจได้รับการตรวจสอบ"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"โดยบุคคลที่สามที่ไม่รู้จัก"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"เปิดเสียง"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"กำลังปิดระบบ..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"แท็บเล็ตของคุณจะปิดการทำงาน"</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"นาฬิกาจะปิดการทำงาน"</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"โทรศัพท์ของคุณจะปิดเครื่อง"</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"คุณต้องการปิดการทำงานหรือไม่"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"รีบูตเข้าสู่โหมดปลอดภัย"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"อนุญาตให้แอปพลิเคชันกระจายข้อมูลการแจ้งเตือนว่าได้รับข้อความ SMS แล้ว แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ปลอมข้อความ SMS ที่เข้ามา"</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"ส่งการกระจายข้อมูลว่าได้รับ WAP-PUSH"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"อนุญาตให้แอปพลิเคชันกระจายข้อมูลการแจ้งเตือนว่าได้รับข้อความ WAP PUSH แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ปลอมการแจ้งรับข้อความ MMS หรือแอบเปลี่ยนเนื้อหาในหน้าเว็บโดยใช้ตัวแปรที่เป็นอันตราย"</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"ส่งเผยแพร่การให้คะแนนเครือข่าย"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"อนุญาตให้แอปนี้เผยแพร่ข้อมูลการแจ้งเตือนว่าเครือข่ายจะต้องผ่านการให้คะแนน ไม่จำเป็นสำหรับแอปทั่วไป"</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"จำกัดจำนวนกระบวนการที่กำลังทำงาน"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"อนุญาตให้แอปพลิเคชันควบคุมจำนวนสูงสุดของกระบวนการที่จะเรียกใช้ ไม่จำเป็นต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"บังคับปิดแอปพลิเคชันในพื้นหลัง"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"อนุญาตให้ผู้ใช้ผูกกับอินเทอร์เฟซระดับสูงสุดของจอแสดงผลระยะไกล ซึ่งแอปพลิเคชันทั่วไปไม่จำเป็นต้องใช้"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"เชื่อมโยงกับบริการวิดเจ็ต"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"อนุญาตให้ผู้ใช้เชื่อมโยงกับส่วนติดต่อผู้ใช้ระดับสูงสุดของบริการวิดเจ็ต ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"เชื่อมโยงกับบริการของผู้ให้บริการเส้นทาง"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"ช่วยให้เจ้าของสามารถเชื่อมโยงกับผู้ให้บริการเส้นทางที่ลงทะเบียนรายใดก็ได้ ไม่จำเป็นสำหรับแอปทั่วไป"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"ติดต่อกับผู้ดูแลอุปกรณ์"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"อนุญาตให้ผู้ใช้ส่งการติดต่อไปยังโปรแกรมควบคุมอุปกรณ์ ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"เชื่อมโยงกับอินพุตทีวี"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"เปลี่ยนสถานะของ WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"อนุญาตให้แอปพลิเคชันเชื่อมต่อและยกเลิกการเชื่อมต่อแท็บเล็ตกับเครือข่าย WiMAX"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"อนุญาตให้แอปพลิเคชันเชื่อมต่อและยกเลิกการเชื่อมต่อโทรศัพท์กับเครือข่าย WiMAX"</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"ให้คะแนนเครือข่าย"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"อนุญาตให้แอปนี้จัดลำดับเครือข่าย ซึ่งมีผลต่อการเลือกใช้เครือข่ายของแท็บเล็ต"</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"อนุญาตให้แอปนี้จัดอันดับเครือข่ายและมีผลต่อการเลือกใช้เครือข่ายของโทรศัพท์"</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"จับคู่กับอุปกรณ์บลูทูธ"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"อนุญาตให้แอปพลิเคชันดูการกำหนดค่าบลูทูธของแท็บเล็ต ตลอดจนเชื่อมต่อและยอมรับการเชื่อมต่อกับอุปกรณ์ที่จับคู่ไว้"</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"อนุญาตให้แอปพลิเคชันดูการกำหนดค่าบลูทูธของโทรศัพท์ ตลอดจนเชื่อมต่อและยอมรับการเชื่อมต่อกับอุปกรณ์ที่จับคู่ไว้"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index dc7de15..394f01e 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"I-sync"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Masyadong maraming pagtanggal ng <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Puno na ang storage ng tablet. Magtanggal ng ilang file upang magbakante ng espasyo."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Puno na ang storage ng relo. Magtanggal ng ilang file upang magbakante ng espasyo."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Puno na ang storage ng telepono. Magtanggal ng ilang file upang magbakante ng espasyo."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Maaaring sinusubaybayan ang network"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Ng isang di-kilalang third party"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"I-on ang ringer"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Nagsa-shut down…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Mag-shut down ang iyong tablet."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Magsa-shut down ang iyong relo."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Magsa-shut down ang iyong telepono."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Nais mo bang mag-shut down?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Mag-reboot sa safe mode"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Pinapayagan ang app na mag-broadcast ng isang notification na natanggap ang isang mensaheng SMS. Maaari itong gamitin ng nakakahamak na apps upang dayain ang papasok na mga mensaheng SMS."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"ipadala ang WAP-PUSH-natanggap na pag-broadcast"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Pinapayagan ang app na mag-broadcast ng isang notification na natanggap ang isang mensaheng WAP PUSH. Maaari itong gamitin ng nakakahamak na apps upang dayain ang pagtanggap ng mensaheng MMS o upang tahimik na palitan ang nilalaman ng anumang webpage ng mga nakakahamak na variant."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"magpadala ng broadcast ng mga network ng score"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Nagbibigay-daan sa app na mag-broadcast ng notification na kailangang ma-score ng mga network. Hindi kailanman kinakailangan para sa mga normal na app."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"limitahan ang numero ng mga tumatakbong proseso"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Pinapayagan ang app na kontrolin ang maximum na bilang ng mga proseso na tatakbo. Hindi kailanman kinakailangan para sa normal na apps."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"pwersahin ang mga app sa background na magsara"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Pinapayagan ang may-hawak na sumailalim sa nangungunang interface ng serbisyo ng widget. Hindi kailanman dapat na kailanganin para sa normal na apps."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"mag-bind sa isang serbisyo ng route provider"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Nagbibigay-daan sa may-pahintulot na mag-bind sa anumang nakarehistrong route provider. Hindi dapat kailanganin kailanman ng mga normal na app."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"makipag-ugnay sa tagapangasiwa ng device"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Pinapayagan ang mga may-ari na magpadala ng mga layunin sa administrator ng device. Hindi kailanman dapat na kailanganin para sa normal na apps."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"i-bind sa isang TV input"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Baguhin ang katayuan ng WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Pinapayagan ang app na ikonekta ang tablet at idiskonekta ang tablet mula sa mga WiMAX network."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Pinapayagan ang app na ikonekta ang telepono at idiskonekta ang telepono mula sa mga WiMAX network."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"mga network ng score"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Nagbibigay-daan sa app na iranggo ang mga network at impluwensiyahan kung aling mga network ang dapat na piliin ng tablet."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Nagbibigay-daan sa app na iranggo ang mga network at impluwensiyahan kung aling mga network ang dapat na piliin ng telepono."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"ipares sa mga Bluetooth device"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Pinapayagan ang app na tingnan ang configuration ng Bluetooth sa tablet, at na gumawa at tumanggap ng mga koneksyong may mga nakapares na device."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Pinapayagan ang app na tingnan ang configuration ng Bluetooth sa telepono, at na gumawa at tumanggap ng mga koneksyong may mga nakapares na device."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index db8c9d4..cde7219 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Senk."</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Çok fazla <xliff:g id="CONTENT_TYPE">%s</xliff:g> silme var."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Tabletin depolama alanı dolu! Yer açmak için bazı dosyaları silin."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Saat depolama alanınız dolu. Lütfen yer boşaltmak için bazı dosyaları silin."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Telefonun depolama alanı dolu! Yer açmak için bazı dosyaları silin."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Ağ izlenebilir"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Bunu, bilinmeyen üçüncü taraflar yapabilir"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Telefon zili açık"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Kapanıyor…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Tabletiniz kapanacak."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Saatiniz kapatılacak."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Telefonunuz kapanacak."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Kapatmak istiyor musunuz?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Güvenli modda yeniden aç"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Uygulamaya, SMS mesajı alındığına dair bildirim yayınlama izni verir. Kötü amaçlı uygulamalar sahte SMS mesajları göndermek için bunu kullanabilir."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"WAP-PUSH ile alınan yayın gönder"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Uygulamaya, WAP PUSH mesajı alındığına dair bildirim yayınlama izni verir. Kötü amaçlı uygulamalar sahte MMS bildirimleri oluşturmak veya bir web sayfasının içeriğini sessiz şekilde zararlı öğelerle değiştirmek için bunu kullanabilir."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"ağları puanlama yayını gönderme"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Uygulamaya, ağların puanlanması gerektiği bildirimini yayınlama izni verir. Normal uygulamalar için hiçbir zaman gerekmez."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"çalışan işlem sayısını sınırla"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Uygulamaya, çalışacak süreçlerin azami sayısını denetleme izni verir. Normal uygulamalar için gerekli değildir."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"arka plan uygulamaları kapanmaya zorla"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Cihazın sahibine bir widget hizmetinin en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"rota sağlayıcı hizmetine bağlanma"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"İzin verilen uygulamaya tüm kayıtlı rota sağlayıcılarına bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"bir cihaz yöneticisi ile etkileşimde bulun"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Cihazın sahibinin cihaz yöneticisine amaç göndermesine izin verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"TV girişine bağlanma"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"WiMAX durumunu değiştir"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Uygulamaya, tableti WiMAX ağlarına bağlanma veya bağlantıyı kesme izni verir."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Uygulamaya, telefonu WiMAX ağlarına bağlanma veya bağlantıyı kesme izni verir."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"ağları puanlama"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Uygulamaya, ağları sıralama ve tabletin tercih edeceği ağları etkileme izni verir."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Uygulamaya, ağları sıralama ve telefonunun tercih edeceği ağları etkileme izni verir."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"Bluetooth cihazlarla eşle"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Uygulamaya, tabletteki Bluetooth yapılandırmasını görüntüleme, eşleştirilmiş cihazlarla bağlantı yapma ve bu tür bağlantıları kabul etme izni verir."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Uygulamaya, telefondaki Bluetooth yapılandırmasını görüntüleme, eşleştirilmiş cihazlarla bağlantı yapma ve bu tür bağlantıları kabul etme izni verir."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 388d124..f55fcf3 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -337,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Дозволяє програмі передавати сповіщення про отримання SMS повідомлення. Шкідливі програми можуть використовувати це для підробки вхідних SMS повідомлень."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"надсил. запис, отр. через WAP-PUSH"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Дозволяє програмі передавати сповіщення про отримання повідомлення WAP PUSH. Шкідливі програми можуть використовувати це для підробки отримання MMS повідомлень або для непомітної заміни вмісту будь-якої веб-сторінки шкідливими варіантами."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"надсилати сповіщення про оцінку мереж"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Дозволяє додатку передавати сповіщення про оцінку мереж. Ніколи не застосовується для звичайних додатків."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"обмежувати кількість запущ. процесів"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Дозволяє програмі контролювати максимальну кількість процесів, які буде запущено. Ніколи не вимагається для звичайних програм."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"примусово закривати фонові програми"</string>
@@ -392,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Дозволяє власникові прив’язуватися до інтерфейсу верхнього рівня віддаленого екрана. Ніколи не застосовується для звичайних програм."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"прив\'язувати до служби віджетів"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Дозволяє власникові прив’язуватися до інтерфейсу верхнього рівня служби віджетів. Ніколи не застосовується для звичайних програм."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"підключитися до служби постачання маршрутів"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Дозволяє власникові підключатися до зареєстрованих постачальників маршрутів. Звичайні додатки ніколи не використовують цей дозвіл."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"взаємодіяти з адмін. пристрою"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Дозволяє власнику надсилати задавані функції адміністратору пристрою. Ніколи не застосовується для звичайних програм."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"підключатися до TV-входу"</string>
@@ -642,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Змінити стан WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Дозволяє програмі під’єднувати планшетний ПК до мереж WiMAX і від’єднувати його від них."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Дозволяє програмі під’єднувати телефон до мереж WiMAX і від’єднувати його від них."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"оцінювати мережі"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Дозволяє додатку оцінювати мережі та впливати на вибір мережі планшетом."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Дозволяє додатку оцінювати мережі та впливати на вибір мережі телефоном."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"створювати пару з пристроями Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Дозволяє програмі переглядати конфігурацію Bluetooth на планшетному ПК, а також створювати та приймати з’єднання зі спареними пристроями."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Дозволяє програмі переглядати конфігурацію Bluetooth на телефоні, а також створювати та приймати з’єднання зі спареними пристроями."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index afde154..841a68d 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -339,10 +339,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Cho phép ứng dụng truyền phát thông báo cho biết đã nhận được tin nhắn SMS. Ứng dụng độc hại có thể sử dụng quyền này để giả mạo tin nhắn SMS đến."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"gửi truyền phát WAP-PUSH nhận được"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Cho phép ứng dụng truyền phát thông báo cho biết rằng đã nhận được tin nhắn WAP PUSH. Ứng dụng độc hại có thể sử dụng quyền này để giả mạo xác nhận đã nhận được tin nhắn MMS hoặc ngầm thay thế nội dung của bất kỳ trang web nào bằng các biến thể độc hại."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"gửi chương trình phát mạng điểm số"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Cho phép ứng dụng truyền thông báo rằng các mạng cần để được tính điểm. Không bao giờ cần cho ứng dụng thông thường."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"giới hạn số quá trình đang chạy"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Cho phép ứng dụng kiểm soát số quy trình tối đa sẽ chạy. Không cần thiết cho các ứng dụng thông thường."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"buộc ứng dụng nền đóng"</string>
@@ -394,6 +392,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"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ụ tiện ích con. Không cần thiết cho các ứng dụng thông thường."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"liên kết với dịch vụ nhà cung cấp định tuyến"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Cho phép chủ sở hữu liên kết với bất kỳ nhà cung cấp định tuyến đã đăng ký nào. Không cần thiết cho các ứng dụng thông thường."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"tương tác với quản trị viên thiết bị"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Cho phép chủ sở hữu gửi các ý định đến quản trị viên thiết bị. Không cần thiết cho các ứng dụng thông thường."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"liên kết với đầu vào TV"</string>
@@ -644,12 +644,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Thay đổi trạng thái WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Cho phép ứng dụng kết nối máy tính bảng và ngắt kết nối máy tính bảng khỏi mạng WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Cho phép ứng dụng kết nối điện thoại và ngắt kết nối điện thoại khỏi mạng WiMAX."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"mạng điểm số"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Cho phép ứng dụng xếp hạng mạng và ảnh hưởng đến việc máy tính bảng nên ưu tiên mạng nào."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Cho phép ứng dụng xếp hạng các mạng và ảnh hưởng đến việc điện thoại nên ưu tiên mạng nào."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"ghép nối với thiết bị Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Cho phép ứng dụng xem cấu hình của Bluetooth trên máy tính bảng và tạo và chấp nhận các kết nối với các thiết bị được ghép nối."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Cho phép ứng dụng xem cấu hình của Bluetooth trên điện thoại, tạo và chấp nhận các kết nối với các thiết bị được ghép nối."</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 38ce45d..020484e 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"同步"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"太多<xliff:g id="CONTENT_TYPE">%s</xliff:g>删除项。"</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"平板电脑存储空间已满。请删除一些文件以腾出空间。"</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"手表存储空间已满。请删除一些文件以腾出空间。"</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"手机存储空间已满。请删除一些文件以腾出空间。"</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"网络可能会受到监控"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"受到不明第三方的监控"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"振铃器开启"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"正在关机..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"您的平板电脑会关闭。"</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"您的手表即将关机。"</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"您的手机将会关机。"</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"您要关机吗?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"重新启动并进入安全模式"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"允许应用广播一条有关已收到短信的通知。恶意应用可能借此伪造接到的短信。"</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"发送 WAP-PUSH 收到的广播"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"允许应用广播一条有关已收到 WAP PUSH 短信的通知。恶意应用可能借此伪造短信接收,或在后台将任意网页的内容替换为恶意内容。"</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"发送网络评分广播通知"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"允许应用广播“需要为网络评分”的通知。普通应用绝不需要此权限。"</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"限制运行的进程个数"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"允许应用控制将运行的进程数上限。普通应用绝不需要此权限。"</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"强制关闭后台应用"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"允许应用绑定至远程显示屏的顶级接口。普通应用绝不需要此权限。"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"绑定到小部件服务"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"允许应用绑定到小部件服务的顶级接口。普通应用绝不需要此权限。"</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"绑定到路由程序服务"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"允许应用绑定到任何已注册的路由程序。普通应用绝不需要此权限。"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"与设备管理器交互"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"允许用户将意向发送给设备管理员。普通应用绝不需要此权限。"</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"绑定至电视输入设备"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"更改 WiMAX 状态"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"允许该应用建立和断开平板电脑与 WiMAX 网络之间的连接。"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"允许该应用建立和断开手机与 WiMAX 网络之间的连接。"</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"为网络评分"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"允许应用为网络排名,并控制平板电脑应优先使用的网络。"</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"允许应用为网络排名,并控制手机应优先使用的网络。"</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"与蓝牙设备配对"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"允许该应用查看平板电脑上的蓝牙配置,以及建立和接受与配对设备的连接。"</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"允许该应用查看手机上的蓝牙配置,以及建立和接受与配对设备的连接。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index e4f54af..8841536 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Google Sync"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"同時刪除太多 <xliff:g id="CONTENT_TYPE">%s</xliff:g>。"</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"平板電腦的儲存空間已滿。請刪除一些檔案,以騰出可用空間。"</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"手錶的儲存空間已滿。請刪除一些檔案,以騰出可用空間。"</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"手機的儲存空間已滿。請刪除一些檔案,以騰出可用空間。"</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"網絡可能會受到監控"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"由不明的第三方監管"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"鈴聲開啟"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"正在關機..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"您的平板電腦將會關機。"</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"您的手錶即將關機。"</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"您的手機即將關機。"</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"您要關機嗎?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"重新啟動進入安全模式"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"允許應用程式在收到短訊時發出通知。惡意應用程式可能會藉此偽造外來短訊。"</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"傳送可由 WAP PUSH 接收的廣播"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"允許應用程式在收到 WAP PUSH 訊息時發送通知。惡意應用程式可能會藉此偽造 MMS 訊息回條或私自以惡意內容更換網頁。"</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"傳送網絡計分廣播"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"允許應用程式廣播網絡需要計分的通知,但一般應用程式並不需要使用。"</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"執行程序數目上限"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"允許應用程式控制可執行程序的數量上限 (不建議一般應用程式使用)。"</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"強制關閉背景應用程式"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"允許應用程式繫結至遠端屏螢的頂層介面 (不建議一般應用程式使用)。"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"繫結至小工具服務"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"允許應用程式繫結至小工具服務的頂層介面 (不建議一般應用程式使用)。"</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"繫結至路由供應商服務"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"允許應用程式繫結至任何已註冊的路由供應商,但一般應用程式並不需要使用。"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"與裝置管理員互動"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"允許應用程式將調用請求傳送至裝置管理員 (不建議一般應用程式使用)。"</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"繫結至電視訊號輸入裝置"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"更改 WiMAX 狀態"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"允許應用程式建立或中斷平板電腦與 WiMAX 網絡的連線。"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"允許應用程式建立或中斷手機與 WiMAX 網絡的連線。"</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"為網絡計分"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"允許應用程式為網絡排名,及決定平板電腦偏好使用的網絡。"</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"允許應用程式為網絡排名,及決定手機偏好使用的網絡。"</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"與藍牙裝置配對"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"允許應用程式查看平板電腦的藍牙設定,以及建立和接受與其他配對裝置的連線。"</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"允許應用程式查看手機的藍牙設定,以及建立和接受與其他配對裝置的連線。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 9b89c75..bab94b4 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"同步處理"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"同時刪除太多 <xliff:g id="CONTENT_TYPE">%s</xliff:g>。"</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"平板電腦的儲存空間已滿。請刪除一些檔案,以釋放出可用空間。"</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"手錶儲存空間已用盡,請刪除一些檔案以釋出可用空間。"</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"手機儲存空間已滿。請刪除一些檔案,以釋放可用空間。"</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"網路可能會受到監控"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"受到不明的第三方監控"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"鈴聲開啟"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"關機中…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"您的平板電腦將會關機。"</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"您的手錶即將關機。"</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"手機即將關機。"</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"您要關機嗎?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"重新啟動進入安全模式"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"允許應用程式在收到 SMS 簡訊時發出通知。請注意,惡意應用程式可能利用此功能偽造外來的 SMS 簡訊。"</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"送出「WAP PUSH 已接收」廣播"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"允許應用程式在收到 WAP PUSH 訊息時發送通知。請注意,惡意應用程式可能利用此功能偽造 MMS 簡訊回條,或私自將網頁內容更換為惡意陷阱。"</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"傳送網路計分廣播通知"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"允許應用程式廣播網路需計分的通知訊息 (一般應用程式並不需要)。"</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"執行程序限制數"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"允許應用程式控制可執行程序的數量上限 (一般應用程式不需使用)。"</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"強制關閉背景應用程式"</string>
@@ -394,6 +390,8 @@
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"允許應用程式繫結至遠端螢幕的頂層介面 (一般應用程式不需使用)。"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"繫結至小工具服務"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"允許應用程式繫結至小工具服務的頂層介面 (一般應用程式不需使用)。"</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"繫結至路由供應商服務"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"允許應用程式繫結至任何已註冊的路由供應商 (一般應用程式並不需要)。"</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"與裝置管理員互動"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"允許應用程式將調用請求傳送至裝置管理員 (一般應用程式不需使用)。"</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"繫結至電視訊號輸入裝置"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"變更 WiMAX 狀態"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"允許應用程式建立或中斷平板電腦與 WiMAX 網路的連線。"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"允許應用程式建立或中斷手機與 WiMAX 網路的連線。"</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"為網路計分"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"允許應用程式建立網路排名,決定平板電腦偏好使用的網路。"</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"允許應用程式建立網路排名,決定手機偏好使用的網路。"</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"與藍牙裝置配對"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"允許應用程式查看平板電腦的藍牙設定,以及建立和接受與其他配對裝置的連線。"</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"允許應用程式查看手機的藍牙設定,以及建立和接受與其他配對裝置的連線。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index e552c4e..4dbd003 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Vumelanisa"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Kunokususa <xliff:g id="CONTENT_TYPE">%s</xliff:g> okuningi kakhulu."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Isilondolozi sethebhulethi sigcwele! Susa amanye amafayela ukukhulula isikhala."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Isitoreji sokubuka sigcwele. Susa amanye amafayela ukukhulula isikhala."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Isilondolozi sefoni sigcwele! Susa amanye amafayela ukukhulula isikhala."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Inethiwekhi ingase inganyelwe"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Ngenkampani yangaphandle engaziwa"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Iringa iyasebenza"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Ivala shaqa..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Ithebhulethi yakho izocima."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Ukubuka kwakho kuzocima."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Ifoni yakho izocima."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Ingabe ufuna ukucisha?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Qala kabusha emodini ephephile"</string>
@@ -339,10 +337,8 @@
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Ivumela uhlelo lokusebenza ukuthi isakaze isaziso sokuthi umyalezo we-SMS utholakele. Izuhlelo lokusebenza ezinobungozi zingasebenzisa lokhu ukufoja imiyalezo ye-SMS engenayo."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"thumela umsakazo otholwe nge-WAP-PUSH"</string>
<string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Ivumela uhlelo lokusebenza ukuthi isakaze isaziso sokuthi umyalezo we-WAP PUSH utholakele. Izuhlelo lokusebenza ezinobungozi zingasebenzisa lokhu ukufoja ukutholakala kwemiyalezo ye-S noma zisuse okuqukethwe kwanoma iliphi ikhasi lewebhu eliqukethe okunobungozi."</string>
- <!-- no translation found for permlab_broadcastScoreNetworks (6432008366605475024) -->
- <skip />
- <!-- no translation found for permdesc_broadcastScoreNetworks (7652980974435077828) -->
- <skip />
+ <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"thumela ukusakaza kwamanethiwekhi ayisikolo"</string>
+ <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"Ivumela uhlelo lokusebenza ukusakaza isaziso sokuthi amanethiwekhi adinga isikolo. Awadingeki kuzinhlelo zokusebenza ezivamile."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"khawula inani lezinqubo ezisebenzayo"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Ivumela uhlelo lokusebenza ukuthi ilawule isibalo esikhulu sezinto eziqhubekayo eziyosebenza. Ayidingakeli izinhlelo zokusebenza ezijwayelekile."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"phoqa izinhlelo zokusebenza ezingemuva ukuthi zivaleke"</string>
@@ -394,6 +390,8 @@
<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>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"Ivumela umbambi ukuhlanganisa uxhumano nomsebenzisi kwezinga eliphezulu lensizakalo yesinqunjwana. Akusoze kwadingeka kwezinhlelo zokusebenza ezivamile."</string>
+ <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"bophezela kusevisi yomhlinzeki womzila"</string>
+ <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Ivumela isibambi ukubophezela kunoma yimuphi umhlinzeki womzila obhalisiwe. Akufanele kudingeke izinhlelo zokusebenza ezivamile."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"xhumana nomphathi wedivaysi"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Ivumela ummeli ukuthumela okuqukethwe kumphathi wedivaysi. Akusoze kwadingeka kwizinhlelo zokusebenza ezivamile."</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"bophezela kokokufaka kwe-TV"</string>
@@ -644,12 +642,9 @@
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"Shintsha isimo se-WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Ivumela uhlelo lokusebenza ukuxhuma ithebhulethi nokunqamula ithebhulethi kumanethiwekhi e-WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Ivumela uhlelo lokusebenza ukuxhuma ifoni nokuyinqamula kumanethiwekhi e-WiMAX."</string>
- <!-- no translation found for permlab_scoreNetworks (6445777779383587181) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1304304745850215556) -->
- <skip />
- <!-- no translation found for permdesc_scoreNetworks (1831501848178651379) -->
- <skip />
+ <string name="permlab_scoreNetworks" msgid="6445777779383587181">"amanethiwekhi ayisikolo"</string>
+ <string name="permdesc_scoreNetworks" product="tablet" msgid="1304304745850215556">"Ivumela uhlelo lokusebenza ukuthi lilinganise amanethiwekhi futhi lithuthukise ukuthi imaphi amanethiwekhi ithebhulethi okufanele iwakhethe."</string>
+ <string name="permdesc_scoreNetworks" product="default" msgid="1831501848178651379">"Ivumela uhlelo lokusebenza ukuthi lilinganise amanethiwekhi futhi lithuthukise ukuthi imaphi amanethiwekhi ifoni okufanele iwakhethe."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"matanisa namadivayisi e-Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Ivumela uhlelo lokusebenza ukubuka ukucushwa kwe-Bluetooth kuthebhulethi, nokwenza futhi nokwamukela uxhumo namadivayisi amatanisiwe."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Ivumela uhlelo lokusebenza ukubuka ukucushwa kwe-Bluetooth efonini, ukwenza futhi nokwamukela uxhumo namadivayisi amatanisiwe."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 53fed98..b7bffde 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3428,6 +3428,8 @@
<attr name="shadowDy" format="float" />
<!-- Radius of the shadow. -->
<attr name="shadowRadius" format="float" />
+ <!-- Elegant text height, especially for less compacted complex script text. -->
+ <attr name="elegantTextHeight" format="boolean" />
</declare-styleable>
<declare-styleable name="TextClock">
<!-- Specifies the formatting pattern used to show the time and/or date
@@ -3719,6 +3721,8 @@
<attr name="textIsSelectable" />
<!-- Present the text in ALL CAPS. This may use a small-caps form when available. -->
<attr name="textAllCaps" />
+ <!-- Elegant text height, especially for less compacted complex script text. -->
+ <attr name="elegantTextHeight" />
</declare-styleable>
<declare-styleable name="TextViewAppearance">
<!-- Base text color, typeface, size, and style. -->
@@ -4500,6 +4504,8 @@
result to valid color values. Saturate(S + D) -->
<enum name="add" value="16" />
</attr>
+ <!-- Specifies the alpha multiplier to apply to the base drawable. -->
+ <attr name="alpha" />
</declare-styleable>
<!-- Drawable used to draw 9-patches. -->
@@ -4519,6 +4525,8 @@
<!-- When a tint color is set, specifies its Porter-Duff blending mode. The
default value is src_in, which treats the drawable as an alpha mask. -->
<attr name="tintMode" />
+ <!-- Specifies the alpha multiplier to apply to the base drawable. -->
+ <attr name="alpha" />
</declare-styleable>
<!-- Drawable used to draw a single color. -->
diff --git a/core/res/res/values/colors_quantum.xml b/core/res/res/values/colors_quantum.xml
index ebe4a49..f8f192f 100644
--- a/core/res/res/values/colors_quantum.xml
+++ b/core/res/res/values/colors_quantum.xml
@@ -118,11 +118,11 @@
<!-- Text & foreground colors -->
- <color name="primary_text_quantum_light">#de000000</color>
+ <color name="primary_text_default_quantum_light">#de000000</color>
<color name="secondary_text_quantum_light">#8a000000</color>
<color name="tertiary_text_quantum_light">#4d000000</color>
- <color name="primary_text_quantum_dark">#deffffff</color>
+ <color name="primary_text_default_quantum_dark">#deffffff</color>
<color name="secondary_text_quantum_dark">#8affffff</color>
<color name="tertiary_text_quantum_dark">#4dffffff</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f549290..89e870c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -807,6 +807,21 @@
<item>com.android.location.fused</item>
</string-array>
+ <!-- This string array can be overriden to enable test location providers initially. -->
+ <!-- Array of "[locationProviderName],[requiresNetwork],
+ [requiresSatellite],[requiresCell],[hasMonetaryCost],
+ [supportAltitute],[supportsSpeed],[supportsBearing],
+ [powerRequirement],[accuracy]" -->
+ <!-- powerRequirement is defined in android.location.Criteria
+ 0 = NO_REQUIREMENT / 1 = POWER_LOW / 2 = POWER_MEDIUM / 3 = POWER_HIGH -->
+ <!-- accuracy is defined in anroid.location.Criteria
+ 1 = ACCURACY_FINE / 2 = ACCURACY_COARSE -->
+ <string-array name="config_testLocationProviders" translatable="false">
+ <!-- Example test network location provider
+ <item>network,false,false,false,false,true,true,true,1,2</item>
+ -->
+ </string-array>
+
<!-- Boolean indicating if current platform supports bluetooth SCO for off call
use cases -->
<bool name="config_bluetooth_sco_off_call">true</bool>
@@ -1429,4 +1444,9 @@
2 - The device DOES NOT have a permanent menu key; ignore autodetection. -->
<integer name="config_overrideHasPermanentMenuKey">0</integer>
+ <!-- default window inset isRound property -->
+ <bool name="config_windowIsRound">false</bool>
+
+ <!-- Package name for default network scorer app; overridden by product overlays. -->
+ <string name="config_defaultNetworkScorerPackageName"></string>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b0e1150..cacb41f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1077,6 +1077,12 @@
interface of a widget 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_bindRouteProvider">bind to a route 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_bindRouteProvider">Allows the holder to bind to any registered
+ route providers. 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_bindDeviceAdmin">interact with a device admin</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_bindDeviceAdmin">Allows the holder to send intents to
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index e42703e..bdc7ad0 100644
--- a/core/res/res/values/styles_quantum.xml
+++ b/core/res/res/values/styles_quantum.xml
@@ -97,6 +97,7 @@
<item name="textColorLink">?textColorLink</item>
<item name="textSize">@dimen/text_size_body_1_quantum</item>
<item name="fontFamily">@string/font_family_body_1_quantum</item>
+ <item name="elegantTextHeight">true</item>
</style>
<style name="TextAppearance.Quantum.Display4">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b0f19ec..07e8341 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -291,6 +291,7 @@
<java-symbol type="bool" name="config_wifi_batched_scan_supported" />
<java-symbol type="bool" name="config_enableMultiUserUI"/>
<java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
+ <java-symbol type="bool" name="config_windowIsRound" />
<java-symbol type="integer" name="config_cursorWindowSize" />
<java-symbol type="integer" name="config_extraFreeKbytesAdjust" />
@@ -1229,6 +1230,7 @@
<java-symbol type="xml" name="sms_short_codes" />
<java-symbol type="xml" name="audio_assets" />
<java-symbol type="xml" name="global_keys" />
+ <java-symbol type="xml" name="default_zen_mode_config" />
<java-symbol type="raw" name="accessibility_gestures" />
<java-symbol type="raw" name="incognito_mode_start_page" />
@@ -1440,6 +1442,7 @@
<java-symbol type="array" name="radioAttributes" />
<java-symbol type="array" name="config_oemUsbModeOverride" />
<java-symbol type="array" name="config_locationProviderPackageNames" />
+ <java-symbol type="array" name="config_testLocationProviders" />
<java-symbol type="array" name="config_defaultNotificationVibePattern" />
<java-symbol type="array" name="config_notificationFallbackVibePattern" />
<java-symbol type="array" name="config_onlySingleDcAllowed" />
@@ -1624,6 +1627,7 @@
<java-symbol type="bool" name="config_powerDecoupleAutoSuspendModeFromDisplay" />
<java-symbol type="bool" name="config_powerDecoupleInteractiveModeFromDisplay" />
<java-symbol type="string" name="config_customAdbPublicKeyConfirmationComponent" />
+ <java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
<java-symbol type="layout" name="resolver_list" />
<java-symbol type="id" name="resolver_list" />
diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml
index a28496e..c2e31f4 100644
--- a/core/res/res/values/themes_quantum.xml
+++ b/core/res/res/values/themes_quantum.xml
@@ -317,7 +317,7 @@
<item name="dividerVertical">?attr/listDivider</item>
<item name="dividerHorizontal">?attr/listDivider</item>
<item name="buttonBarStyle">@style/Widget.Quantum.ButtonBar</item>
- <item name="buttonBarButtonStyle">?attr/borderlessButtonStyle</item>
+ <item name="buttonBarButtonStyle">@style/Widget.Quantum.Button.Borderless.Small</item>
<item name="segmentedButtonStyle">@style/Widget.Quantum.SegmentedButton</item>
<!-- SearchView attributes -->
@@ -662,7 +662,7 @@
<item name="dividerVertical">?attr/listDivider</item>
<item name="dividerHorizontal">?attr/listDivider</item>
<item name="buttonBarStyle">@style/Widget.Quantum.Light.ButtonBar</item>
- <item name="buttonBarButtonStyle">?attr/borderlessButtonStyle</item>
+ <item name="buttonBarButtonStyle">@style/Widget.Quantum.Light.Button.Borderless.Small</item>
<item name="segmentedButtonStyle">@style/Widget.Quantum.Light.SegmentedButton</item>
<!-- SearchView attributes -->
diff --git a/media/java/android/media/session/MediaSessionToken.aidl b/core/res/res/xml/default_zen_mode_config.xml
similarity index 62%
copy from media/java/android/media/session/MediaSessionToken.aidl
copy to core/res/res/xml/default_zen_mode_config.xml
index 5812682..1bdc1ec 100644
--- a/media/java/android/media/session/MediaSessionToken.aidl
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -1,4 +1,7 @@
-/* Copyright 2014, The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
@@ -12,7 +15,10 @@
** See the License for the specific language governing permissions and
** limitations under the License.
*/
+-->
-package android.media.session;
-
-parcelable MediaSessionToken;
+<!-- Default configuration for zen mode. See android.service.notification.ZenModeConfig. -->
+<zen version="1">
+ <allow calls="false" messages="false" />
+ <sleep startHour="22" startMin="0" endHour="7" endMin="0" />
+</zen>
diff --git a/core/tests/bluetoothtests/AndroidManifest.xml b/core/tests/bluetoothtests/AndroidManifest.xml
index 60b6dc1..cbfa84d 100644
--- a/core/tests/bluetoothtests/AndroidManifest.xml
+++ b/core/tests/bluetoothtests/AndroidManifest.xml
@@ -31,5 +31,8 @@
<instrumentation android:name="android.bluetooth.BluetoothTestRunner"
android:targetPackage="com.android.bluetooth.tests"
android:label="Bluetooth Tests" />
+ <instrumentation android:name="android.bluetooth.BluetoothInstrumentation"
+ android:targetPackage="com.android.bluetooth.tests"
+ android:label="Bluetooth Test Utils" />
</manifest>
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
new file mode 100644
index 0000000..34393f9
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.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.bluetooth;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.Bundle;
+
+public class BluetoothInstrumentation extends Instrumentation {
+
+ private BluetoothTestUtils mUtils = null;
+ private BluetoothAdapter mAdapter = null;
+ private Bundle mArgs = null;
+
+ private BluetoothTestUtils getBluetoothTestUtils() {
+ if (mUtils == null) {
+ mUtils = new BluetoothTestUtils(getContext(),
+ BluetoothInstrumentation.class.getSimpleName());
+ }
+ return mUtils;
+ }
+
+ private BluetoothAdapter getBluetoothAdapter() {
+ if (mAdapter == null) {
+ mAdapter = ((BluetoothManager)getContext().getSystemService(
+ Context.BLUETOOTH_SERVICE)).getAdapter();
+ }
+ return mAdapter;
+ }
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ super.onCreate(arguments);
+ mArgs = arguments;
+ start();
+ }
+
+ @Override
+ public void onStart() {
+ String command = mArgs.getString("command");
+ if ("enable".equals(command)) {
+ enable();
+ } else if ("disable".equals(command)) {
+ disable();
+ } else if ("unpairAll".equals(command)) {
+ unpairAll();
+ } else if ("getName".equals(command)) {
+ getName();
+ } else {
+ finish(null);
+ }
+ }
+
+ public void enable() {
+ getBluetoothTestUtils().enable(getBluetoothAdapter());
+ finish(null);
+ }
+
+ public void disable() {
+ getBluetoothTestUtils().disable(getBluetoothAdapter());
+ finish(null);
+ }
+
+ public void unpairAll() {
+ getBluetoothTestUtils().unpairAll(getBluetoothAdapter());
+ finish(null);
+ }
+
+ public void getName() {
+ String name = getBluetoothAdapter().getName();
+ Bundle bundle = new Bundle();
+ bundle.putString("name", name);
+ finish(bundle);
+ }
+
+ public void finish(Bundle result) {
+ if (result == null) {
+ result = new Bundle();
+ }
+ finish(Activity.RESULT_OK, result);
+ }
+}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
index 4858be8..8fbd214 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -34,6 +34,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
public class BluetoothTestUtils extends Assert {
@@ -893,6 +894,17 @@
}
/**
+ * Deletes all pairings of remote devices
+ * @param adapter the BT adapter
+ */
+ public void unpairAll(BluetoothAdapter adapter) {
+ Set<BluetoothDevice> devices = adapter.getBondedDevices();
+ for (BluetoothDevice device : devices) {
+ unpair(adapter, device);
+ }
+ }
+
+ /**
* Connects a profile from the local device to a remote device and checks to make sure that the
* profile is connected and that the correct actions were broadcast.
*
diff --git a/core/tests/coretests/src/android/net/NetworkScorerApplicationTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
similarity index 95%
rename from core/tests/coretests/src/android/net/NetworkScorerApplicationTest.java
rename to core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index 6d5ede8..cac6b93 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerApplicationTest.java
+++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
@@ -33,7 +33,7 @@
import java.util.Iterator;
-public class NetworkScorerApplicationTest extends InstrumentationTestCase {
+public class NetworkScorerAppManagerTest extends InstrumentationTestCase {
@Mock private Context mMockContext;
@Mock private PackageManager mMockPm;
@@ -64,7 +64,7 @@
setScorers(package1, package2, package3);
Iterator<String> result =
- NetworkScorerApplication.getAllValidScorers(mMockContext).iterator();
+ NetworkScorerAppManager.getAllValidScorers(mMockContext).iterator();
assertTrue(result.hasNext());
assertEquals("package1", result.next());
diff --git a/data/keyboards/Vendor_1949_Product_0401.kl b/data/keyboards/Vendor_1949_Product_0401.kl
new file mode 100644
index 0000000..ab24bcd
--- /dev/null
+++ b/data/keyboards/Vendor_1949_Product_0401.kl
@@ -0,0 +1,27 @@
+# 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.
+
+# Amazon Fire TV remote
+
+key 103 DPAD_UP
+key 108 DPAD_DOWN
+key 105 DPAD_LEFT
+key 106 DPAD_RIGHT
+key 96 DPAD_CENTER
+key 158 BACK
+key 172 HOME
+key 168 MEDIA_REWIND
+key 208 MEDIA_FAST_FORWARD
+key 164 MEDIA_PLAY_PAUSE
+key 217 ASSIST
diff --git a/docs/html/design/media/dialogs_examples.png b/docs/html/design/media/dialogs_examples.png
index c136476..6ffcee2 100644
--- a/docs/html/design/media/dialogs_examples.png
+++ b/docs/html/design/media/dialogs_examples.png
Binary files differ
diff --git a/docs/html/design/media/navigation_drawer_titles_icons.png b/docs/html/design/media/navigation_drawer_titles_icons.png
index 7cf1e0c..902a72d 100644
--- a/docs/html/design/media/navigation_drawer_titles_icons.png
+++ b/docs/html/design/media/navigation_drawer_titles_icons.png
Binary files differ
diff --git a/docs/html/design/media/selection_adjusting_actions.png b/docs/html/design/media/selection_adjusting_actions.png
index 0799b6b..32a7fec 100644
--- a/docs/html/design/media/selection_adjusting_actions.png
+++ b/docs/html/design/media/selection_adjusting_actions.png
Binary files differ
diff --git a/docs/html/design/media/touch_feedback_communication.png b/docs/html/design/media/touch_feedback_communication.png
index f8162d0..1d4a9dc 100644
--- a/docs/html/design/media/touch_feedback_communication.png
+++ b/docs/html/design/media/touch_feedback_communication.png
Binary files differ
diff --git a/docs/html/design/media/ui_overview_notifications.png b/docs/html/design/media/ui_overview_notifications.png
index 6043412..7975657 100644
--- a/docs/html/design/media/ui_overview_notifications.png
+++ b/docs/html/design/media/ui_overview_notifications.png
Binary files differ
diff --git a/docs/html/guide/components/intents-common.jd b/docs/html/guide/components/intents-common.jd
index 826dcff..a0f7ce1 100644
--- a/docs/html/guide/components/intents-common.jd
+++ b/docs/html/guide/components/intents-common.jd
@@ -56,6 +56,7 @@
<li><a href="#Music">Music or Video</a>
<ol>
<li><a href="#PlayMedia">Play a media file</a></li>
+ <li><a href="#PlaySearch">Play music based on a search query</a></li>
</ol>
</li>
<li><a href="#Phone">Phone</a>
@@ -1287,9 +1288,251 @@
</pre>
+<h3 id="PlaySearch">Play music based on a search query</h3>
+
+<p>To play music based on a search query, use the
+{@link android.provider.MediaStore#INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH} intent. An app may fire
+this intent in response to the user's voice command to play music. The receiving app for this
+intent performs a search within its inventory to match existing content to the given query and
+starts playing that content.</p>
+
+<p>This intent should include the {@link android.provider.MediaStore#EXTRA_MEDIA_FOCUS} string
+extra, which specifies the inteded search mode. For example, the search mode can specify whether
+the search is for an artist name or song name.</p>
+
+<dl>
+<dt><b>Action</b></dt>
+<dd>{@link android.provider.MediaStore#INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH}</dd>
+
+<dt><b>Data URI Scheme</b></dt>
+<dd>None</dd>
+
+<dt><b>MIME Type</b></dt>
+<dd>None</dd>
+
+<dt><b>Extras</b></dt>
+<dd>
+<dl>
+<dt>{@link android.provider.MediaStore#EXTRA_MEDIA_FOCUS MediaStore.EXTRA_MEDIA_FOCUS} (required)</dt>
+<dd>
+<p>Indicates the search mode (whether the user is looking for a particular artist, album, song,
+playlist, or radio channel). Most search modes take additional extras. For example, if the user
+is interested in listening to a particular song, the intent might have three additional extras:
+the song title, the artist, and the album. This intent supports the following search modes for
+each value of {@link android.provider.MediaStore#EXTRA_MEDIA_FOCUS}:</p>
+<dl>
+<dt><p><em>Any</em> - <code>"vnd.android.cursor.item/*"</p></code></dt>
+<dd>
+<p>Play any music. The receiving app should play some music based on a smart choice, such
+as the last playlist the user listened to.</p>
+<p>Additional extras:</p>
+<ul>
+ <li>{@link android.app.SearchManager#QUERY} (required) - An empty string. This extra is always
+ provided for backward compatibility: existing apps that do not know about search modes can
+ process this intent as an unstructured search.</li>
+</ul>
+</dd>
+<dt><p><em>Unstructured</em> - <code>"vnd.android.cursor.item/*"</code></p></dt>
+<dd>
+<p>Play a particular song, album or genre from an unstructured search query. Apps may generate
+an intent with this search mode when they can't identify the type of content the user wants to
+listen to. Apps should use more specific search modes when possible.</p>
+<p>Additional extras:</p>
+<ul>
+ <li>{@link android.app.SearchManager#QUERY} (required) - A string that contains any combination
+ of: the artist, the album, the song name, or the genre.</li>
+</ul>
+</dd>
+<dt><p><em>Genre</em> -
+{@link android.provider.MediaStore.Audio.Genres#ENTRY_CONTENT_TYPE Audio.Genres.ENTRY_CONTENT_TYPE}</p></dt>
+<dd>
+<p>Play music of a particular genre.</p>
+<p>Additional extras:</p>
+<ul>
+ <li><code>"android.intent.extra.genre"</code> (required) - The genre.</li>
+ <li>{@link android.app.SearchManager#QUERY} (required) - The genre. This extra is always provided
+ for backward compatibility: existing apps that do not know about search modes can process
+ this intent as an unstructured search.</li>
+</ul>
+</dd>
+<dt><p><em>Artist</em> -
+{@link android.provider.MediaStore.Audio.Artists#ENTRY_CONTENT_TYPE Audio.Artists.ENTRY_CONTENT_TYPE}</p></dt>
+<dd>
+<p>Play music from a particular artist.</p>
+<p>Additional extras:</p>
+<ul>
+ <li>{@link android.provider.MediaStore#EXTRA_MEDIA_ARTIST} (required) - The artist.</li>
+ <li><code>"android.intent.extra.genre"</code> - The genre.</li>
+ <li>{@link android.app.SearchManager#QUERY} (required) - A string that contains any combination of
+ the artist or the genre. This extra is always provided for backward compatibility:
+ existing apps that do not know about search modes can process this intent as an unstructured
+ search.</li>
+</ul>
+</dd>
+<dt><p><em>Album</em> -
+{@link android.provider.MediaStore.Audio.Albums#ENTRY_CONTENT_TYPE Audio.Albums.ENTRY_CONTENT_TYPE}</p></dt>
+<dd>
+<p>Play music from a particular album.</p>
+<p>Additional extras:</p>
+<ul>
+ <li>{@link android.provider.MediaStore#EXTRA_MEDIA_ALBUM} (required) - The album.</li>
+ <li>{@link android.provider.MediaStore#EXTRA_MEDIA_ARTIST} - The artist.</li>
+ <li><code>"android.intent.extra.genre"</code> - The genre.</li>
+ <li>{@link android.app.SearchManager#QUERY} (required) - A string that contains any combination of
+ the album or the artist. This extra is always provided for backward
+ compatibility: existing apps that do not know about search modes can process this intent as an
+ unstructured search.</li>
+</ul>
+</dd>
+<dt><p><em>Song</em> - <code>"vnd.android.cursor.item/audio"</code></p></dt>
+<dd>
+<p>Play a particular song.</p>
+<p>Additional extras:</p>
+<ul>
+ <li>{@link android.provider.MediaStore#EXTRA_MEDIA_ALBUM} - The album.</li>
+ <li>{@link android.provider.MediaStore#EXTRA_MEDIA_ARTIST} - The artist.</li>
+ <li><code>"android.intent.extra.genre"</code> - The genre.</li>
+ <li>{@link android.provider.MediaStore#EXTRA_MEDIA_TITLE} (required) - The song name.</li>
+ <li>{@link android.app.SearchManager#QUERY} (required) - A string that contains any combination of:
+ the album, the artist, the genre, or the title. This extra is always provided for
+ backward compatibility: existing apps that do not know about search modes can process this
+ intent as an unstructured search.</li>
+</ul>
+</dd>
+<dt><p><em>Radio channel</em> - <code>"vnd.android.cursor.item/radio"</code></p></dt>
+<dd>
+<p>Play a particular radio channel or a radio channel that matches some criteria specified
+by additional extras.</p>
+<p>Additional extras:</p>
+<ul>
+ <li>{@link android.provider.MediaStore#EXTRA_MEDIA_ALBUM} - The album.</li>
+ <li>{@link android.provider.MediaStore#EXTRA_MEDIA_ARTIST} - The artist.</li>
+ <li><code>"android.intent.extra.genre"</code> - The genre.</li>
+ <li><code>"android.intent.extra.radio_channel"</code> - The radio channel.</li>
+ <li>{@link android.provider.MediaStore#EXTRA_MEDIA_TITLE} - The song name that the radio
+ channel is based on.</li>
+ <li>{@link android.app.SearchManager#QUERY} (required) - A string that contains any combination
+ of: the album, the artist, the genre, the radio channel, or the title. This extra is
+ always provided for backward compatibility: existing apps that do not know about search
+ modes can process this intent as an unstructured search.</li>
+</ul>
+</dd>
+<dt><p><em>Playlist</em> - {@link android.provider.MediaStore.Audio.Playlists#ENTRY_CONTENT_TYPE Audio.Playlists.ENTRY_CONTENT_TYPE}</p></dt>
+<dd>
+<p>Play a particular playlist or a playlist that matches some criteria specified
+by additional extras.</p>
+<p>Additional extras:</p>
+<ul>
+ <li>{@link android.provider.MediaStore#EXTRA_MEDIA_ALBUM} - The album.</li>
+ <li>{@link android.provider.MediaStore#EXTRA_MEDIA_ARTIST} - The artist.</li>
+ <li><code>"android.intent.extra.genre"</code> - The genre.</li>
+ <li><code>"android.intent.extra.playlist"</code> - The playlist.</li>
+ <li>{@link android.provider.MediaStore#EXTRA_MEDIA_TITLE} - The song name that the playlist is
+ based on.</li>
+ <li>{@link android.app.SearchManager#QUERY} (required) - A string that contains any combination
+ of: the album, the artist, the genre, the playlist, or the title. This extra is always
+ provided for backward compatibility: existing apps that do not know about search modes can
+ process this intent as an unstructured search.</li>
+</ul>
+</dd>
+</dl>
+</dd>
+</dl>
+</dd>
+</dl>
+<p><b>Example intent:</b></p>
+<p>If the user wants to listen to a radio station that plays songs from a particular artist,
+a search app may generate the following intent:</p>
+<pre>
+public void playSearchRadioByArtist(String artist) {
+ Intent intent = new Intent(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH);
+ intent.putExtra(MediaStore.EXTRA_MEDIA_FOCUS,
+ "vnd.android.cursor.item/radio");
+ intent.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, artist);
+ intent.putExtra(SearchManager.QUERY, artist);
+ if (intent.resolveActivity(getPackageManager()) != null) {
+ startActivity(intent);
+ }
+}
+</pre>
+
+<p><b>Example intent filter:</b></p>
+<pre>
+<activity ...>
+ <intent-filter>
+ <action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+</activity>
+</pre>
+<p>When handling this intent, your activity should check the value of the
+{@link android.provider.MediaStore#EXTRA_MEDIA_FOCUS} extra in the incoming
+{@link android.content.Intent} to determine the search mode. Once your activity has identified
+the search mode, it should read the values of the additional extras for that particular search mode.
+With this information your app can then perform the search within its inventory to play the
+content that matches the search query. For example:</p>
+<pre>
+protected void onCreate(Bundle savedInstanceState) {
+ ...
+ Intent intent = this.getIntent();
+ if (intent.getAction().compareTo(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH) == 0) {
+
+ String mediaFocus = intent.getStringExtra(MediaStore.EXTRA_MEDIA_FOCUS);
+ String query = intent.getStringExtra(SearchManager.QUERY);
+
+ // Some of these extras may not be available depending on the search mode
+ String album = intent.getStringExtra(MediaStore.EXTRA_MEDIA_ALBUM);
+ String artist = intent.getStringExtra(MediaStore.EXTRA_MEDIA_ARTIST);
+ String genre = intent.getStringExtra("android.intent.extra.genre");
+ String playlist = intent.getStringExtra("android.intent.extra.playlist");
+ String rchannel = intent.getStringExtra("android.intent.extra.radio_channel");
+ String title = intent.getStringExtra(MediaStore.EXTRA_MEDIA_TITLE);
+
+ // Determine the search mode and use the corresponding extras
+ if (mediaFocus == null) {
+ // 'Unstructured' search mode (backward compatible)
+ playUnstructuredSearch(query);
+
+ } else if (mediaFocus.compareTo("vnd.android.cursor.item/*") == 0) {
+ if (query.isEmpty()) {
+ // 'Any' search mode
+ playResumeLastPlaylist();
+ } else {
+ // 'Unstructured' search mode
+ playUnstructuredSearch(query);
+ }
+
+ } else if (mediaFocus.compareTo(MediaStore.Audio.Genres.ENTRY_CONTENT_TYPE) == 0) {
+ // 'Genre' search mode
+ playGenre(genre);
+
+ } else if (mediaFocus.compareTo(MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE) == 0) {
+ // 'Artist' search mode
+ playArtist(artist, genre);
+
+ } else if (mediaFocus.compareTo(MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE) == 0) {
+ // 'Album' search mode
+ playAlbum(album, artist);
+
+ } else if (mediaFocus.compareTo("vnd.android.cursor.item/audio") == 0) {
+ // 'Song' search mode
+ playSong(album, artist, genre, title);
+
+ } else if (mediaFocus.compareTo("vnd.android.cursor.item/radio") == 0) {
+ // 'Radio channel' search mode
+ playRadioChannel(album, artist, genre, rchannel, title);
+
+ } else if (mediaFocus.compareTo(MediaStore.Audio.Playlists.ENTRY_CONTENT_TYPE) == 0) {
+ // 'Playlist' search mode
+ playPlaylist(album, artist, genre, playlist, title);
+ }
+ }
+}
+</pre>
+
diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd
index a122443..f169f5f 100644
--- a/docs/html/guide/topics/ui/actionbar.jd
+++ b/docs/html/guide/topics/ui/actionbar.jd
@@ -1145,7 +1145,7 @@
<item>Venus</item>
<item>Earth</item>
</string-array>
-</pre>
+</resources>
</pre>
<p>The {@link android.widget.ArrayAdapter} returned by {@link
diff --git a/docs/html/wear/index.jd b/docs/html/wear/index.jd
index a6a6460..659e9f2 100644
--- a/docs/html/wear/index.jd
+++ b/docs/html/wear/index.jd
@@ -121,13 +121,13 @@
</p>
</div>
<div class="col-3-wide">
- <img src="/wear/images/screens/circle_message2.png" alt="Image of a Hangouts message">
+ <img src="/wear/images/screens/circle_message2.png" itemprop="image" alt="" >
<p class="wear-small">
Get glanceable, actionable information at just the right time throughout the day.
</p>
</div>
<div class="col-3-wide">
- <img src="/wear/images/screens/fitness-24.png" alt="Image showing ">
+ <img src="/wear/images/screens/fitness-24.png" alt="">
<p class="wear-small">
A wide range of sensors is available to your applications, from accelerometers to heart rate monitors.
</p>
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 1252ee2..f80ce28 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -168,21 +168,10 @@
}
/**
- * Gets the native canvas pointer.
- *
- * @return The native pointer.
- *
- * @hide
- */
- public long getNativeCanvas() {
- return mNativeCanvas;
- }
-
- /**
* Returns null.
- *
+ *
* @deprecated This method is not supported and should not be invoked.
- *
+ *
* @hide
*/
@Deprecated
@@ -233,14 +222,14 @@
mBitmap = bitmap;
}
-
+
/**
* Set the viewport dimensions if this canvas is GL based. If it is not,
* this method is ignored and no exception is thrown.
*
* @param width The width of the viewport
* @param height The height of the viewport
- *
+ *
* @hide
*/
public void setViewport(int width, int height) {
@@ -389,7 +378,14 @@
paint != null ? paint.mNativePaint : 0,
saveFlags);
}
-
+
+ /**
+ * Convenience for saveLayer(bounds, paint, {@link #ALL_SAVE_FLAG})
+ */
+ public int saveLayer(RectF bounds, Paint paint) {
+ return saveLayer(bounds, paint, ALL_SAVE_FLAG);
+ }
+
/**
* Helper version of saveLayer() that takes 4 values rather than a RectF.
*/
@@ -401,6 +397,13 @@
}
/**
+ * Convenience for saveLayer(left, top, right, bottom, paint, {@link #ALL_SAVE_FLAG})
+ */
+ public int saveLayer(float left, float top, float right, float bottom, Paint paint) {
+ return saveLayer(left, top, right, bottom, paint, ALL_SAVE_FLAG);
+ }
+
+ /**
* This behaves the same as save(), but in addition it allocates an
* offscreen bitmap. All drawing calls are directed there, and only when
* the balancing call to restore() is made is that offscreen transfered to
@@ -420,7 +423,14 @@
alpha = Math.min(255, Math.max(0, alpha));
return native_saveLayerAlpha(mNativeCanvas, bounds, alpha, saveFlags);
}
-
+
+ /**
+ * Convenience for saveLayerAlpha(bounds, alpha, {@link #ALL_SAVE_FLAG})
+ */
+ public int saveLayerAlpha(RectF bounds, int alpha) {
+ return saveLayerAlpha(bounds, alpha, ALL_SAVE_FLAG);
+ }
+
/**
* Helper for saveLayerAlpha() that takes 4 values instead of a RectF.
*/
@@ -431,6 +441,13 @@
}
/**
+ * Helper for saveLayerAlpha(left, top, right, bottom, alpha, {@link #ALL_SAVE_FLAG})
+ */
+ public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha) {
+ return saveLayerAlpha(left, top, right, bottom, alpha, ALL_SAVE_FLAG);
+ }
+
+ /**
* This call balances a previous call to save(), and is used to remove all
* modifications to the matrix/clip state since the last save call. It is
* an error to call restore() more times than save() was called.
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 916cb5a..1e1128e 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -500,6 +500,7 @@
mBidiFlags = BIDI_DEFAULT_LTR;
setTextLocale(Locale.getDefault());
+ setElegantTextHeight(false);
}
/**
@@ -1221,6 +1222,22 @@
}
/**
+ * Get the elegant metrics flag.
+ *
+ * @return true if elegant metrics are enabled for text drawing.
+ */
+ public native boolean isElegantTextHeight();
+
+ /**
+ * Set the paint's elegant height metrics flag. This setting selects font
+ * variants that have not been compacted to fit Latin-based vertical
+ * metrics, and also increases top and bottom bounds to provide more space.
+ *
+ * @param elegant set the paint's elegant metrics flag for drawing text.
+ */
+ public native void setElegantTextHeight(boolean elegant);
+
+ /**
* Return the paint's text size.
*
* @return the paint's text size.
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 66a88a2..60b4615 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -484,6 +484,15 @@
copyBounds(mDstRect);
}
+ final int restoreAlpha;
+ if (state.mBaseAlpha != 1.0f) {
+ final Paint p = getPaint();
+ restoreAlpha = p.getAlpha();
+ p.setAlpha((int) (restoreAlpha * state.mBaseAlpha + 0.5f));
+ } else {
+ restoreAlpha = -1;
+ }
+
final boolean clearColorFilter;
if (mTintFilter != null && paint.getColorFilter() == null) {
paint.setColorFilter(mTintFilter);
@@ -537,6 +546,10 @@
if (clearColorFilter) {
paint.setColorFilter(null);
}
+
+ if (restoreAlpha >= 0) {
+ paint.setAlpha(restoreAlpha);
+ }
}
@Override
@@ -762,6 +775,10 @@
paint.setDither(dither);
}
+ if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_alpha] == 0) {
+ state.mBaseAlpha = a.getFloat(R.styleable.BitmapDrawable_alpha, 1.0f);
+ }
+
if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_gravity] == 0) {
final int gravity = a.getInt(
R.styleable.BitmapDrawable_gravity, Gravity.FILL);
@@ -818,6 +835,10 @@
paint.setDither(dither);
}
+ if (a.hasValue(R.styleable.BitmapDrawable_alpha)) {
+ state.mBaseAlpha = a.getFloat(R.styleable.BitmapDrawable_alpha, state.mBaseAlpha);
+ }
+
if (a.hasValue(R.styleable.BitmapDrawable_gravity)) {
final int gravity = a.getInt(
R.styleable.BitmapDrawable_gravity, Gravity.FILL);
@@ -933,6 +954,7 @@
int[] mThemeAttrs;
int mChangingConfigurations;
int mGravity = Gravity.FILL;
+ float mBaseAlpha = 1.0f;
Paint mPaint = new Paint(DEFAULT_PAINT_FLAGS);
Shader.TileMode mTileModeX = null;
Shader.TileMode mTileModeY = null;
@@ -954,6 +976,7 @@
mTileModeX = bitmapState.mTileModeX;
mTileModeY = bitmapState.mTileModeY;
mTargetDensity = bitmapState.mTargetDensity;
+ mBaseAlpha = bitmapState.mBaseAlpha;
mPaint = new Paint(bitmapState.mPaint);
mRebuildShader = bitmapState.mRebuildShader;
mAutoMirrored = bitmapState.mAutoMirrored;
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 05df3bc..1f8b51d 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -238,6 +238,35 @@
}
@Override
+ public void setHotspot(int key, float x, float y) {
+ if (mCurrDrawable != null) {
+ mCurrDrawable.setHotspot(key, x, y);
+ }
+ }
+
+ @Override
+ public void removeHotspot(int key) {
+ if (mCurrDrawable != null) {
+ mCurrDrawable.removeHotspot(key);
+ }
+ }
+
+ @Override
+ public void clearHotspots() {
+ if (mCurrDrawable != null) {
+ mCurrDrawable.clearHotspots();
+ }
+ }
+
+ @Override
+ public boolean supportsHotspots() {
+ if (mCurrDrawable != null) {
+ return mCurrDrawable.supportsHotspots();
+ }
+ return false;
+ }
+
+ @Override
protected boolean onStateChange(int[] state) {
if (mLastDrawable != null) {
return mLastDrawable.setState(state);
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 3e9ca0a..21992ce 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -241,6 +241,15 @@
canvas.scale(-1.0f, 1.0f);
}
+ final int restoreAlpha;
+ if (mNinePatchState.mBaseAlpha != 1.0f) {
+ final Paint p = getPaint();
+ restoreAlpha = p.getAlpha();
+ p.setAlpha((int) (restoreAlpha * mNinePatchState.mBaseAlpha + 0.5f));
+ } else {
+ restoreAlpha = -1;
+ }
+
mNinePatch.draw(canvas, bounds, mPaint);
if (needsMirroring) {
@@ -250,6 +259,10 @@
if (clearColorFilter) {
mPaint.setColorFilter(null);
}
+
+ if (restoreAlpha >= 0) {
+ mPaint.setAlpha(restoreAlpha);
+ }
}
@Override
@@ -491,6 +504,10 @@
}
}
+ if (themeAttrs == null || themeAttrs[R.styleable.NinePatchDrawable_alpha] == 0) {
+ ninePatchState.mBaseAlpha = a.getFloat(R.styleable.NinePatchDrawable_alpha, 1.0f);
+ }
+
// Apply the constant state to the paint.
initializeWithState(ninePatchState, r);
@@ -584,6 +601,10 @@
}
}
+ if (a.hasValue(R.styleable.NinePatchDrawable_alpha)) {
+ state.mBaseAlpha = a.getFloat(R.styleable.NinePatchDrawable_alpha, 1.0f);
+ }
+
// Apply the constant state to the paint.
initializeWithState(state, r);
@@ -689,12 +710,13 @@
Mode mTintMode = Mode.SRC_IN;
Rect mPadding;
Insets mOpticalInsets;
+ float mBaseAlpha = 1.0f;
boolean mDither;
int[] mThemeAttrs;
int mChangingConfigurations;
int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
boolean mAutoMirrored;
-
+
NinePatchState() {
// Empty constructor.
}
@@ -726,6 +748,7 @@
mThemeAttrs = state.mThemeAttrs;
mPadding = state.mPadding;
mOpticalInsets = state.mOpticalInsets;
+ mBaseAlpha = state.mBaseAlpha;
mDither = state.mDither;
mChangingConfigurations = state.mChangingConfigurations;
mTargetDensity = state.mTargetDensity;
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index 03dd841..796da50 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -62,27 +62,31 @@
/** Whether the center is within the parent bounds. */
private boolean mInside;
+
+ /** Whether to pulse this ripple. */
+ boolean mPulse;
/** Enter state. A value in [0...1] or -1 if not set. */
- private float mEnterState = -1;
+ float mEnterState = -1;
/** Exit state. A value in [0...1] or -1 if not set. */
- private float mExitState = -1;
+ float mExitState = -1;
/** Outside state. A value in [0...1] or -1 if not set. */
- private float mOutsideState = -1;
+ float mOutsideState = -1;
/** Pulse state. A value in [0...1] or -1 if not set. */
- private float mPulseState = -1;
+ float mPulseState = -1;
/**
* Creates a new ripple with the specified parent bounds, padding, initial
* position, and screen density.
*/
- public Ripple(Rect bounds, Rect padding, float x, float y, float density) {
+ public Ripple(Rect bounds, Rect padding, float x, float y, float density, boolean pulse) {
mBounds = bounds;
mPadding = padding;
mInside = mBounds.contains((int) x, (int) y);
+ mPulse = pulse;
mX = x;
mY = y;
@@ -115,6 +119,16 @@
}
}
+ public void onBoundsChanged() {
+ final boolean inside = mBounds.contains((int) mX, (int) mY);
+ if (mInside != inside) {
+ if (mAnimator != null) {
+ mAnimator.outside();
+ }
+ mInside = inside;
+ }
+ }
+
public RippleAnimator animate() {
if (mAnimator == null) {
mAnimator = new RippleAnimator(this);
@@ -308,9 +322,11 @@
MathUtils.constrain((currentTime - mOutsideTime) / (float) OUTSIDE_DURATION, 0, 1));
// Pulse is a little more complicated.
- final long pulseTime = (currentTime - mEnterTime - ENTER_DURATION - PULSE_DELAY);
- mTarget.mPulseState = pulseTime < 0 ? -1
- : (pulseTime % (PULSE_INTERVAL + PULSE_DURATION)) / (float) PULSE_DURATION;
+ if (mTarget.mPulse) {
+ final long pulseTime = (currentTime - mEnterTime - ENTER_DURATION - PULSE_DELAY);
+ mTarget.mPulseState = pulseTime < 0 ? -1
+ : (pulseTime % (PULSE_INTERVAL + PULSE_DURATION)) / (float) PULSE_DURATION;
+ }
}
}
}
diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
index 3773a49..813d755c 100644
--- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
+++ b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
@@ -39,11 +39,14 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.Arrays;
/**
* Documentation pending.
*/
public class TouchFeedbackDrawable extends LayerDrawable {
+ private static final PorterDuffXfermode DST_IN = new PorterDuffXfermode(Mode.DST_IN);
+
/** The maximum number of ripples supported. */
private static final int MAX_RIPPLES = 10;
@@ -108,6 +111,34 @@
return false;
}
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+
+ final int N = mActiveRipplesCount;
+ for (int i = 0; i < N; i++) {
+ mActiveRipples[i].onBoundsChanged();
+ }
+ }
+
+ @Override
+ public boolean setVisible(boolean visible, boolean restart) {
+ if (!visible) {
+ if (mTouchedRipples != null) {
+ mTouchedRipples.clear();
+ }
+
+ if (mActiveRipplesCount > 0) {
+ Arrays.fill(mActiveRipples, null);
+ mActiveRipplesCount = 0;
+ mAnimating = false;
+ unscheduleSelf(mAnimationRunnable);
+ }
+ }
+
+ return super.setVisible(visible, restart);
+ }
+
/**
* @hide
*/
@@ -270,7 +301,9 @@
y = bounds.exactCenterY();
}
- final Ripple newRipple = new Ripple(bounds, padding, x, y, mDensity);
+ // TODO: Clean this up in the API.
+ final boolean pulse = (id != R.attr.state_focused);
+ final Ripple newRipple = new Ripple(bounds, padding, x, y, mDensity, pulse);
newRipple.animate().enter();
mActiveRipples[mActiveRipplesCount++] = newRipple;
@@ -397,7 +430,7 @@
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(mState.mTintXfermode), 0);
+ bounds.bottom, getMaskingPaint(DST_IN), 0);
mask.draw(canvas);
}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 736b143..0992717 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -182,14 +182,14 @@
public VectorDrawable() {
mVectorState = new VectorDrawableState(null);
- mVectorState.mBasicAnimator = ObjectAnimator.ofFloat(this, "AnimationFraction", 0, 1);
+ mVectorState.mBasicAnimator = ObjectAnimator.ofFloat(this, "AnimationFraction", 0, 0);
setDuration(DEFAULT_DURATION);
}
private VectorDrawable(VectorDrawableState state, Resources res, Theme theme) {
mVectorState = new VectorDrawableState(state);
- mVectorState.mBasicAnimator = ObjectAnimator.ofFloat(this, "AnimationFraction", 0, 1);
+ mVectorState.mBasicAnimator = ObjectAnimator.ofFloat(this, "AnimationFraction", 0, 0);
if (theme != null && canApplyTheme()) {
applyTheme(theme);
@@ -213,7 +213,7 @@
@Override
public void jumpToCurrentState() {
- mVectorState.mBasicAnimator.end();
+ stop();
}
/**
@@ -318,7 +318,7 @@
private void animateBackward() {
if (!mVectorState.mBasicAnimator.isStarted()) {
- mVectorState.mBasicAnimator.setFloatValues(.99f, 0);
+ mVectorState.mBasicAnimator.setFloatValues(1, 0);
start();
}
}
@@ -985,7 +985,13 @@
for (int j = 0; j < sp.length; j++) {
mSeqMap.add(sp[j].trim());
- VectorDrawable.VPath path = groups.get(j).get(sp[j]);
+
+ final VectorDrawable.VPath path = groups.get(j).get(sp[j]);
+ if (path == null) {
+ throw new XmlPullParserException(a.getPositionDescription()
+ + " missing path with name: " + sp[j]);
+ }
+
path.mAnimated = true;
paths[j] = path;
}
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 08829ef..58ec32d 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -34,7 +34,7 @@
#define NONZERO_EPSILON .001f
static inline bool is_zero(float value) {
- return (value >= -NONZERO_EPSILON) || (value <= NONZERO_EPSILON);
+ return (value >= -NONZERO_EPSILON) && (value <= NONZERO_EPSILON);
}
namespace android {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 4ed73c3..5ce7ba6 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -91,7 +91,8 @@
void destroy();
bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; }
- void makeCurrent(EGLSurface surface);
+ // Returns true if the current surface changed, false if it was already current
+ bool makeCurrent(EGLSurface surface);
void beginFrame(EGLSurface surface, EGLint* width, EGLint* height);
void swapBuffers(EGLSurface surface);
@@ -250,8 +251,8 @@
mCurrentSurface = EGL_NO_SURFACE;
}
-void GlobalContext::makeCurrent(EGLSurface surface) {
- if (isCurrent(surface)) return;
+bool GlobalContext::makeCurrent(EGLSurface surface) {
+ if (isCurrent(surface)) return false;
if (surface == EGL_NO_SURFACE) {
// If we are setting EGL_NO_SURFACE we don't care about any of the potential
@@ -263,6 +264,7 @@
(void*)surface, egl_error_str());
}
mCurrentSurface = surface;
+ return true;
}
void GlobalContext::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) {
@@ -281,7 +283,6 @@
void GlobalContext::swapBuffers(EGLSurface surface) {
eglSwapBuffers(mEglDisplay, surface);
EGLint err = eglGetError();
- // TODO: Check whether we need to special case EGL_CONTEXT_LOST
LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS,
"Encountered EGL error %d %s during rendering", err, egl_error_str(err));
}
@@ -344,8 +345,8 @@
if (mEglSurface != EGL_NO_SURFACE) {
mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface);
- mGlobalContext->makeCurrent(mEglSurface);
mHaveNewSurface = true;
+ makeCurrent();
}
}
@@ -357,7 +358,7 @@
void CanvasContext::requireSurface() {
LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
"requireSurface() called but no surface set!");
- mGlobalContext->makeCurrent(mEglSurface);
+ makeCurrent();
}
bool CanvasContext::initialize(EGLNativeWindowType window) {
@@ -383,7 +384,9 @@
}
void CanvasContext::makeCurrent() {
- mGlobalContext->makeCurrent(mEglSurface);
+ // TODO: Figure out why this workaround is needed, see b/13913604
+ // In the meantime this matches the behavior of GLRenderer, so it is not a regression
+ mHaveNewSurface |= mGlobalContext->makeCurrent(mEglSurface);
}
void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters,
@@ -475,7 +478,7 @@
void CanvasContext::requireGlContext() {
if (mEglSurface != EGL_NO_SURFACE) {
- mGlobalContext->makeCurrent(mEglSurface);
+ makeCurrent();
} else {
mGlobalContext->usePBufferSurface();
}
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 1283e9b..2616b6c 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -217,7 +217,7 @@
if (mAudioManager.getStreamVolume(mStreamType) != 0) {
mLocalPlayer.start();
}
- } else if (mAllowRemote) {
+ } else if (mAllowRemote && (mRemotePlayer != null)) {
final Uri canonicalUri = mUri.getCanonicalUri();
try {
mRemotePlayer.play(mRemoteToken, canonicalUri, mStreamType);
@@ -239,7 +239,7 @@
public void stop() {
if (mLocalPlayer != null) {
destroyLocalPlayer();
- } else if (mAllowRemote) {
+ } else if (mAllowRemote && (mRemotePlayer != null)) {
try {
mRemotePlayer.stop(mRemoteToken);
} catch (RemoteException e) {
@@ -264,7 +264,7 @@
public boolean isPlaying() {
if (mLocalPlayer != null) {
return mLocalPlayer.isPlaying();
- } else if (mAllowRemote) {
+ } else if (mAllowRemote && (mRemotePlayer != null)) {
try {
return mRemotePlayer.isPlaying(mRemoteToken);
} catch (RemoteException e) {
diff --git a/media/java/android/media/session/IMediaSessionManager.aidl b/media/java/android/media/routeprovider/IRouteConnection.aidl
similarity index 65%
copy from media/java/android/media/session/IMediaSessionManager.aidl
copy to media/java/android/media/routeprovider/IRouteConnection.aidl
index 0b4328e..15c8039 100644
--- a/media/java/android/media/session/IMediaSessionManager.aidl
+++ b/media/java/android/media/routeprovider/IRouteConnection.aidl
@@ -13,16 +13,16 @@
* limitations under the License.
*/
-package android.media.session;
+package android.media.routeprovider;
-import android.media.session.IMediaSession;
-import android.media.session.IMediaSessionCallback;
-import android.os.Bundle;
+import android.media.session.RouteCommand;
+import android.os.ResultReceiver;
/**
- * Interface to the MediaSessionManagerService
+ * Interface for a specific connected route.
* @hide
*/
-interface IMediaSessionManager {
- IMediaSession createSession(String packageName, in IMediaSessionCallback cb, String tag);
+oneway interface IRouteConnection {
+ void onCommand(in RouteCommand command, in ResultReceiver cb);
+ void disconnect();
}
\ No newline at end of file
diff --git a/media/java/android/media/routeprovider/IRouteProvider.aidl b/media/java/android/media/routeprovider/IRouteProvider.aidl
new file mode 100644
index 0000000..c36f6a7
--- /dev/null
+++ b/media/java/android/media/routeprovider/IRouteProvider.aidl
@@ -0,0 +1,36 @@
+/* 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.media.routeprovider;
+
+import android.media.routeprovider.IRouteConnection;
+import android.media.routeprovider.IRouteProviderCallback;
+import android.media.routeprovider.RouteRequest;
+import android.media.session.RouteInfo;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+
+/**
+ * Interface to an app's RouteProviderService.
+ * @hide
+ */
+oneway interface IRouteProvider {
+ void registerCallback(in IRouteProviderCallback cb);
+ void unregisterCallback(in IRouteProviderCallback cb);
+ void updateDiscoveryRequests(in List<RouteRequest> requests);
+
+ void getAvailableRoutes(in List<RouteRequest> requests, in ResultReceiver cb);
+ void connect(in RouteInfo route, in RouteRequest request, in ResultReceiver cb);
+}
\ No newline at end of file
diff --git a/media/java/android/media/routeprovider/IRouteProviderCallback.aidl b/media/java/android/media/routeprovider/IRouteProviderCallback.aidl
new file mode 100644
index 0000000..9185347
--- /dev/null
+++ b/media/java/android/media/routeprovider/IRouteProviderCallback.aidl
@@ -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.
+ */
+
+package android.media.routeprovider;
+
+import android.media.routeprovider.IRouteConnection;
+import android.media.session.RouteEvent;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+
+/**
+ * System's provider callback interface.
+ * @hide
+ */
+oneway interface IRouteProviderCallback {
+ void onRoutesChanged();
+ void onConnectionStateChanged(in IRouteConnection connection, int state);
+ void onConnectionTerminated(in IRouteConnection connection);
+ void onRouteEvent(in RouteEvent event);
+}
\ No newline at end of file
diff --git a/media/java/android/media/routeprovider/RouteConnection.java b/media/java/android/media/routeprovider/RouteConnection.java
new file mode 100644
index 0000000..9214ff8
--- /dev/null
+++ b/media/java/android/media/routeprovider/RouteConnection.java
@@ -0,0 +1,164 @@
+/*
+ * 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.media.routeprovider;
+
+import android.media.routeprovider.IRouteConnection;
+import android.media.session.RouteCommand;
+import android.media.session.RouteEvent;
+import android.media.session.RouteInfo;
+import android.media.session.RouteInterface;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents an ongoing connection between an application and a media route
+ * offered by a media route provider.
+ * <p>
+ * The media route provider should add interfaces to the connection before
+ * returning it to the system in order to receive commands from clients on those
+ * interfaces. Use {@link #addRouteInterface(String)} to add an interface and
+ * {@link #getRouteInterface(String)} to retrieve the interface's handle anytime
+ * after it has been added.
+ */
+public final class RouteConnection {
+ private static final String TAG = "RouteConnection";
+ private final ConnectionStub mBinder;
+ private final ArrayList<String> mIfaceNames = new ArrayList<String>();
+ private final ArrayMap<String, RouteInterfaceHandler> mIfaces
+ = new ArrayMap<String, RouteInterfaceHandler>();
+ private final RouteProviderService mProvider;
+ private final RouteInfo mRoute;
+
+ private boolean mPublished;
+
+ /**
+ * Create a new connection for the given Provider and Route.
+ *
+ * @param provider The provider this route is associated with.
+ * @param route The route this is a connection to.
+ */
+ public RouteConnection(RouteProviderService provider, RouteInfo route) {
+ if (provider == null) {
+ throw new IllegalArgumentException("provider may not be null.");
+ }
+ if (route == null) {
+ throw new IllegalArgumentException("route may not be null.");
+ }
+ mBinder = new ConnectionStub(this);
+ mProvider = provider;
+ mRoute = route;
+ }
+
+ /**
+ * Add an interface to this route connection. All interfaces must be added
+ * to the connection before the connection is returned to the system.
+ *
+ * @param ifaceName The name of the interface to add
+ * @return The route interface that was registered
+ */
+ public RouteInterfaceHandler addRouteInterface(String ifaceName) {
+ if (TextUtils.isEmpty(ifaceName)) {
+ throw new IllegalArgumentException("The interface's name may not be empty");
+ }
+ if (mPublished) {
+ throw new IllegalStateException(
+ "Connection has already been published to the system.");
+ }
+ RouteInterfaceHandler iface = mIfaces.get(ifaceName);
+ if (iface == null) {
+ iface = new RouteInterfaceHandler(this, ifaceName);
+ mIfaceNames.add(ifaceName);
+ mIfaces.put(ifaceName, iface);
+ } else {
+ Log.w(TAG, "Attempted to add an interface that already exists");
+ }
+ return iface;
+ }
+
+ /**
+ * Get the interface instance for the specified interface name. If the
+ * interface was not added to this connection null will be returned.
+ *
+ * @param ifaceName The name of the interface to get.
+ * @return The route interface with that name or null.
+ */
+ public RouteInterfaceHandler getRouteInterface(String ifaceName) {
+ return mIfaces.get(ifaceName);
+ }
+
+ /**
+ * Close the connection and inform the system that it may no longer be used.
+ */
+ public void shutDown() {
+ mProvider.disconnect(this);
+ }
+
+ /**
+ * @hide
+ */
+ public void sendEvent(String iface, String event, Bundle extras) {
+ RouteEvent e = new RouteEvent(mBinder, iface, event, extras);
+ mProvider.sendRouteEvent(e);
+ }
+
+ /**
+ * @hide
+ */
+ IRouteConnection.Stub getBinder() {
+ return mBinder;
+ }
+
+ /**
+ * @hide
+ */
+ void publish() {
+ mPublished = true;
+ }
+
+ private static class ConnectionStub extends IRouteConnection.Stub {
+ private final WeakReference<RouteConnection> mConnection;
+
+ public ConnectionStub(RouteConnection connection) {
+ mConnection = new WeakReference<RouteConnection>(connection);
+ }
+
+ @Override
+ public void onCommand(RouteCommand command, ResultReceiver cb) {
+ RouteConnection connection = mConnection.get();
+ if (connection != null) {
+ RouteInterfaceHandler iface = connection.mIfaces.get(command.getIface());
+ if (iface != null) {
+ iface.onCommand(command.getEvent(), command.getExtras(), cb);
+ } else if (cb != null) {
+ cb.send(RouteInterface.RESULT_INTERFACE_NOT_SUPPORTED, null);
+ }
+ }
+ }
+
+ @Override
+ public void disconnect() {
+ // TODO
+ }
+ }
+}
diff --git a/media/java/android/media/routeprovider/RouteInterfaceHandler.java b/media/java/android/media/routeprovider/RouteInterfaceHandler.java
new file mode 100644
index 0000000..9693dc6
--- /dev/null
+++ b/media/java/android/media/routeprovider/RouteInterfaceHandler.java
@@ -0,0 +1,245 @@
+/*
+ * 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.media.routeprovider;
+
+import android.media.session.Route;
+import android.media.session.Session;
+import android.media.session.RouteInterface;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ResultReceiver;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * Represents an interface that an application may use to send requests to a
+ * connected media route.
+ * <p>
+ * A {@link RouteProviderService} may expose multiple interfaces on a
+ * {@link RouteConnection} for a {@link Session} to interact with. A
+ * provider creates an interface with
+ * {@link RouteConnection#addRouteInterface(String)} to allow messages to be
+ * routed appropriately. Events are then sent through a specific interface and
+ * all commands being sent on the interface will be sent to any registered
+ * {@link CommandListener}s.
+ * <p>
+ * An interface instance can only be registered on one {@link RouteConnection}.
+ * To use the same interface on multiple connections a new instance must be
+ * created for each connection.
+ * <p>
+ * It is recommended you wrap this interface with a standard implementation to
+ * avoid errors, but for simple interfaces this class may be used directly. TODO
+ * add link to sample code.
+ */
+public final class RouteInterfaceHandler {
+ private static final String TAG = "RouteInterfaceHandler";
+
+ private final Object mLock = new Object();
+ private final RouteConnection mConnection;
+ private final String mName;
+
+ private ArrayList<MessageHandler> mListeners = new ArrayList<MessageHandler>();
+
+ /**
+ * Create a new RouteInterface for a given connection. This can be used to
+ * send events on the given interface and register listeners for commands
+ * from the connected session.
+ *
+ * @param connection The connection this interface sends events on
+ * @param ifaceName The name of this interface
+ * @hide
+ */
+ public RouteInterfaceHandler(RouteConnection connection, String ifaceName) {
+ if (connection == null) {
+ throw new IllegalArgumentException("connection may not be null");
+ }
+ if (TextUtils.isEmpty(ifaceName)) {
+ throw new IllegalArgumentException("ifaceName can not be empty");
+ }
+ mConnection = connection;
+ mName = ifaceName;
+ }
+
+ /**
+ * Send an event on this interface to the connected session.
+ *
+ * @param event The event to send
+ * @param extras Any extras for the event
+ */
+ public void sendEvent(String event, Bundle extras) {
+ mConnection.sendEvent(mName, event, extras);
+ }
+
+ /**
+ * Send a result from a command to the specified callback. The result codes
+ * in {@link RouteInterface} must be used. More information
+ * about the result, whether successful or an error, should be included in
+ * the extras.
+ *
+ * @param cb The callback to send the result to
+ * @param resultCode The result code for the call
+ * @param extras Any extras to include
+ */
+ public static void sendResult(ResultReceiver cb, int resultCode, Bundle extras) {
+ if (cb != null) {
+ cb.send(resultCode, extras);
+ }
+ }
+
+ /**
+ * Add a listener for this interface. If a handler is specified callbacks
+ * will be performed on the handler's thread, otherwise the callers thread
+ * will be used.
+ *
+ * @param listener The listener to receive calls on.
+ * @param handler The handler whose thread to post calls on or null.
+ */
+ public void addListener(CommandListener listener, Handler handler) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener may not be null");
+ }
+ Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
+ synchronized (mLock) {
+ if (findIndexOfListenerLocked(listener) != -1) {
+ Log.d(TAG, "Listener is already added, ignoring");
+ return;
+ }
+ mListeners.add(new MessageHandler(looper, listener));
+ }
+ }
+
+ /**
+ * Remove a listener from this interface.
+ *
+ * @param listener The listener to stop receiving commands on.
+ */
+ public void removeListener(CommandListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener may not be null");
+ }
+ synchronized (mLock) {
+ int index = findIndexOfListenerLocked(listener);
+ if (index != -1) {
+ mListeners.remove(index);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void onCommand(String command, Bundle args, ResultReceiver cb) {
+ synchronized (mLock) {
+ Command cmd = new Command(command, args, cb);
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).post(MessageHandler.MSG_COMMAND, cmd);
+ }
+ }
+ }
+
+ /**
+ * Get the interface name.
+ *
+ * @return The name of this interface
+ */
+ public String getName() {
+ return mName;
+ }
+
+ private int findIndexOfListenerLocked(CommandListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("Callback cannot be null");
+ }
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ MessageHandler handler = mListeners.get(i);
+ if (listener == handler.mListener) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Handles commands sent to the interface.
+ * <p>
+ * Register an InterfaceListener using {@link #addListener}.
+ */
+ public abstract static class CommandListener {
+ /**
+ * This is called when a command is received that matches this
+ * interface. Commands are sent by a {@link Session} that is
+ * connected to the route this interface is registered with.
+ *
+ * @param iface The interface the command was received on.
+ * @param command The command or method to invoke.
+ * @param args Any args that were included with the command. May be
+ * null.
+ * @param cb The callback provided to send a response on. May be null.
+ * @return true if the command was handled, false otherwise. If the
+ * command was not handled an error will be sent automatically.
+ * true may be returned if the command will be handled
+ * asynchronously.
+ * @see Route
+ * @see Session
+ */
+ public abstract boolean onCommand(RouteInterfaceHandler iface, String command, Bundle args,
+ ResultReceiver cb);
+ }
+
+ private class MessageHandler extends Handler {
+ private static final int MSG_COMMAND = 1;
+
+ private final CommandListener mListener;
+
+ public MessageHandler(Looper looper, CommandListener listener) {
+ super(looper, null, true /* async */);
+ mListener = listener;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_COMMAND:
+ Command cmd = (Command) msg.obj;
+ if (!mListener.onCommand(RouteInterfaceHandler.this, cmd.command, cmd.args, cmd.cb)) {
+ sendResult(cmd.cb, RouteInterface.RESULT_COMMAND_NOT_SUPPORTED,
+ null);
+ }
+ break;
+ }
+ }
+
+ public void post(int what, Object obj) {
+ obtainMessage(what, obj).sendToTarget();
+ }
+ }
+
+ private final static class Command {
+ public final String command;
+ public final Bundle args;
+ public final ResultReceiver cb;
+
+ public Command(String command, Bundle args, ResultReceiver cb) {
+ this.command = command;
+ this.args = args;
+ this.cb = cb;
+ }
+ }
+}
diff --git a/media/java/android/media/routeprovider/RoutePlaybackControlsHandler.java b/media/java/android/media/routeprovider/RoutePlaybackControlsHandler.java
new file mode 100644
index 0000000..dcef79a
--- /dev/null
+++ b/media/java/android/media/routeprovider/RoutePlaybackControlsHandler.java
@@ -0,0 +1,221 @@
+/*
+ * 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.media.routeprovider;
+
+import android.media.session.RoutePlaybackControls;
+import android.media.session.RouteInterface;
+import android.media.session.PlaybackState;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * Standard wrapper for using playback controls over a {@link RouteInterfaceHandler}.
+ * This is the provider half of the interface. Sessions should use
+ * {@link RoutePlaybackControls} to interact with this interface.
+ */
+public final class RoutePlaybackControlsHandler {
+ private static final String TAG = "RoutePlaybackControls";
+
+ private final RouteInterfaceHandler mIface;
+
+ private RoutePlaybackControlsHandler(RouteInterfaceHandler iface) {
+ mIface = iface;
+ }
+
+ /**
+ * Add this interface to the specified route and return a handle for
+ * communicating on the interface.
+ *
+ * @param connection The connection to register this interface on.
+ * @return A handle for communicating on this interface.
+ */
+ public static RoutePlaybackControlsHandler addTo(RouteConnection connection) {
+ if (connection == null) {
+ throw new IllegalArgumentException("connection may not be null");
+ }
+ RouteInterfaceHandler iface = connection
+ .addRouteInterface(RoutePlaybackControls.NAME);
+
+ return new RoutePlaybackControlsHandler(iface);
+ }
+
+ /**
+ * Add a {@link Listener} to this interface. The listener will receive
+ * commands on the caller's thread.
+ *
+ * @param listener The listener to send commands to.
+ */
+ public void addListener(Listener listener) {
+ addListener(listener, null);
+ }
+
+ /**
+ * Add a {@link Listener} to this interface. The listener will receive
+ * updates on the handler's thread. If no handler is specified the caller's
+ * thread will be used instead.
+ *
+ * @param listener The listener to send commands to.
+ * @param handler The handler whose thread calls should be posted on. May be
+ * null.
+ */
+ public void addListener(Listener listener, Handler handler) {
+ mIface.addListener(listener, handler);
+ }
+
+ /**
+ * Remove a {@link Listener} from this interface.
+ *
+ * @param listener The Listener to remove.
+ */
+ public void removeListener(Listener listener) {
+ mIface.removeListener(listener);
+ }
+
+ /**
+ * Publish the current playback state to the system and any controllers.
+ * Valid values are defined in {@link PlaybackState}. TODO create
+ * RoutePlaybackState.
+ *
+ * @param state
+ */
+ public void sendPlaybackChangeEvent(int state) {
+ Bundle extras = new Bundle();
+ extras.putInt(RoutePlaybackControls.KEY_VALUE1, state);
+ mIface.sendEvent(RoutePlaybackControls.EVENT_PLAYSTATE_CHANGE, extras);
+ }
+
+ /**
+ * Command handler for the RoutePlaybackControls interface. You can add a
+ * Listener to the interface using {@link #addListener}.
+ */
+ public static abstract class Listener extends RouteInterfaceHandler.CommandListener {
+
+ @Override
+ public final boolean onCommand(RouteInterfaceHandler iface, String method, Bundle extras,
+ ResultReceiver cb) {
+ if (RoutePlaybackControls.CMD_FAST_FORWARD.equals(method)) {
+ boolean success = fastForward();
+ // TODO specify type of error
+ RouteInterfaceHandler.sendResult(cb, success
+ ? RouteInterface.RESULT_SUCCESS
+ : RouteInterface.RESULT_ERROR, null);
+ return true;
+ } else if (RoutePlaybackControls.CMD_GET_CURRENT_POSITION.equals(method)) {
+ Bundle result = new Bundle();
+ result.putLong(RoutePlaybackControls.KEY_VALUE1, getCurrentPosition());
+ RouteInterfaceHandler.sendResult(cb, RouteInterface.RESULT_SUCCESS,
+ result);
+ return true;
+ } else if (RoutePlaybackControls.CMD_GET_CAPABILITIES.equals(method)) {
+ Bundle result = new Bundle();
+ result.putLong(RoutePlaybackControls.KEY_VALUE1, getCapabilities());
+ RouteInterfaceHandler.sendResult(cb, RouteInterface.RESULT_SUCCESS,
+ result);
+ return true;
+ } else if (RoutePlaybackControls.CMD_PLAY_NOW.equals(method)) {
+ playNow(extras.getString(RoutePlaybackControls.KEY_VALUE1, null), cb);
+ return true;
+ } else if (RoutePlaybackControls.CMD_RESUME.equals(method)) {
+ boolean success = resume();
+ RouteInterfaceHandler.sendResult(cb, success
+ ? RouteInterface.RESULT_SUCCESS
+ : RouteInterface.RESULT_ERROR, null);
+ return true;
+ } else if (RoutePlaybackControls.CMD_PAUSE.equals(method)) {
+ boolean success = pause();
+ RouteInterfaceHandler.sendResult(cb, success
+ ? RouteInterface.RESULT_SUCCESS
+ : RouteInterface.RESULT_ERROR, null);
+ return true;
+ } else {
+ // The command wasn't recognized
+ }
+ return false;
+ }
+
+ /**
+ * Override to handle fast forwarding.
+ *
+ * @return true if the request succeeded, false otherwise
+ */
+ public boolean fastForward() {
+ Log.w(TAG, "fastForward is not supported.");
+ return false;
+ }
+
+ /**
+ * Override to handle getting the current position of playback in
+ * millis.
+ *
+ * @return The current position in millis or -1
+ */
+ public long getCurrentPosition() {
+ Log.w(TAG, "getCurrentPosition is not supported");
+ return -1;
+ }
+
+ /**
+ * Override to handle getting the set of capabilities currently
+ * available.
+ *
+ * @return A bit mask of the supported capabilities
+ */
+ public long getCapabilities() {
+ Log.w(TAG, "getCapabilities is not supported");
+ return 0;
+ }
+
+ /**
+ * Override to handle play now requests.
+ *
+ * @param content The uri of the item to play.
+ * @param cb The callback to send the result to.
+ */
+ public void playNow(String content, ResultReceiver cb) {
+ Log.w(TAG, "playNow is not supported");
+ if (cb != null) {
+ // We do this directly since we don't have a reference to the
+ // iface
+ cb.send(RouteInterface.RESULT_COMMAND_NOT_SUPPORTED, null);
+ }
+ }
+
+ /**
+ * Override to handle resume requests. Return true if the call was
+ * handled, even if it was a no-op.
+ *
+ * @return true if the call was handled.
+ */
+ public boolean resume() {
+ Log.w(TAG, "resume is not supported");
+ return false;
+ }
+
+ /**
+ * Override to handle pause requests. Return true if the call was
+ * handled, even if it was a no-op.
+ *
+ * @return true if the call was handled.
+ */
+ public boolean pause() {
+ Log.w(TAG, "pause is not supported");
+ return false;
+ }
+ }
+}
diff --git a/media/java/android/media/routeprovider/RouteProviderService.java b/media/java/android/media/routeprovider/RouteProviderService.java
new file mode 100644
index 0000000..6ebfb5b
--- /dev/null
+++ b/media/java/android/media/routeprovider/RouteProviderService.java
@@ -0,0 +1,227 @@
+/*
+ * 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.media.routeprovider;
+
+import android.app.Service;
+import android.content.Intent;
+import android.media.routeprovider.IRouteProvider;
+import android.media.routeprovider.IRouteProviderCallback;
+import android.media.session.RouteEvent;
+import android.media.session.RouteInfo;
+import android.media.session.RouteOptions;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base class for defining a route provider service.
+ * <p>
+ * A route provider offers media routes which represent destinations to which
+ * applications may connect, control, and send content. This provides a means
+ * for Android applications to interact with a variety of media streaming
+ * devices such as speakers or television sets.
+ * <p>
+ * The system will bind to your provider when an active app is interested in
+ * routes that may be discovered through your provider. After binding, the
+ * system will send updates on which routes to discover through
+ * {@link #updateDiscoveryRequests(List)}. The system will call
+ * {@link #getMatchingRoutes(List)} with a subset of filters when a route is
+ * needed for a specific app.
+ * <p>
+ * TODO add documentation for how the sytem knows an app is interested. Maybe
+ * interface declarations in the manifest.
+ * <p>
+ * The system will only start a provider when an app may discover routes through
+ * it. If your service needs to run at other times you are responsible for
+ * managing its lifecycle.
+ * <p>
+ * Declare your route provider service in your application manifest like this:
+ * <p>
+ *
+ * <pre>
+ * <service android:name=".MyRouteProviderService"
+ * android:label="@string/my_route_provider_service">
+ * <intent-filter>
+ * <action android:name="com.android.media.session.MediaRouteProvider" />
+ * </intent-filter>
+ * </service>
+ * </pre>
+ */
+public abstract class RouteProviderService extends Service {
+ private static final String TAG = "RouteProvider";
+ /**
+ * A service that implements a RouteProvider must declare that it handles
+ * this action in its AndroidManifest.
+ */
+ public static final String SERVICE_INTERFACE =
+ "com.android.media.session.MediaRouteProvider";
+
+ /**
+ * @hide
+ */
+ public static final String KEY_ROUTES = "routes";
+ /**
+ * @hide
+ */
+ public static final String KEY_CONNECTION = "connection";
+ /**
+ * @hide
+ */
+ public static final int RESULT_FAILURE = -1;
+ /**
+ * @hide
+ */
+ public static final int RESULT_SUCCESS = 0;
+
+ // The system's callback once it has bound to the service
+ private IRouteProviderCallback mCb;
+
+ /**
+ * If your service overrides onBind it must return super.onBind() in
+ * response to the {@link #SERVICE_INTERFACE} action.
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (intent != null && RouteProviderService.SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mBinder;
+ }
+ return null;
+ }
+
+ /**
+ * Disconnect the specified RouteConnection. The system will stop sending
+ * commands to this connection.
+ *
+ * @param connection The connection to disconnect.
+ * @hide
+ */
+ public final void disconnect(RouteConnection connection) {
+ if (mCb != null) {
+ try {
+ mCb.onConnectionTerminated(connection.getBinder());
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error in disconnect.", e);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public final void sendRouteEvent(RouteEvent event) {
+ if (mCb != null) {
+ try {
+ mCb.onRouteEvent(event);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Unable to send MediaRouteEvent to system", e);
+ }
+ }
+ }
+
+ /**
+ * Override to handle updates to the routes that are of interest. Each
+ * {@link RouteRequest} will specify if it is an active or passive request.
+ * Route discovery may perform more aggressive discovery on behalf of active
+ * requests but should use low power discovery methods otherwise.
+ * <p>
+ * A single app may have more than one request. Your provider is responsible
+ * for deciding the set of features that are important for discovery given
+ * the set of requests. If your provider only has one method of discovery it
+ * may simply verify that one or more requests are valid before starting
+ * discovery.
+ *
+ * @param requests The route requests that are currently relevant.
+ */
+ public void updateDiscoveryRequests(List<RouteRequest> requests) {
+ }
+
+ /**
+ * Return a list of matching routes for the given set of requests. Returning
+ * null or an empty list indicates there are no matches. A route is
+ * considered matching if it supports one or more of the
+ * {@link RouteOptions} specified. Each returned {@link RouteInfo}
+ * should include all the requested connections that it supports.
+ *
+ * @param options The set of requests for routes
+ * @return The routes that this caller may connect to using one or more of
+ * the route options.
+ */
+ public abstract List<RouteInfo> getMatchingRoutes(List<RouteRequest> options);
+
+ /**
+ * Handle a request to connect to a specific route with a specific request.
+ * The {@link RouteConnection} must be fully defined before being returned,
+ * though the actual connection to the route may be performed in the
+ * background.
+ *
+ * @param route The route to connect to
+ * @param request The connection request parameters
+ * @return A MediaRouteConnection representing the connection to the route
+ */
+ public abstract RouteConnection connect(RouteInfo route, RouteRequest request);
+
+ private IRouteProvider.Stub mBinder = new IRouteProvider.Stub() {
+
+ @Override
+ public void registerCallback(IRouteProviderCallback cb) throws RemoteException {
+ mCb = cb;
+ }
+
+ @Override
+ public void unregisterCallback(IRouteProviderCallback cb) throws RemoteException {
+ mCb = null;
+ }
+
+ @Override
+ public void updateDiscoveryRequests(List<RouteRequest> requests)
+ throws RemoteException {
+ RouteProviderService.this.updateDiscoveryRequests(requests);
+ }
+
+ @Override
+ public void getAvailableRoutes(List<RouteRequest> requests, ResultReceiver cb)
+ throws RemoteException {
+ List<RouteInfo> routes = RouteProviderService.this.getMatchingRoutes(requests);
+ ArrayList<RouteInfo> routesArray;
+ if (routes instanceof ArrayList) {
+ routesArray = (ArrayList<RouteInfo>) routes;
+ } else {
+ routesArray = new ArrayList<RouteInfo>(routes);
+ }
+ Bundle resultData = new Bundle();
+ resultData.putParcelableArrayList(KEY_ROUTES, routesArray);
+ cb.send(routes == null ? RESULT_FAILURE : RESULT_SUCCESS, resultData);
+ }
+
+ @Override
+ public void connect(RouteInfo route, RouteRequest request, ResultReceiver cb)
+ throws RemoteException {
+ RouteConnection connection = RouteProviderService.this.connect(route, request);
+ Bundle resultData = new Bundle();
+ if (connection != null) {
+ connection.publish();
+ resultData.putBinder(KEY_CONNECTION, connection.getBinder());
+ }
+
+ cb.send(connection == null ? RESULT_FAILURE : RESULT_SUCCESS, resultData);
+ }
+ };
+}
diff --git a/media/java/android/media/session/MediaSessionToken.aidl b/media/java/android/media/routeprovider/RouteRequest.aidl
similarity index 90%
copy from media/java/android/media/session/MediaSessionToken.aidl
copy to media/java/android/media/routeprovider/RouteRequest.aidl
index 5812682..7bc5722 100644
--- a/media/java/android/media/session/MediaSessionToken.aidl
+++ b/media/java/android/media/routeprovider/RouteRequest.aidl
@@ -13,6 +13,6 @@
** limitations under the License.
*/
-package android.media.session;
+package android.media.routeprovider;
-parcelable MediaSessionToken;
+parcelable RouteRequest;
diff --git a/media/java/android/media/routeprovider/RouteRequest.java b/media/java/android/media/routeprovider/RouteRequest.java
new file mode 100644
index 0000000..9913566
--- /dev/null
+++ b/media/java/android/media/routeprovider/RouteRequest.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.media.routeprovider;
+
+import android.media.session.RouteOptions;
+import android.media.session.SessionInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A request to connect or discover routes with certain capabilities.
+ * <p>
+ * Passed to a {@link RouteProviderService} when a request for discovery or to
+ * connect to a route is made. This identifies the app making the request and
+ * provides the full set of connection parameters they would like to use for a
+ * connection. An app that can connect in multiple ways will be represented by
+ * multiple requests.
+ */
+public final class RouteRequest implements Parcelable {
+ private final SessionInfo mSessionInfo;
+ private final RouteOptions mOptions;
+ private final boolean mActive;
+
+ /**
+ * @hide
+ */
+ public RouteRequest(SessionInfo info, RouteOptions connRequest,
+ boolean active) {
+ mSessionInfo = info;
+ mOptions = connRequest;
+ mActive = active;
+ }
+
+ private RouteRequest(Parcel in) {
+ mSessionInfo = SessionInfo.CREATOR.createFromParcel(in);
+ mOptions = RouteOptions.CREATOR.createFromParcel(in);
+ mActive = in.readInt() != 0;
+ }
+
+ /**
+ * Get information about the session making the request.
+ *
+ * @return Info on the session making the request
+ */
+ public SessionInfo getSessionInfo() {
+ return mSessionInfo;
+ }
+
+ /**
+ * Get the connection options, which includes the interfaces and other
+ * connection params the session wants to use with a route.
+ *
+ * @return The connection options
+ */
+ public RouteOptions getConnectionOptions() {
+ return mOptions;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ mSessionInfo.writeToParcel(dest, flags);
+ mOptions.writeToParcel(dest, flags);
+ dest.writeInt(mActive ? 1 : 0);
+ }
+
+ public static final Parcelable.Creator<RouteRequest> CREATOR
+ = new Parcelable.Creator<RouteRequest>() {
+ @Override
+ public RouteRequest createFromParcel(Parcel in) {
+ return new RouteRequest(in);
+ }
+
+ @Override
+ public RouteRequest[] newArray(int size) {
+ return new RouteRequest[size];
+ }
+ };
+}
diff --git a/media/java/android/media/session/IMediaSession.aidl b/media/java/android/media/session/ISession.aidl
similarity index 64%
rename from media/java/android/media/session/IMediaSession.aidl
rename to media/java/android/media/session/ISession.aidl
index aed7641..ca77f04 100644
--- a/media/java/android/media/session/IMediaSession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -15,25 +15,33 @@
package android.media.session;
-import android.media.session.IMediaController;
+import android.media.session.ISessionController;
import android.media.session.MediaMetadata;
+import android.media.session.RouteOptions;
+import android.media.session.RouteCommand;
+import android.media.session.RouteInfo;
import android.media.session.PlaybackState;
import android.os.Bundle;
+import android.os.ResultReceiver;
/**
* Interface to a MediaSession in the system.
* @hide
*/
-interface IMediaSession {
+interface ISession {
void sendEvent(String event, in Bundle data);
- IMediaController getMediaController();
+ ISessionController getController();
void setTransportPerformerEnabled();
- void setRouteState(in Bundle routeState);
- void setRoute(in Bundle mediaRouteDescriptor);
- List<String> getSupportedInterfaces();
void publish();
void destroy();
+ // These commands are for setting up and communicating with routes
+ // Returns true if the route was set for this session
+ boolean setRoute(in RouteInfo route);
+ void setRouteOptions(in List<RouteOptions> options);
+ void connectToRoute(in RouteInfo route, in RouteOptions options);
+ void sendRouteCommand(in RouteCommand event, in ResultReceiver cb);
+
// These commands are for the TransportPerformer
void setMetadata(in MediaMetadata metadata);
void setPlaybackState(in PlaybackState state);
diff --git a/media/java/android/media/session/IMediaSessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
similarity index 73%
rename from media/java/android/media/session/IMediaSessionCallback.aidl
rename to media/java/android/media/session/ISessionCallback.aidl
index 7c183e0..f04cbcc 100644
--- a/media/java/android/media/session/IMediaSessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -16,6 +16,9 @@
package android.media.session;
import android.media.Rating;
+import android.media.session.RouteEvent;
+import android.media.session.RouteInfo;
+import android.media.session.RouteOptions;
import android.content.Intent;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -23,10 +26,13 @@
/**
* @hide
*/
-oneway interface IMediaSessionCallback {
+oneway interface ISessionCallback {
void onCommand(String command, in Bundle extras, in ResultReceiver cb);
- void onMediaButton(in Intent mediaRequestIntent);
- void onRequestRouteChange(in Bundle route);
+ void onMediaButton(in Intent mediaButtonIntent);
+ void onRequestRouteChange(in RouteInfo route);
+ void onRouteConnected(in RouteInfo route, in RouteOptions options);
+ void onRouteStateChange(int state);
+ void onRouteEvent(in RouteEvent event);
// These callbacks are for the TransportPerformer
void onPlay();
diff --git a/media/java/android/media/session/IMediaController.aidl b/media/java/android/media/session/ISessionController.aidl
similarity index 85%
rename from media/java/android/media/session/IMediaController.aidl
rename to media/java/android/media/session/ISessionController.aidl
index d34e973..e2e046f 100644
--- a/media/java/android/media/session/IMediaController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -17,7 +17,7 @@
import android.content.Intent;
import android.media.Rating;
-import android.media.session.IMediaControllerCallback;
+import android.media.session.ISessionControllerCallback;
import android.media.session.MediaMetadata;
import android.media.session.PlaybackState;
import android.os.Bundle;
@@ -28,12 +28,13 @@
* Interface to a MediaSession in the system.
* @hide
*/
-interface IMediaController {
+interface ISessionController {
void sendCommand(String command, in Bundle extras, in ResultReceiver cb);
void sendMediaButton(in KeyEvent mediaButton);
- void registerCallbackListener(in IMediaControllerCallback cb);
- void unregisterCallbackListener(in IMediaControllerCallback cb);
+ void registerCallbackListener(in ISessionControllerCallback cb);
+ void unregisterCallbackListener(in ISessionControllerCallback cb);
boolean isTransportControlEnabled();
+ void showRoutePicker();
// These commands are for the TransportController
void play();
diff --git a/media/java/android/media/session/IMediaControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
similarity index 88%
rename from media/java/android/media/session/IMediaControllerCallback.aidl
rename to media/java/android/media/session/ISessionControllerCallback.aidl
index 3651f1b..bc1ae05 100644
--- a/media/java/android/media/session/IMediaControllerCallback.aidl
+++ b/media/java/android/media/session/ISessionControllerCallback.aidl
@@ -16,15 +16,16 @@
package android.media.session;
import android.media.session.MediaMetadata;
+import android.media.session.RouteInfo;
import android.media.session.PlaybackState;
import android.os.Bundle;
/**
* @hide
*/
-oneway interface IMediaControllerCallback {
+oneway interface ISessionControllerCallback {
void onEvent(String event, in Bundle extras);
- void onRouteChanged(in Bundle route);
+ void onRouteChanged(in RouteInfo route);
// These callbacks are for the TransportController
void onPlaybackStateChanged(in PlaybackState state);
diff --git a/media/java/android/media/session/IMediaSessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
similarity index 77%
rename from media/java/android/media/session/IMediaSessionManager.aidl
rename to media/java/android/media/session/ISessionManager.aidl
index 0b4328e..84b9a0f 100644
--- a/media/java/android/media/session/IMediaSessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -15,14 +15,14 @@
package android.media.session;
-import android.media.session.IMediaSession;
-import android.media.session.IMediaSessionCallback;
+import android.media.session.ISession;
+import android.media.session.ISessionCallback;
import android.os.Bundle;
/**
* Interface to the MediaSessionManagerService
* @hide
*/
-interface IMediaSessionManager {
- IMediaSession createSession(String packageName, in IMediaSessionCallback cb, String tag);
+interface ISessionManager {
+ ISession createSession(String packageName, in ISessionCallback cb, String tag);
}
\ No newline at end of file
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index b3506b3..14d9fb1 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -15,12 +15,11 @@
*/
package android.media.session;
-import android.media.RemoteControlClient;
import android.os.Parcel;
import android.os.Parcelable;
/**
- * Playback state for a {@link MediaSession}. This includes a state like
+ * Playback state for a {@link Session}. This includes a state like
* {@link PlaybackState#PLAYSTATE_PLAYING}, the current playback position,
* and the current control capabilities.
*/
@@ -147,6 +146,14 @@
*/
public final static int PLAYSTATE_ERROR = 7;
+ /**
+ * State indicating the class doing playback is currently connecting to a
+ * route. Depending on the implementation you may return to the previous
+ * state when the connection finishes or enter {@link #PLAYSTATE_NONE}. If
+ * the connection failed {@link #PLAYSTATE_ERROR} should be used.
+ */
+ public final static int PLAYSTATE_CONNECTING = 8;
+
private int mState;
private long mPosition;
private long mBufferPosition;
diff --git a/media/java/android/media/session/Route.java b/media/java/android/media/session/Route.java
new file mode 100644
index 0000000..c9530a6
--- /dev/null
+++ b/media/java/android/media/session/Route.java
@@ -0,0 +1,99 @@
+/*
+ * 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.media.session;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * Represents a destination which an application has connected to and may send
+ * media content.
+ * <p>
+ * This allows a session owner to interact with a route it has been connected
+ * to. The MediaRoute must be used to get {@link RouteInterface}
+ * instances which can be used to communicate over a specific interface on the
+ * route.
+ */
+public final class Route {
+ private static final String TAG = "Route";
+ private final RouteInfo mInfo;
+ private final Session mSession;
+ private final RouteOptions mOptions;
+
+ /**
+ * @hide
+ */
+ public Route(RouteInfo info, RouteOptions options, Session session) {
+ if (info == null || options == null) {
+ throw new IllegalStateException("Route info was not valid!");
+ }
+ mInfo = info;
+ mOptions = options;
+ mSession = session;
+ }
+
+ /**
+ * Get the {@link RouteInfo} for this route.
+ *
+ * @return The info for this route.
+ */
+ public RouteInfo getRouteInfo() {
+ return mInfo;
+ }
+
+ /**
+ * Get the {@link RouteOptions} that were used to connect this route.
+ *
+ * @return The options used to connect to this route.
+ */
+ public RouteOptions getOptions() {
+ return mOptions;
+ }
+
+ /**
+ * Gets an interface provided by this route. If the interface is not
+ * supported by the route, returns null.
+ *
+ * @see RouteInterface
+ * @param iface The name of the interface to create
+ * @return A {@link RouteInterface} or null if the interface is
+ * not supported.
+ */
+ public RouteInterface getInterface(String iface) {
+ if (TextUtils.isEmpty(iface)) {
+ throw new IllegalArgumentException("iface may not be empty.");
+ }
+ List<String> ifaces = mOptions.getInterfaceNames();
+ if (ifaces != null) {
+ for (int i = ifaces.size() - 1; i >= 0; i--) {
+ if (iface.equals(ifaces.get(i))) {
+ return new RouteInterface(this, iface, mSession);
+ }
+ }
+ }
+ Log.e(TAG, "Interface not supported by route");
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ Session getSession() {
+ return mSession;
+ }
+}
diff --git a/media/java/android/media/session/MediaSessionToken.aidl b/media/java/android/media/session/RouteCommand.aidl
similarity index 95%
copy from media/java/android/media/session/MediaSessionToken.aidl
copy to media/java/android/media/session/RouteCommand.aidl
index 5812682..725b308 100644
--- a/media/java/android/media/session/MediaSessionToken.aidl
+++ b/media/java/android/media/session/RouteCommand.aidl
@@ -15,4 +15,4 @@
package android.media.session;
-parcelable MediaSessionToken;
+parcelable RouteCommand;
diff --git a/media/java/android/media/session/RouteCommand.java b/media/java/android/media/session/RouteCommand.java
new file mode 100644
index 0000000..358bc0a
--- /dev/null
+++ b/media/java/android/media/session/RouteCommand.java
@@ -0,0 +1,117 @@
+/*
+ * 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.media.session;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a command that an application may send to a route.
+ * <p>
+ * Commands are associated with a specific route and interface supported by that
+ * route and sent through the session. This class isn't used directly by apps.
+ *
+ * @hide
+ */
+public final class RouteCommand implements Parcelable {
+ private final String mRoute;
+ private final String mIface;
+ private final String mEvent;
+ private final Bundle mExtras;
+
+ /**
+ * @param route The id of the route this event is being sent on
+ * @param iface The interface the sender used
+ * @param event The event or command
+ * @param extras Any extras included with the event
+ */
+ public RouteCommand(String route, String iface, String event, Bundle extras) {
+ mRoute = route;
+ mIface = iface;
+ mEvent = event;
+ mExtras = extras;
+ }
+
+ private RouteCommand(Parcel in) {
+ mRoute = in.readString();
+ mIface = in.readString();
+ mEvent = in.readString();
+ mExtras = in.readBundle();
+ }
+
+ /**
+ * Get the id for the route this event was sent on.
+ *
+ * @return The route id this event is using
+ */
+ public String getRouteInfo() {
+ return mRoute;
+ }
+
+ /**
+ * Get the interface this event was sent from
+ *
+ * @return The interface for this event
+ */
+ public String getIface() {
+ return mIface;
+ }
+
+ /**
+ * Get the action/name of the event.
+ *
+ * @return The name of event/command.
+ */
+ public String getEvent() {
+ return mEvent;
+ }
+
+ /**
+ * Get any extras included with the event.
+ *
+ * @return The bundle included with the event or null
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mRoute);
+ dest.writeString(mIface);
+ dest.writeString(mEvent);
+ dest.writeBundle(mExtras);
+ }
+
+ public static final Parcelable.Creator<RouteCommand> CREATOR
+ = new Parcelable.Creator<RouteCommand>() {
+ @Override
+ public RouteCommand createFromParcel(Parcel in) {
+ return new RouteCommand(in);
+ }
+
+ @Override
+ public RouteCommand[] newArray(int size) {
+ return new RouteCommand[size];
+ }
+ };
+}
diff --git a/media/java/android/media/session/MediaSessionToken.aidl b/media/java/android/media/session/RouteEvent.aidl
similarity index 95%
rename from media/java/android/media/session/MediaSessionToken.aidl
rename to media/java/android/media/session/RouteEvent.aidl
index 5812682..6966207 100644
--- a/media/java/android/media/session/MediaSessionToken.aidl
+++ b/media/java/android/media/session/RouteEvent.aidl
@@ -15,4 +15,4 @@
package android.media.session;
-parcelable MediaSessionToken;
+parcelable RouteEvent;
diff --git a/media/java/android/media/session/RouteEvent.java b/media/java/android/media/session/RouteEvent.java
new file mode 100644
index 0000000..918e410
--- /dev/null
+++ b/media/java/android/media/session/RouteEvent.java
@@ -0,0 +1,120 @@
+/*
+ * 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.media.session;
+
+import android.media.routeprovider.RouteConnection;
+import android.media.routeprovider.RouteProviderService;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents an event that a route provider is sending to a particular
+ * {@link RouteConnection}. Events are associated with a specific interface
+ * supported by the connection and sent through the {@link RouteProviderService}.
+ * This class isn't used directly by apps.
+ *
+ * @hide
+ */
+public class RouteEvent implements Parcelable {
+ private final IBinder mConnection;
+ private final String mIface;
+ private final String mEvent;
+ private final Bundle mExtras;
+
+ /**
+ * @param connection The connection that this event is for
+ * @param iface The interface the sender used
+ * @param event The event or command
+ * @param extras Any extras included with the event
+ */
+ public RouteEvent(IBinder connection, String iface, String event, Bundle extras) {
+ mConnection = connection;
+ mIface = iface;
+ mEvent = event;
+ mExtras = extras;
+ }
+
+ private RouteEvent(Parcel in) {
+ mConnection = in.readStrongBinder();
+ mIface = in.readString();
+ mEvent = in.readString();
+ mExtras = in.readBundle();
+ }
+
+ /**
+ * Get the connection this event was sent on.
+ *
+ * @return The connection this event is using
+ */
+ public IBinder getConnection() {
+ return mConnection;
+ }
+
+ /**
+ * Get the interface this event was sent from
+ *
+ * @return The interface for this event
+ */
+ public String getIface() {
+ return mIface;
+ }
+
+ /**
+ * Get the action/name of the event.
+ *
+ * @return The name of event/command.
+ */
+ public String getEvent() {
+ return mEvent;
+ }
+
+ /**
+ * Get any extras included with the event.
+ *
+ * @return The bundle included with the event or null
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongBinder(mConnection);
+ dest.writeString(mIface);
+ dest.writeString(mEvent);
+ dest.writeBundle(mExtras);
+ }
+
+ public static final Parcelable.Creator<RouteEvent> CREATOR
+ = new Parcelable.Creator<RouteEvent>() {
+ @Override
+ public RouteEvent createFromParcel(Parcel in) {
+ return new RouteEvent(in);
+ }
+
+ @Override
+ public RouteEvent[] newArray(int size) {
+ return new RouteEvent[size];
+ }
+ };
+}
diff --git a/media/java/android/media/session/MediaSessionToken.aidl b/media/java/android/media/session/RouteInfo.aidl
similarity index 95%
copy from media/java/android/media/session/MediaSessionToken.aidl
copy to media/java/android/media/session/RouteInfo.aidl
index 5812682..c5f50c8 100644
--- a/media/java/android/media/session/MediaSessionToken.aidl
+++ b/media/java/android/media/session/RouteInfo.aidl
@@ -15,4 +15,4 @@
package android.media.session;
-parcelable MediaSessionToken;
+parcelable RouteInfo;
diff --git a/media/java/android/media/session/RouteInfo.java b/media/java/android/media/session/RouteInfo.java
new file mode 100644
index 0000000..17df969
--- /dev/null
+++ b/media/java/android/media/session/RouteInfo.java
@@ -0,0 +1,233 @@
+/*
+ * 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.media.session;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Information about a route, including its display name, a way to identify it,
+ * and the ways it can be connected to.
+ */
+public final class RouteInfo implements Parcelable {
+ private final String mName;
+ private final String mId;
+ private final String mProviderId;
+ private final List<RouteOptions> mOptions;
+
+ private RouteInfo(String id, String name, String providerId,
+ List<RouteOptions> connRequests) {
+ mId = id;
+ mName = name;
+ mProviderId = providerId;
+ mOptions = connRequests;
+ }
+
+ private RouteInfo(Parcel in) {
+ mId = in.readString();
+ mName = in.readString();
+ mProviderId = in.readString();
+ mOptions = new ArrayList<RouteOptions>();
+ in.readTypedList(mOptions, RouteOptions.CREATOR);
+ }
+
+ /**
+ * Get the displayable name of this route.
+ *
+ * @return A short, user readable name for this route
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Get the unique id for this route.
+ *
+ * @return A unique route id.
+ */
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * Get the package name of this route's provider.
+ *
+ * @return The package name of this route's provider.
+ */
+ public String getProvider() {
+ return mProviderId;
+ }
+
+ /**
+ * Get the set of connections that may be used with this route.
+ *
+ * @return An array of connection requests that may be used to connect
+ */
+ public List<RouteOptions> getConnectionMethods() {
+ return mOptions;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mId);
+ dest.writeString(mName);
+ dest.writeString(mProviderId);
+ dest.writeTypedList(mOptions);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder bob = new StringBuilder();
+ bob.append("RouteInfo: id=").append(mId).append(", name=").append(mName)
+ .append(", provider=").append(mProviderId).append(", options={");
+ for (int i = 0; i < mOptions.size(); i++) {
+ if (i != 0) {
+ bob.append(", ");
+ }
+ bob.append(mOptions.get(i).toString());
+ }
+ bob.append("}");
+ return bob.toString();
+ }
+
+ public static final Parcelable.Creator<RouteInfo> CREATOR
+ = new Parcelable.Creator<RouteInfo>() {
+ @Override
+ public RouteInfo createFromParcel(Parcel in) {
+ return new RouteInfo(in);
+ }
+
+ @Override
+ public RouteInfo[] newArray(int size) {
+ return new RouteInfo[size];
+ }
+ };
+
+ /**
+ * Helper for creating MediaRouteInfos. A route must have a name and an id.
+ * While options are not strictly required the route cannot be connected to
+ * without at least one set of options.
+ */
+ public static final class Builder {
+ private String mName;
+ private String mId;
+ private String mProviderPackage;
+ private ArrayList<RouteOptions> mOptions;
+
+ /**
+ * Copies an existing route info object. TODO Remove once we have
+ * helpers for creating route infos.
+ *
+ * @param from The existing info to copy.
+ */
+ public Builder(RouteInfo from) {
+ mOptions = new ArrayList<RouteOptions>(from.getConnectionMethods());
+ mName = from.mName;
+ mId = from.mId;
+ mProviderPackage = from.mProviderId;
+ }
+
+ public Builder() {
+ mOptions = new ArrayList<RouteOptions>();
+ }
+
+ /**
+ * Set the user visible name for this route.
+ *
+ * @param name The name of the route
+ * @return The builder for easy chaining.
+ */
+ public Builder setName(String name) {
+ mName = name;
+ return this;
+ }
+
+ /**
+ * Set the id of the route. This should be unique to the provider.
+ *
+ * @param id The unique id of the route.
+ * @return The builder for easy chaining.
+ */
+ public Builder setId(String id) {
+ mId = id;
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ public Builder setProviderId(String packageName) {
+ mProviderPackage = packageName;
+ return this;
+ }
+
+ /**
+ * Add a set of {@link RouteOptions} to the route. Multiple options
+ * may be added to the same route.
+ *
+ * @param options The options to add to this route.
+ * @return The builder for easy chaining.
+ */
+ public Builder addRouteOptions(RouteOptions options) {
+ mOptions.add(options);
+ return this;
+ }
+
+ /**
+ * Clear the set of {@link RouteOptions} on the route.
+ *
+ * @return The builder for easy chaining
+ */
+ public Builder clearRouteOptions() {
+ mOptions.clear();
+ return this;
+ }
+
+ /**
+ * Build a new MediaRouteInfo.
+ *
+ * @return A new MediaRouteInfo with the values that were set.
+ */
+ public RouteInfo build() {
+ if (TextUtils.isEmpty(mName)) {
+ throw new IllegalArgumentException("Must set a name before building");
+ }
+ if (TextUtils.isEmpty(mId)) {
+ throw new IllegalArgumentException("Must set an id before building");
+ }
+ return new RouteInfo(mId, mName, mProviderPackage, mOptions);
+ }
+
+ /**
+ * Get the current number of options that have been added to this
+ * builder.
+ *
+ * @return The number of options that have been added.
+ */
+ public int getOptionsSize() {
+ return mOptions.size();
+ }
+ }
+}
diff --git a/media/java/android/media/session/RouteInterface.java b/media/java/android/media/session/RouteInterface.java
index 2391f27..e9c9fd3 100644
--- a/media/java/android/media/session/RouteInterface.java
+++ b/media/java/android/media/session/RouteInterface.java
@@ -17,136 +17,161 @@
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.Parcelable;
import android.os.ResultReceiver;
+import android.util.Log;
+
+import java.util.ArrayList;
/**
- * Routes can support multiple interfaces for MediaSessions to interact with. To
- * add a standard interface you should implement that interface's RouteInterface
- * Stub and register it with the session. The set of supported commands is
- * dependent on the specific interface's implementation.
- * <p>
- * A MediaInterface can be registered by calling TODO. Once added an interface
- * will be used by Sessions to decide how they communicate with a session and
- * cannot be removed, so all interfaces that you plan to support should be added
- * when the route is created.
+ * A route can support multiple interfaces for a {@link Session} to
+ * interact with. To use a specific interface with a route a
+ * MediaSessionRouteInterface needs to be retrieved from the route. An
+ * implementation of the specific interface, like
+ * {@link RoutePlaybackControls}, should be used to simplify communication
+ * and reduce errors on that interface.
*
- * @see RouteTransportControls
+ * @see RoutePlaybackControls for an example
*/
public final class RouteInterface {
- private static final String TAG = "MediaInterface";
+ private static final String TAG = "RouteInterface";
- private static final String KEY_RESULT = "result";
+ /**
+ * Error indicating the route is currently not connected.
+ */
+ public static final int RESULT_NOT_CONNECTED = -5;
+ /**
+ * Error indicating the session is no longer using the route this command
+ * was sent to.
+ */
+ public static final int RESULT_ROUTE_IS_STALE = -4;
+ /**
+ * Error indicating that the interface does not support the command.
+ */
+ public static final int RESULT_COMMAND_NOT_SUPPORTED = -3;
+ /**
+ * Error indicating that the route does not support the interface.
+ */
+ public static final int RESULT_INTERFACE_NOT_SUPPORTED = -2;
+ /**
+ * Generic error. Extra information about the error may be included in the
+ * result bundle.
+ */
+ public static final int RESULT_ERROR = -1;
+ /**
+ * The command was successful. Extra information may be included in the
+ * result bundle.
+ */
+ public static final int RESULT_SUCCESS = 1;
- private final MediaController mController;
+ private final Route mRoute;
private final String mIface;
+ private final Session mSession;
+
+ private final Object mLock = new Object();
+ private final ArrayList<EventHandler> mListeners = new ArrayList<EventHandler>();
/**
* @hide
*/
- RouteInterface(MediaController controller, String iface) {
- mController = controller;
+ RouteInterface(Route route, String iface, Session session) {
+ mRoute = route;
mIface = iface;
+ mSession = session;
+ mSession.addInterfaceListener(iface, mEventListener);
}
- public void sendCommand(String command, Bundle params, ResultReceiver cb) {
- // TODO
+ /**
+ * Send a command using this interface.
+ *
+ * @param command The command to send.
+ * @param extras Any extras to include with the command.
+ * @param cb The callback to receive the result on.
+ * @return true if the command was sent, false otherwise.
+ */
+ public boolean sendCommand(String command, Bundle extras, ResultReceiver cb) {
+ RouteCommand cmd = new RouteCommand(mRoute.getRouteInfo().getId(), mIface,
+ command, extras);
+ return mSession.sendRouteCommand(cmd, cb);
}
+ /**
+ * Add a listener to this interface. Events will be sent on the caller's
+ * thread.
+ *
+ * @param listener The listener to receive events on.
+ */
public void addListener(EventListener listener) {
addListener(listener, null);
}
+ /**
+ * Add a listener for this interface. If a handler is specified events will
+ * be performed on the handler's thread, otherwise the caller's thread will
+ * be used.
+ *
+ * @param listener The listener to receive events on
+ * @param handler The handler whose thread to post calls on
+ */
public void addListener(EventListener listener, Handler handler) {
- // TODO See MediaController for add/remove pattern
+ if (listener == null) {
+ throw new IllegalArgumentException("listener may not be null");
+ }
+ if (handler == null) {
+ handler = new Handler();
+ }
+ synchronized (mLock) {
+ if (findIndexOfListenerLocked(listener) != -1) {
+ Log.d(TAG, "Listener is already added, ignoring");
+ return;
+ }
+ mListeners.add(new EventHandler(handler.getLooper(), listener));
+ }
}
+ /**
+ * Remove a listener from this interface.
+ *
+ * @param listener The listener to stop receiving events on.
+ */
public void removeListener(EventListener listener) {
- // TODO
- }
-
- // TODO decide on list of supported types
- private static Bundle writeResultToBundle(Object v) {
- Bundle b = new Bundle();
- if (v == null) {
- // Don't send anything if null
- } else if (v instanceof String) {
- b.putString(KEY_RESULT, (String) v);
- } else if (v instanceof Integer) {
- b.putInt(KEY_RESULT, (Integer) v);
- } else if (v instanceof Bundle) {
- // Must be before Parcelable
- b.putBundle(KEY_RESULT, (Bundle) v);
- } else if (v instanceof Parcelable) {
- b.putParcelable(KEY_RESULT, (Parcelable) v);
- } else if (v instanceof Short) {
- b.putShort(KEY_RESULT, (Short) v);
- } else if (v instanceof Long) {
- b.putLong(KEY_RESULT, (Long) v);
- } else if (v instanceof Float) {
- b.putFloat(KEY_RESULT, (Float) v);
- } else if (v instanceof Double) {
- b.putDouble(KEY_RESULT, (Double) v);
- } else if (v instanceof Boolean) {
- b.putBoolean(KEY_RESULT, (Boolean) v);
- } else if (v instanceof CharSequence) {
- // Must be after String
- b.putCharSequence(KEY_RESULT, (CharSequence) v);
- } else if (v instanceof boolean[]) {
- b.putBooleanArray(KEY_RESULT, (boolean[]) v);
- } else if (v instanceof byte[]) {
- b.putByteArray(KEY_RESULT, (byte[]) v);
- } else if (v instanceof String[]) {
- b.putStringArray(KEY_RESULT, (String[]) v);
- } else if (v instanceof CharSequence[]) {
- // Must be after String[] and before Object[]
- b.putCharSequenceArray(KEY_RESULT, (CharSequence[]) v);
- } else if (v instanceof IBinder) {
- b.putBinder(KEY_RESULT, (IBinder) v);
- } else if (v instanceof Parcelable[]) {
- b.putParcelableArray(KEY_RESULT, (Parcelable[]) v);
- } else if (v instanceof int[]) {
- b.putIntArray(KEY_RESULT, (int[]) v);
- } else if (v instanceof long[]) {
- b.putLongArray(KEY_RESULT, (long[]) v);
- } else if (v instanceof Byte) {
- b.putByte(KEY_RESULT, (Byte) v);
+ if (listener == null) {
+ throw new IllegalArgumentException("listener may not be null");
}
- return b;
- }
-
- public abstract static class Stub {
-
- /**
- * The name of an interface should be a fully qualified name to prevent
- * namespace collisions. Example: "com.myproject.MyPlaybackInterface"
- *
- * @return The name of this interface
- */
- public abstract String getName();
-
- /**
- * This is called when a command is received that matches the interface
- * you registered. Commands can come from any app with a MediaController
- * reference to the session.
- *
- * @see MediaController
- * @see MediaSession
- * @param command The command or method to invoke.
- * @param args Any args that were included with the command. May be
- * null.
- * @param cb The callback provided to send a response on. May be null.
- */
- public abstract void onCommand(String command, Bundle args, ResultReceiver cb);
-
- public final void sendEvent(MediaSession session, String event, Bundle extras) {
- // TODO
+ synchronized (mLock) {
+ int index = findIndexOfListenerLocked(listener);
+ if (index != -1) {
+ mListeners.remove(index);
+ }
}
}
+ private int findIndexOfListenerLocked(EventListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("Callback cannot be null");
+ }
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ EventHandler handler = mListeners.get(i);
+ if (listener == handler.mListener) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private EventListener mEventListener = new EventListener() {
+ @Override
+ public void onEvent(String event, Bundle args) {
+ synchronized (mLock) {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).postEvent(event, args);
+ }
+ }
+ }
+
+ };
+
/**
* An EventListener can be registered by an app with TODO to handle events
* sent by the session on a specific interface.
@@ -166,9 +191,9 @@
private static final class EventHandler extends Handler {
- private final RouteInterface.EventListener mListener;
+ private final EventListener mListener;
- public EventHandler(Looper looper, RouteInterface.EventListener cb) {
+ public EventHandler(Looper looper, EventListener cb) {
super(looper, null, true);
mListener = cb;
}
diff --git a/media/java/android/media/session/MediaSessionToken.aidl b/media/java/android/media/session/RouteOptions.aidl
similarity index 95%
copy from media/java/android/media/session/MediaSessionToken.aidl
copy to media/java/android/media/session/RouteOptions.aidl
index 5812682..feaf517 100644
--- a/media/java/android/media/session/MediaSessionToken.aidl
+++ b/media/java/android/media/session/RouteOptions.aidl
@@ -15,4 +15,4 @@
package android.media.session;
-parcelable MediaSessionToken;
+parcelable RouteOptions;
diff --git a/media/java/android/media/session/RouteOptions.java b/media/java/android/media/session/RouteOptions.java
new file mode 100644
index 0000000..5105867
--- /dev/null
+++ b/media/java/android/media/session/RouteOptions.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.media.session;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Specifies options that an application might use when connecting to a route.
+ * This includes things like interfaces, connection parameters, and required
+ * features.
+ * <p>
+ * An application may create several different route options that describe
+ * alternative sets of capabilities that it can use and choose the most
+ * appropriate route options when it is ready to connect to the route. Each
+ * route options instance must specify a complete set of capabilities to request
+ * when the connection is established.
+ */
+public final class RouteOptions implements Parcelable {
+ private static final String TAG = "RouteOptions";
+
+ private final ArrayList<String> mIfaces;
+ private final Bundle mConnectionParams;
+
+ private RouteOptions(List<String> ifaces, Bundle params) {
+ mIfaces = new ArrayList<String>(ifaces);
+ mConnectionParams = params;
+ }
+
+ private RouteOptions(Parcel in) {
+ mIfaces = new ArrayList<String>();
+ in.readStringList(mIfaces);
+ mConnectionParams = in.readBundle();
+ }
+
+ /**
+ * Get the interfaces this connection wants to use.
+ *
+ * @return The interfaces for this connection
+ */
+ public List<String> getInterfaceNames() {
+ return mIfaces;
+ }
+
+ /**
+ * Get the parameters that will be used for connecting.
+ *
+ * @return The set of connection parameters this connections uses
+ */
+ public Bundle getConnectionParams() {
+ return mConnectionParams;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStringList(mIfaces);
+ dest.writeBundle(mConnectionParams);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder bob = new StringBuilder();
+ bob.append("Options: interfaces={");
+ for (int i = 0; i < mIfaces.size(); i++) {
+ if (i != 0) {
+ bob.append(", ");
+ }
+ bob.append(mIfaces.get(i));
+ }
+ bob.append("}");
+ bob.append(", parameters=");
+ bob.append(mConnectionParams == null ? "null" : mConnectionParams.toString());
+ return bob.toString();
+ }
+
+ public static final Parcelable.Creator<RouteOptions> CREATOR
+ = new Parcelable.Creator<RouteOptions>() {
+ @Override
+ public RouteOptions createFromParcel(Parcel in) {
+ return new RouteOptions(in);
+ }
+
+ @Override
+ public RouteOptions[] newArray(int size) {
+ return new RouteOptions[size];
+ }
+ };
+
+ /**
+ * Builder for creating {@link RouteOptions}.
+ */
+ public final static class Builder {
+ private ArrayList<String> mIfaces = new ArrayList<String>();
+ private Bundle mConnectionParams;
+
+ public Builder() {
+ }
+
+ /**
+ * Add a required interface to the options.
+ *
+ * @param interfaceName The name of the interface to add.
+ * @return The builder to allow chaining commands.
+ */
+ public Builder addInterface(String interfaceName) {
+ if (TextUtils.isEmpty(interfaceName)) {
+ throw new IllegalArgumentException("interfaceName cannot be empty");
+ }
+ if (!mIfaces.contains(interfaceName)) {
+ mIfaces.add(interfaceName);
+ } else {
+ Log.w(TAG, "Attempted to add interface that is already added");
+ }
+ return this;
+ }
+
+ /**
+ * Set the connection parameters to use with the options. TODO replace
+ * with more specific calls once we decide on the standard way to
+ * express parameters.
+ *
+ * @param parameters The parameters to use.
+ * @return The builder to allow chaining commands.
+ */
+ public Builder setParameters(Bundle parameters) {
+ mConnectionParams = parameters;
+ return this;
+ }
+
+ /**
+ * Generate a set of options.
+ *
+ * @return The options with the specified components.
+ */
+ public RouteOptions build() {
+ return new RouteOptions(mIfaces, mConnectionParams);
+ }
+ }
+}
diff --git a/media/java/android/media/session/RoutePlaybackControls.java b/media/java/android/media/session/RoutePlaybackControls.java
new file mode 100644
index 0000000..a3ffb58
--- /dev/null
+++ b/media/java/android/media/session/RoutePlaybackControls.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.media.session;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
+
+/**
+ * A standard media control interface for Routes that support queueing and
+ * transport controls. Routes may support multiple interfaces for MediaSessions
+ * to interact with.
+ */
+public final class RoutePlaybackControls {
+ private static final String TAG = "RoutePlaybackControls";
+ public static final String NAME = "android.media.session.RoutePlaybackControls";
+
+ /** @hide */
+ public static final String KEY_VALUE1 = "value1";
+
+ /** @hide */
+ public static final String CMD_FAST_FORWARD = "fastForward";
+ /** @hide */
+ public static final String CMD_GET_CURRENT_POSITION = "getCurrentPosition";
+ /** @hide */
+ public static final String CMD_GET_CAPABILITIES = "getCapabilities";
+ /** @hide */
+ public static final String CMD_PLAY_NOW = "playNow";
+ /** @hide */
+ public static final String CMD_RESUME = "resume";
+ /** @hide */
+ public static final String CMD_PAUSE = "pause";
+
+ /** @hide */
+ public static final String EVENT_PLAYSTATE_CHANGE = "playstateChange";
+ /** @hide */
+ public static final String EVENT_METADATA_CHANGE = "metadataChange";
+
+ private final RouteInterface mIface;
+
+ private RoutePlaybackControls(RouteInterface iface) {
+ mIface = iface;
+ }
+
+ /**
+ * Get a new MediaRoutePlaybackControls instance for sending commands using
+ * this interface. If the provided route doesn't support this interface null
+ * will be returned.
+ *
+ * @param route The route to send commands to.
+ * @return A MediaRoutePlaybackControls instance or null if not supported.
+ */
+ public static RoutePlaybackControls from(Route route) {
+ RouteInterface iface = route.getInterface(NAME);
+ if (iface != null) {
+ return new RoutePlaybackControls(iface);
+ }
+ return null;
+ }
+
+ /**
+ * Send a resume command to the route.
+ */
+ public void resume() {
+ mIface.sendCommand(CMD_RESUME, null, null);
+ }
+
+ /**
+ * Send a pause command to the route.
+ */
+ public void pause() {
+ mIface.sendCommand(CMD_PAUSE, null, null);
+ }
+
+ /**
+ * Send a fast forward command.
+ */
+ public void fastForward() {
+ Bundle b = new Bundle();
+ mIface.sendCommand(CMD_FAST_FORWARD, b, null);
+ }
+
+ /**
+ * Retrieves the current playback position.
+ *
+ * @param cb The callback to receive the result on.
+ */
+ public void getCurrentPosition(ResultReceiver cb) {
+ mIface.sendCommand(CMD_GET_CURRENT_POSITION, null, cb);
+ }
+
+ public void getCapabilities(ResultReceiver cb) {
+ mIface.sendCommand(CMD_GET_CAPABILITIES, null, cb);
+ }
+
+ public void addListener(Listener listener) {
+ mIface.addListener(listener);
+ }
+
+ public void addListener(Listener listener, Handler handler) {
+ mIface.addListener(listener, handler);
+ }
+
+ public void removeListener(Listener listener) {
+ mIface.removeListener(listener);
+ }
+
+ public void playNow(String content) {
+ Bundle bundle = new Bundle();
+ bundle.putString(KEY_VALUE1, content);
+ mIface.sendCommand(CMD_PLAY_NOW, bundle, null);
+ }
+
+ /**
+ * Register this event listener using {@link #addListener} to receive
+ * RoutePlaybackControl events from a session.
+ */
+ public static abstract class Listener extends RouteInterface.EventListener {
+ @Override
+ public final void onEvent(String event, Bundle args) {
+ if (EVENT_PLAYSTATE_CHANGE.equals(event)) {
+ onPlaybackStateChange(args.getInt(KEY_VALUE1, 0));
+ } else if (EVENT_METADATA_CHANGE.equals(event)) {
+ onMetadataUpdate((MediaMetadata) args.getParcelable(KEY_VALUE1));
+ }
+ }
+
+ /**
+ * Override to handle updates to the playback state. Valid values are in
+ * {@link TransportPerformer}. TODO put playstate values somewhere more
+ * generic.
+ *
+ * @param state
+ */
+ public void onPlaybackStateChange(int state) {
+ }
+
+ /**
+ * Override to handle metadata changes for this session's media. The
+ * default supported fields are those in {@link MediaMetadata}.
+ *
+ * @param metadata
+ */
+ public void onMetadataUpdate(MediaMetadata metadata) {
+ }
+ }
+
+}
diff --git a/media/java/android/media/session/RouteTransportControls.java b/media/java/android/media/session/RouteTransportControls.java
deleted file mode 100644
index 665fd10..0000000
--- a/media/java/android/media/session/RouteTransportControls.java
+++ /dev/null
@@ -1,230 +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 android.media.session;
-
-import android.media.RemoteControlClient;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.ResultReceiver;
-import android.text.TextUtils;
-import android.util.Log;
-
-/**
- * A standard media control interface for Routes. Routes can support multiple
- * interfaces for MediaSessions to interact with. TODO rewrite for routes
- */
-public final class RouteTransportControls {
- private static final String TAG = "RouteTransportControls";
- public static final String NAME = "android.media.session.RouteTransportControls";
-
- private static final String KEY_VALUE1 = "value1";
-
- private static final String METHOD_FAST_FORWARD = "fastForward";
- private static final String METHOD_GET_CURRENT_POSITION = "getCurrentPosition";
- private static final String METHOD_GET_CAPABILITIES = "getCapabilities";
-
- private static final String EVENT_PLAYSTATE_CHANGE = "playstateChange";
- private static final String EVENT_METADATA_CHANGE = "metadataChange";
-
- private final MediaController mController;
- private final RouteInterface mIface;
-
- private RouteTransportControls(RouteInterface iface, MediaController controller) {
- mIface = iface;
- mController = controller;
- }
-
- public static RouteTransportControls from(MediaController controller) {
-// MediaInterface iface = controller.getInterface(NAME);
-// if (iface != null) {
-// return new RouteTransportControls(iface, controller);
-// }
- return null;
- }
-
- /**
- * Send a play command to the route. TODO rename resume() and use messaging
- * protocol, not KeyEvent
- */
- public void play() {
- // TODO
- }
-
- /**
- * Send a pause command to the session.
- */
- public void pause() {
- // TODO
- }
-
- /**
- * Set the rate at which to fastforward. Valid values are in the range [0,1]
- * with actual rates depending on the implementation.
- *
- * @param rate
- */
- public void fastForward(float rate) {
- if (rate < 0 || rate > 1) {
- throw new IllegalArgumentException("Rate must be between 0 and 1 inclusive");
- }
- Bundle b = new Bundle();
- b.putFloat(KEY_VALUE1, rate);
- mIface.sendCommand(METHOD_FAST_FORWARD, b, null);
- }
-
- public void getCurrentPosition(ResultReceiver cb) {
- mIface.sendCommand(METHOD_GET_CURRENT_POSITION, null, cb);
- }
-
- public void getCapabilities(ResultReceiver cb) {
- mIface.sendCommand(METHOD_GET_CAPABILITIES, null, cb);
- }
-
- public void addListener(Listener listener) {
- mIface.addListener(listener.mListener);
- }
-
- public void addListener(Listener listener, Handler handler) {
- mIface.addListener(listener.mListener, handler);
- }
-
- public void removeListener(Listener listener) {
- mIface.removeListener(listener.mListener);
- }
-
- public static abstract class Stub extends RouteInterface.Stub {
- private final MediaSession mSession;
-
- public Stub(MediaSession session) {
- mSession = session;
- }
-
- @Override
- public String getName() {
- return NAME;
- }
-
- @Override
- public void onCommand(String method, Bundle extras, ResultReceiver cb) {
- if (TextUtils.isEmpty(method)) {
- return;
- }
- Bundle result;
- if (METHOD_FAST_FORWARD.equals(method)) {
- fastForward(extras.getFloat(KEY_VALUE1, -1));
- } else if (METHOD_GET_CURRENT_POSITION.equals(method)) {
- if (cb != null) {
- result = new Bundle();
- result.putLong(KEY_VALUE1, getCurrentPosition());
- cb.send(0, result);
- }
- } else if (METHOD_GET_CAPABILITIES.equals(method)) {
- if (cb != null) {
- result = new Bundle();
- result.putLong(KEY_VALUE1, getCapabilities());
- cb.send(0, result);
- }
- }
- }
-
- /**
- * Override to handle fast forwarding. Valid values are [0,1] inclusive.
- * The interpretation of the rate is up to the implementation. If no
- * rate was included with the command a rate of -1 will be used by
- * default.
- *
- * @param rate The rate at which to fast forward as a multiplier
- */
- public void fastForward(float rate) {
- Log.w(TAG, "fastForward is not supported.");
- }
-
- /**
- * Override to handle getting the current position of playback in
- * millis.
- *
- * @return The current position in millis or -1
- */
- public long getCurrentPosition() {
- Log.w(TAG, "getCurrentPosition is not supported");
- return -1;
- }
-
- /**
- * Override to handle getting the set of capabilities currently
- * available.
- *
- * @return A bit mask of the supported capabilities
- */
- public long getCapabilities() {
- Log.w(TAG, "getCapabilities is not supported");
- return 0;
- }
-
- /**
- * Publish the current playback state to the system and any controllers.
- * Valid values are defined in {@link RemoteControlClient}. TODO move
- * play states somewhere else.
- *
- * @param state
- */
- public final void updatePlaybackState(int state) {
- Bundle extras = new Bundle();
- extras.putInt(KEY_VALUE1, state);
- sendEvent(mSession, EVENT_PLAYSTATE_CHANGE, extras);
- }
- }
-
- /**
- * Register this event listener using TODO to receive
- * TransportControlInterface events from a session.
- *
- * @see RouteInterface.EventListener
- */
- public static abstract class Listener {
-
- private RouteInterface.EventListener mListener = new RouteInterface.EventListener() {
- @Override
- public final void onEvent(String event, Bundle args) {
- if (EVENT_PLAYSTATE_CHANGE.equals(event)) {
- onPlaybackStateChange(args.getInt(KEY_VALUE1));
- } else if (EVENT_METADATA_CHANGE.equals(event)) {
- onMetadataUpdate(args);
- }
- }
- };
-
- /**
- * Override to handle updates to the playback state. Valid values are in
- * {@link TransportPerformer}. TODO put playstate values somewhere more
- * generic.
- *
- * @param state
- */
- public void onPlaybackStateChange(int state) {
- }
-
- /**
- * Override to handle metadata changes for this session's media. The
- * default supported fields are those in {@link MediaMetadata}.
- *
- * @param metadata
- */
- public void onMetadataUpdate(Bundle metadata) {
- }
- }
-
-}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/Session.java
similarity index 66%
rename from media/java/android/media/session/MediaSession.java
rename to media/java/android/media/session/Session.java
index 23c3035..8ccd788 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/Session.java
@@ -18,9 +18,9 @@
import android.content.Intent;
import android.media.Rating;
-import android.media.session.IMediaController;
-import android.media.session.IMediaSession;
-import android.media.session.IMediaSessionCallback;
+import android.media.session.ISessionController;
+import android.media.session.ISession;
+import android.media.session.ISessionCallback;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -33,6 +33,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.List;
/**
* Allows interaction with media controllers, media routes, volume keys, media
@@ -44,11 +45,11 @@
* media to multiple routes or to provide finer grain controls of media.
* <p>
* A MediaSession is created by calling
- * {@link MediaSessionManager#createSession(String)}. Once a session is created
+ * {@link SessionManager#createSession(String)}. Once a session is created
* apps that have the MEDIA_CONTENT_CONTROL permission can interact with the
- * session through {@link MediaSessionManager#getActiveSessions()}. The owner of
+ * session through {@link SessionManager#getActiveSessions()}. The owner of
* the session may also use {@link #getSessionToken()} to allow apps without
- * this permission to create a {@link MediaController} to interact with this
+ * this permission to create a {@link SessionController} to interact with this
* session.
* <p>
* To receive commands, media keys, and other events a Callback must be set with
@@ -59,12 +60,13 @@
* <p>
* MediaSession objects are thread safe
*/
-public final class MediaSession {
- private static final String TAG = "MediaSession";
+public final class Session {
+ private static final String TAG = "Session";
private static final int MSG_MEDIA_BUTTON = 1;
private static final int MSG_COMMAND = 2;
private static final int MSG_ROUTE_CHANGE = 3;
+ private static final int MSG_ROUTE_CONNECTED = 4;
private static final String KEY_COMMAND = "command";
private static final String KEY_EXTRAS = "extras";
@@ -72,32 +74,33 @@
private final Object mLock = new Object();
- private final MediaSessionToken mSessionToken;
- private final IMediaSession mBinder;
+ private final SessionToken mSessionToken;
+ private final ISession mBinder;
private final CallbackStub mCbStub;
private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
// TODO route interfaces
- private final ArrayMap<String, RouteInterface.Stub> mInterfaces
- = new ArrayMap<String, RouteInterface.Stub>();
+ private final ArrayMap<String, RouteInterface.EventListener> mInterfaceListeners
+ = new ArrayMap<String, RouteInterface.EventListener>();
private TransportPerformer mPerformer;
+ private Route mRoute;
private boolean mPublished = false;;
/**
* @hide
*/
- public MediaSession(IMediaSession binder, CallbackStub cbStub) {
+ public Session(ISession binder, CallbackStub cbStub) {
mBinder = binder;
mCbStub = cbStub;
- IMediaController controllerBinder = null;
+ ISessionController controllerBinder = null;
try {
- controllerBinder = mBinder.getMediaController();
+ controllerBinder = mBinder.getController();
} catch (RemoteException e) {
throw new RuntimeException("Dead object in MediaSessionController constructor: ", e);
}
- mSessionToken = new MediaSessionToken(controllerBinder);
+ mSessionToken = new SessionToken(controllerBinder);
}
/**
@@ -109,6 +112,13 @@
addCallback(callback, null);
}
+ /**
+ * Add a callback to receive updates for the MediaSession. This includes
+ * events like route updates, media buttons, and focus changes.
+ *
+ * @param callback The callback to receive updates on.
+ * @param handler The handler that events should be posted on.
+ */
public void addCallback(Callback callback, Handler handler) {
if (callback == null) {
throw new IllegalArgumentException("Callback cannot be null");
@@ -126,6 +136,11 @@
}
}
+ /**
+ * Remove a callback. It will no longer receive updates.
+ *
+ * @param callback The callback to remove.
+ */
public void removeCallback(Callback callback) {
synchronized (mLock) {
removeCallbackLocked(callback);
@@ -186,30 +201,6 @@
}
/**
- * Add an interface that can be used by MediaSessions. TODO make this a
- * route provider api
- *
- * @see RouteInterface
- * @param iface The interface to add
- * @hide
- */
- public void addInterface(RouteInterface.Stub iface) {
- if (iface == null) {
- throw new IllegalArgumentException("Stub cannot be null");
- }
- String name = iface.getName();
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException("Stub must return a valid name");
- }
- if (mInterfaces.containsKey(iface)) {
- throw new IllegalArgumentException("Interface is already added");
- }
- synchronized (mLock) {
- mInterfaces.put(iface.getName(), iface);
- }
- }
-
- /**
* Send a proprietary event to all MediaControllers listening to this
* Session. It's up to the Controller/Session owner to determine the meaning
* of any events.
@@ -243,16 +234,92 @@
/**
* Retrieve a token object that can be used by apps to create a
- * {@link MediaController} for interacting with this session. The owner of
+ * {@link SessionController} for interacting with this session. The owner of
* the session is responsible for deciding how to distribute these tokens.
*
* @return A token that can be used to create a MediaController for this
* session
*/
- public MediaSessionToken getSessionToken() {
+ public SessionToken getSessionToken() {
return mSessionToken;
}
+ /**
+ * Connect to the current route using the specified request.
+ * <p>
+ * Connection updates will be sent to the callback's
+ * {@link Callback#onRouteConnected(Route)} and
+ * {@link Callback#onRouteDisconnected(Route, int)} methods. If the
+ * connection fails {@link Callback#onRouteDisconnected(Route, int)}
+ * will be called.
+ * <p>
+ * If you already have a connection to this route it will be disconnected
+ * before the new connection is established. TODO add an easy way to compare
+ * MediaRouteOptions.
+ *
+ * @param route The route the app is trying to connect to.
+ * @param request The connection request to use.
+ */
+ public void connect(RouteInfo route, RouteOptions request) {
+ if (route == null) {
+ throw new IllegalArgumentException("Must specify the route");
+ }
+ if (request == null) {
+ throw new IllegalArgumentException("Must specify the connection request");
+ }
+ try {
+ mBinder.connectToRoute(route, request);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error starting connection to route", e);
+ }
+ }
+
+ /**
+ * Disconnect from the current route. After calling you will be switched
+ * back to the default route.
+ *
+ * @param route The route to disconnect from.
+ */
+ public void disconnect(RouteInfo route) {
+ // TODO
+ }
+
+ /**
+ * Set the list of route options your app is interested in connecting to. It
+ * will be used for picking valid routes.
+ *
+ * @param options The set of route options your app may use to connect.
+ */
+ public void setRouteOptions(List<RouteOptions> options) {
+ try {
+ mBinder.setRouteOptions(options);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error setting route options.", e);
+ }
+ }
+
+ /**
+ * @hide
+ * TODO allow multiple listeners for the same interface, allow removal
+ */
+ public void addInterfaceListener(String iface,
+ RouteInterface.EventListener listener) {
+ mInterfaceListeners.put(iface, listener);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean sendRouteCommand(RouteCommand command, ResultReceiver cb) {
+ try {
+ mBinder.sendRouteCommand(command, cb);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error sending command to route.", e);
+ return false;
+ }
+ return true;
+ }
+
private MessageHandler getHandlerForCallbackLocked(Callback cb) {
if (cb == null) {
throw new IllegalArgumentException("Callback cannot be null");
@@ -297,10 +364,19 @@
}
}
- private void postRequestRouteChange(Bundle mediaRouteDescriptor) {
+ private void postRequestRouteChange(RouteInfo route) {
synchronized (mLock) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- mCallbacks.get(i).post(MSG_ROUTE_CHANGE, mediaRouteDescriptor);
+ mCallbacks.get(i).post(MSG_ROUTE_CHANGE, route);
+ }
+ }
+ }
+
+ private void postRouteConnected(RouteInfo route, RouteOptions options) {
+ synchronized (mLock) {
+ mRoute = new Route(route, options, this);
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ mCallbacks.get(i).post(MSG_ROUTE_CONNECTED, mRoute);
}
}
}
@@ -346,26 +422,49 @@
* The app is responsible for connecting to the new route and migrating
* ongoing playback if necessary.
*
- * @param descriptor
+ * @param route
*/
- public void onRequestRouteChange(Bundle descriptor) {
+ public void onRequestRouteChange(RouteInfo route) {
+ }
+
+ /**
+ * Called when a route has successfully connected. Calls to the route
+ * are now valid.
+ *
+ * @param route The route that was connected
+ */
+ public void onRouteConnected(Route route) {
+ }
+
+ /**
+ * Called when a route was disconnected. Further calls to the route will
+ * fail. If available a reason for being disconnected will be provided.
+ * <p>
+ * Valid reasons are:
+ * <ul>
+ * </ul>
+ *
+ * @param route The route that disconnected
+ * @param reason The reason for the disconnect
+ */
+ public void onRouteDisconnected(Route route, int reason) {
}
}
/**
* @hide
*/
- public static class CallbackStub extends IMediaSessionCallback.Stub {
- private WeakReference<MediaSession> mMediaSession;
+ public static class CallbackStub extends ISessionCallback.Stub {
+ private WeakReference<Session> mMediaSession;
- public void setMediaSession(MediaSession session) {
- mMediaSession = new WeakReference<MediaSession>(session);
+ public void setMediaSession(Session session) {
+ mMediaSession = new WeakReference<Session>(session);
}
@Override
public void onCommand(String command, Bundle extras, ResultReceiver cb)
throws RemoteException {
- MediaSession session = mMediaSession.get();
+ Session session = mMediaSession.get();
if (session != null) {
session.postCommand(command, extras, cb);
}
@@ -373,23 +472,31 @@
@Override
public void onMediaButton(Intent mediaButtonIntent) throws RemoteException {
- MediaSession session = mMediaSession.get();
+ Session session = mMediaSession.get();
if (session != null) {
session.postMediaButton(mediaButtonIntent);
}
}
@Override
- public void onRequestRouteChange(Bundle mediaRouteDescriptor) throws RemoteException {
- MediaSession session = mMediaSession.get();
+ public void onRequestRouteChange(RouteInfo route) throws RemoteException {
+ Session session = mMediaSession.get();
if (session != null) {
- session.postRequestRouteChange(mediaRouteDescriptor);
+ session.postRequestRouteChange(route);
+ }
+ }
+
+ @Override
+ public void onRouteConnected(RouteInfo route, RouteOptions options) {
+ Session session = mMediaSession.get();
+ if (session != null) {
+ session.postRouteConnected(route, options);
}
}
@Override
public void onPlay() throws RemoteException {
- MediaSession session = mMediaSession.get();
+ Session session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -400,7 +507,7 @@
@Override
public void onPause() throws RemoteException {
- MediaSession session = mMediaSession.get();
+ Session session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -411,7 +518,7 @@
@Override
public void onStop() throws RemoteException {
- MediaSession session = mMediaSession.get();
+ Session session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -422,7 +529,7 @@
@Override
public void onNext() throws RemoteException {
- MediaSession session = mMediaSession.get();
+ Session session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -433,7 +540,7 @@
@Override
public void onPrevious() throws RemoteException {
- MediaSession session = mMediaSession.get();
+ Session session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -444,7 +551,7 @@
@Override
public void onFastForward() throws RemoteException {
- MediaSession session = mMediaSession.get();
+ Session session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -455,7 +562,7 @@
@Override
public void onRewind() throws RemoteException {
- MediaSession session = mMediaSession.get();
+ Session session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -466,7 +573,7 @@
@Override
public void onSeekTo(long pos) throws RemoteException {
- MediaSession session = mMediaSession.get();
+ Session session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -477,7 +584,7 @@
@Override
public void onRate(Rating rating) throws RemoteException {
- MediaSession session = mMediaSession.get();
+ Session session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -486,12 +593,32 @@
}
}
+ @Override
+ public void onRouteEvent(RouteEvent event) throws RemoteException {
+ Session session = mMediaSession.get();
+ if (session != null) {
+ RouteInterface.EventListener iface
+ = session.mInterfaceListeners.get(event.getIface());
+ Log.d(TAG, "Received route event on iface " + event.getIface() + ". Listener is "
+ + iface);
+ if (iface != null) {
+ iface.onEvent(event.getEvent(), event.getExtras());
+ }
+ }
+ }
+
+ @Override
+ public void onRouteStateChange(int state) throws RemoteException {
+ // TODO
+
+ }
+
}
private class MessageHandler extends Handler {
- private MediaSession.Callback mCallback;
+ private Session.Callback mCallback;
- public MessageHandler(Looper looper, MediaSession.Callback callback) {
+ public MessageHandler(Looper looper, Session.Callback callback) {
super(looper, null, true);
mCallback = callback;
}
@@ -511,11 +638,13 @@
mCallback.onCommand(cmd.command, cmd.extras, cmd.stub);
break;
case MSG_ROUTE_CHANGE:
- mCallback.onRequestRouteChange((Bundle) msg.obj);
+ mCallback.onRequestRouteChange((RouteInfo) msg.obj);
+ break;
+ case MSG_ROUTE_CONNECTED:
+ mCallback.onRouteConnected((Route) msg.obj);
break;
}
}
- msg.recycle();
}
public void post(int what, Object obj) {
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/SessionController.java
similarity index 83%
rename from media/java/android/media/session/MediaController.java
rename to media/java/android/media/session/SessionController.java
index afd8b11..dc4f7d9 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/SessionController.java
@@ -34,21 +34,21 @@
* other commands can be sent to the session. A callback may be registered to
* receive updates from the session, such as metadata and play state changes.
* <p>
- * A MediaController can be created through {@link MediaSessionManager} if you
+ * A MediaController can be created through {@link SessionManager} if you
* hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or directly if
- * you have a {@link MediaSessionToken} from the session owner.
+ * you have a {@link SessionToken} from the session owner.
* <p>
* MediaController objects are thread-safe.
*/
-public final class MediaController {
- private static final String TAG = "MediaController";
+public final class SessionController {
+ private static final String TAG = "SessionController";
private static final int MSG_EVENT = 1;
private static final int MESSAGE_PLAYBACK_STATE = 2;
private static final int MESSAGE_METADATA = 3;
private static final int MSG_ROUTE = 4;
- private final IMediaController mSessionBinder;
+ private final ISessionController mSessionBinder;
private final CallbackStub mCbStub = new CallbackStub(this);
private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
@@ -58,15 +58,15 @@
private TransportController mTransportController;
- private MediaController(IMediaController sessionBinder) {
+ private SessionController(ISessionController sessionBinder) {
mSessionBinder = sessionBinder;
}
/**
* @hide
*/
- public static MediaController fromBinder(IMediaController sessionBinder) {
- MediaController controller = new MediaController(sessionBinder);
+ public static SessionController fromBinder(ISessionController sessionBinder) {
+ SessionController controller = new SessionController(sessionBinder);
try {
controller.mSessionBinder.registerCallbackListener(controller.mCbStub);
if (controller.mSessionBinder.isTransportControlEnabled()) {
@@ -87,7 +87,7 @@
* @param token The session token to use
* @return A controller for the session or null
*/
- public static MediaController fromToken(MediaSessionToken token) {
+ public static SessionController fromToken(SessionToken token) {
return fromBinder(token.getBinder());
}
@@ -181,10 +181,22 @@
}
}
+ /**
+ * Request that the route picker be shown for this session. This should
+ * generally be called in response to a user action.
+ */
+ public void showRoutePicker() {
+ try {
+ mSessionBinder.showRoutePicker();
+ } catch (RemoteException e) {
+ Log.d(TAG, "Dead object in showRoutePicker", e);
+ }
+ }
+
/*
* @hide
*/
- IMediaController getSessionBinder() {
+ ISessionController getSessionBinder() {
return mSessionBinder;
}
@@ -247,10 +259,10 @@
}
}
- private void postRouteChanged(Bundle routeDescriptor) {
+ private void postRouteChanged(RouteInfo route) {
synchronized (mLock) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- mCallbacks.get(i).post(MSG_ROUTE, null, routeDescriptor);
+ mCallbacks.get(i).post(MSG_ROUTE, route, null);
}
}
}
@@ -275,36 +287,36 @@
*
* @param route
*/
- public void onRouteChanged(Bundle route) {
+ public void onRouteChanged(RouteInfo route) {
}
}
- private final static class CallbackStub extends IMediaControllerCallback.Stub {
- private final WeakReference<MediaController> mController;
+ private final static class CallbackStub extends ISessionControllerCallback.Stub {
+ private final WeakReference<SessionController> mController;
- public CallbackStub(MediaController controller) {
- mController = new WeakReference<MediaController>(controller);
+ public CallbackStub(SessionController controller) {
+ mController = new WeakReference<SessionController>(controller);
}
@Override
public void onEvent(String event, Bundle extras) {
- MediaController controller = mController.get();
+ SessionController controller = mController.get();
if (controller != null) {
controller.postEvent(event, extras);
}
}
@Override
- public void onRouteChanged(Bundle mediaRouteDescriptor) {
- MediaController controller = mController.get();
+ public void onRouteChanged(RouteInfo route) {
+ SessionController controller = mController.get();
if (controller != null) {
- controller.postRouteChanged(mediaRouteDescriptor);
+ controller.postRouteChanged(route);
}
}
@Override
public void onPlaybackStateChanged(PlaybackState state) {
- MediaController controller = mController.get();
+ SessionController controller = mController.get();
if (controller != null) {
TransportController tc = controller.getTransportController();
if (tc != null) {
@@ -315,7 +327,7 @@
@Override
public void onMetadataChanged(MediaMetadata metadata) {
- MediaController controller = mController.get();
+ SessionController controller = mController.get();
if (controller != null) {
TransportController tc = controller.getTransportController();
if (tc != null) {
@@ -327,9 +339,9 @@
}
private final static class MessageHandler extends Handler {
- private final MediaController.Callback mCallback;
+ private final SessionController.Callback mCallback;
- public MessageHandler(Looper looper, MediaController.Callback cb) {
+ public MessageHandler(Looper looper, SessionController.Callback cb) {
super(looper, null, true);
mCallback = cb;
}
@@ -341,7 +353,7 @@
mCallback.onEvent((String) msg.obj, msg.getData());
break;
case MSG_ROUTE:
- mCallback.onRouteChanged(msg.getData());
+ mCallback.onRouteChanged((RouteInfo) msg.obj);
}
}
diff --git a/media/java/android/media/session/SessionInfo.java b/media/java/android/media/session/SessionInfo.java
new file mode 100644
index 0000000..22d8ab1
--- /dev/null
+++ b/media/java/android/media/session/SessionInfo.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.media.session;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Information about a media session, including the owner's package name.
+ */
+public final class SessionInfo implements Parcelable {
+ private final String mId;
+ private final String mPackageName;
+
+ /**
+ * @hide
+ */
+ public SessionInfo(String id, String packageName) {
+ mId = id;
+ mPackageName = packageName;
+ }
+
+ private SessionInfo(Parcel in) {
+ mId = in.readString();
+ mPackageName = in.readString();
+ }
+
+ /**
+ * Get the package name of the owner of this session.
+ *
+ * @return The owner's package name
+ */
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * Get the unique id for this session.
+ *
+ * @return The id for the session.
+ */
+ public String getId() {
+ return mId;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mId);
+ dest.writeString(mPackageName);
+ }
+
+ public static final Parcelable.Creator<SessionInfo> CREATOR
+ = new Parcelable.Creator<SessionInfo>() {
+ @Override
+ public SessionInfo createFromParcel(Parcel in) {
+ return new SessionInfo(in);
+ }
+
+ @Override
+ public SessionInfo[] newArray(int size) {
+ return new SessionInfo[size];
+ }
+ };
+}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/SessionManager.java
similarity index 75%
rename from media/java/android/media/session/MediaSessionManager.java
rename to media/java/android/media/session/SessionManager.java
index e3f2d9c..15bf0e3 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/SessionManager.java
@@ -17,7 +17,7 @@
package android.media.session;
import android.content.Context;
-import android.media.session.IMediaSessionManager;
+import android.media.session.ISessionManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -35,37 +35,37 @@
* get an instance of this class.
* <p>
*
- * @see MediaSession
- * @see MediaController
+ * @see Session
+ * @see SessionController
*/
-public final class MediaSessionManager {
- private static final String TAG = "MediaSessionManager";
+public final class SessionManager {
+ private static final String TAG = "SessionManager";
- private final IMediaSessionManager mService;
+ private final ISessionManager mService;
private Context mContext;
/**
* @hide
*/
- public MediaSessionManager(Context context) {
+ public SessionManager(Context context) {
// Consider rewriting like DisplayManagerGlobal
// Decide if we need context
mContext = context;
IBinder b = ServiceManager.getService(Context.MEDIA_SESSION_SERVICE);
- mService = IMediaSessionManager.Stub.asInterface(b);
+ mService = ISessionManager.Stub.asInterface(b);
}
/**
* Creates a new session.
*
* @param tag A short name for debugging purposes
- * @return a {@link MediaSession} for the new session
+ * @return a {@link Session} for the new session
*/
- public MediaSession createSession(String tag) {
+ public Session createSession(String tag) {
try {
- MediaSession.CallbackStub cbStub = new MediaSession.CallbackStub();
- MediaSession session = new MediaSession(mService
+ Session.CallbackStub cbStub = new Session.CallbackStub();
+ Session session = new Session(mService
.createSession(mContext.getPackageName(), cbStub, tag), cbStub);
cbStub.setMediaSession(session);
@@ -83,8 +83,8 @@
*
* @return a list of controllers for ongoing sessions
*/
- public List<MediaController> getActiveSessions() {
+ public List<SessionController> getActiveSessions() {
// TODO
- return new ArrayList<MediaController>();
+ return new ArrayList<SessionController>();
}
}
diff --git a/media/java/android/media/session/MediaSessionToken.aidl b/media/java/android/media/session/SessionToken.aidl
similarity index 95%
copy from media/java/android/media/session/MediaSessionToken.aidl
copy to media/java/android/media/session/SessionToken.aidl
index 5812682..db35f85 100644
--- a/media/java/android/media/session/MediaSessionToken.aidl
+++ b/media/java/android/media/session/SessionToken.aidl
@@ -15,4 +15,4 @@
package android.media.session;
-parcelable MediaSessionToken;
+parcelable SessionToken;
diff --git a/media/java/android/media/session/MediaSessionToken.java b/media/java/android/media/session/SessionToken.java
similarity index 62%
rename from media/java/android/media/session/MediaSessionToken.java
rename to media/java/android/media/session/SessionToken.java
index dbb4964..59486f6 100644
--- a/media/java/android/media/session/MediaSessionToken.java
+++ b/media/java/android/media/session/SessionToken.java
@@ -16,28 +16,28 @@
package android.media.session;
-import android.media.session.IMediaController;
+import android.media.session.ISessionController;
import android.os.Parcel;
import android.os.Parcelable;
-public class MediaSessionToken implements Parcelable {
- private IMediaController mBinder;
+public class SessionToken implements Parcelable {
+ private ISessionController mBinder;
/**
* @hide
*/
- MediaSessionToken(IMediaController binder) {
+ SessionToken(ISessionController binder) {
mBinder = binder;
}
- private MediaSessionToken(Parcel in) {
- mBinder = IMediaController.Stub.asInterface(in.readStrongBinder());
+ private SessionToken(Parcel in) {
+ mBinder = ISessionController.Stub.asInterface(in.readStrongBinder());
}
/**
* @hide
*/
- IMediaController getBinder() {
+ ISessionController getBinder() {
return mBinder;
}
@@ -51,16 +51,16 @@
dest.writeStrongBinder(mBinder.asBinder());
}
- public static final Parcelable.Creator<MediaSessionToken> CREATOR
- = new Parcelable.Creator<MediaSessionToken>() {
+ public static final Parcelable.Creator<SessionToken> CREATOR
+ = new Parcelable.Creator<SessionToken>() {
@Override
- public MediaSessionToken createFromParcel(Parcel in) {
- return new MediaSessionToken(in);
+ public SessionToken createFromParcel(Parcel in) {
+ return new SessionToken(in);
}
@Override
- public MediaSessionToken[] newArray(int size) {
- return new MediaSessionToken[size];
+ public SessionToken[] newArray(int size) {
+ return new SessionToken[size];
}
};
}
diff --git a/media/java/android/media/session/TransportController.java b/media/java/android/media/session/TransportController.java
index 15b11f3..9574df6 100644
--- a/media/java/android/media/session/TransportController.java
+++ b/media/java/android/media/session/TransportController.java
@@ -34,12 +34,12 @@
private final Object mLock = new Object();
private final ArrayList<MessageHandler> mListeners = new ArrayList<MessageHandler>();
- private final IMediaController mBinder;
+ private final ISessionController mBinder;
/**
* @hide
*/
- public TransportController(IMediaController binder) {
+ public TransportController(ISessionController binder) {
mBinder = binder;
}
diff --git a/media/java/android/media/session/TransportPerformer.java b/media/java/android/media/session/TransportPerformer.java
index b96db20..eddffd1 100644
--- a/media/java/android/media/session/TransportPerformer.java
+++ b/media/java/android/media/session/TransportPerformer.java
@@ -34,12 +34,12 @@
private final Object mLock = new Object();
private final ArrayList<MessageHandler> mListeners = new ArrayList<MessageHandler>();
- private IMediaSession mBinder;
+ private ISession mBinder;
/**
* @hide
*/
- public TransportPerformer(IMediaSession binder) {
+ public TransportPerformer(ISession binder) {
mBinder = binder;
}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 7c45682..4fbd2a4 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -297,8 +297,6 @@
}
JDrm::~JDrm() {
- mDrm.clear();
-
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->DeleteWeakGlobalRef(mObject);
@@ -363,6 +361,13 @@
}
}
+void JDrm::disconnect() {
+ if (mDrm != NULL) {
+ mDrm->destroyPlugin();
+ mDrm.clear();
+ }
+}
+
// static
bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) {
@@ -527,6 +532,7 @@
sp<JDrm> drm = setDrm(env, thiz, NULL);
if (drm != NULL) {
drm->setListener(NULL);
+ drm->disconnect();
}
}
diff --git a/media/jni/android_media_MediaDrm.h b/media/jni/android_media_MediaDrm.h
index 620ad28..b7b8e5d 100644
--- a/media/jni/android_media_MediaDrm.h
+++ b/media/jni/android_media_MediaDrm.h
@@ -47,6 +47,8 @@
void notify(DrmPlugin::EventType, int extra, const Parcel *obj);
status_t setListener(const sp<DrmListener>& listener);
+ void disconnect();
+
protected:
virtual ~JDrm();
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 0e55228..d781336 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -859,6 +859,7 @@
result = malloc(exifdata->size);
if (result) {
memcpy(result, exifdata->data, exifdata->size);
+ outThumbSize = exifdata->size;
}
}
exif_data_unref(exifdata);
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index a9322b9..1406f6b 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -878,7 +878,7 @@
protected int[] mConfigSpec;
private int[] filterConfigSpec(int[] configSpec) {
- if (mEGLContextClientVersion != 2) {
+ if (mEGLContextClientVersion != 2 && mEGLContextClientVersion != 3) {
return configSpec;
}
/* We know none of the subclasses define EGL_RENDERABLE_TYPE.
@@ -888,7 +888,11 @@
int[] newConfigSpec = new int[len + 2];
System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
- newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
+ if (mEGLContextClientVersion == 2) {
+ newConfigSpec[len] = EGL14.EGL_OPENGL_ES2_BIT; /* EGL_OPENGL_ES2_BIT */
+ } else {
+ newConfigSpec[len] = EGLExt.EGL_OPENGL_ES3_BIT_KHR; /* EGL_OPENGL_ES3_BIT_KHR */
+ }
newConfigSpec[len+1] = EGL10.EGL_NONE;
return newConfigSpec;
}
diff --git a/packages/DefaultContainerService/Android.mk b/packages/DefaultContainerService/Android.mk
index 9961168..0de2c1f 100644
--- a/packages/DefaultContainerService/Android.mk
+++ b/packages/DefaultContainerService/Android.mk
@@ -7,7 +7,7 @@
LOCAL_PACKAGE_NAME := DefaultContainerService
-LOCAL_REQUIRED_MODULES := libdefcontainer_jni
+LOCAL_JNI_SHARED_LIBRARIES := libdefcontainer_jni
LOCAL_CERTIFICATE := platform
diff --git a/packages/Keyguard/res/layout/keyguard_bouncer.xml b/packages/Keyguard/res/layout/keyguard_bouncer.xml
index dedf427..8716ebc 100644
--- a/packages/Keyguard/res/layout/keyguard_bouncer.xml
+++ b/packages/Keyguard/res/layout/keyguard_bouncer.xml
@@ -24,8 +24,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- <include layout="@layout/keyguard_simple_host_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
+ <include
+ style="@style/BouncerSecurityContainer"
+ layout="@layout/keyguard_simple_host_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
</FrameLayout>
diff --git a/packages/Keyguard/res/layout/keyguard_sim_pin_view.xml b/packages/Keyguard/res/layout/keyguard_sim_pin_view.xml
index e96220e..0e2b33a 100644
--- a/packages/Keyguard/res/layout/keyguard_sim_pin_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_sim_pin_view.xml
@@ -52,7 +52,7 @@
android:orientation="horizontal"
android:layout_weight="1"
>
- <TextView android:id="@+id/pinEntry"
+ <TextView android:id="@+id/simPinEntry"
android:editable="true"
android:layout_width="0dip"
android:layout_height="match_parent"
@@ -96,7 +96,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/simPinEntry"
androidprv:digit="1"
/>
<view class="com.android.keyguard.NumPadKey"
@@ -105,7 +105,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/simPinEntry"
androidprv:digit="2"
/>
<view class="com.android.keyguard.NumPadKey"
@@ -114,7 +114,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/simPinEntry"
androidprv:digit="3"
/>
</LinearLayout>
@@ -130,7 +130,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/simPinEntry"
androidprv:digit="4"
/>
<view class="com.android.keyguard.NumPadKey"
@@ -139,7 +139,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/simPinEntry"
androidprv:digit="5"
/>
<view class="com.android.keyguard.NumPadKey"
@@ -148,7 +148,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/simPinEntry"
androidprv:digit="6"
/>
</LinearLayout>
@@ -164,7 +164,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/simPinEntry"
androidprv:digit="7"
/>
<view class="com.android.keyguard.NumPadKey"
@@ -173,7 +173,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/simPinEntry"
androidprv:digit="8"
/>
<view class="com.android.keyguard.NumPadKey"
@@ -182,7 +182,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/simPinEntry"
androidprv:digit="9"
/>
</LinearLayout>
@@ -203,7 +203,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/simPinEntry"
androidprv:digit="0"
/>
<ImageButton
diff --git a/packages/Keyguard/res/layout/keyguard_sim_puk_view.xml b/packages/Keyguard/res/layout/keyguard_sim_puk_view.xml
index bf15ba0..88049a7 100644
--- a/packages/Keyguard/res/layout/keyguard_sim_puk_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_sim_puk_view.xml
@@ -53,7 +53,7 @@
android:orientation="horizontal"
android:layout_weight="1"
>
- <TextView android:id="@+id/pinEntry"
+ <TextView android:id="@+id/pukEntry"
android:editable="true"
android:layout_width="0dip"
android:layout_height="match_parent"
@@ -97,7 +97,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/pukEntry"
androidprv:digit="1"
/>
<view class="com.android.keyguard.NumPadKey"
@@ -106,7 +106,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/pukEntry"
androidprv:digit="2"
/>
<view class="com.android.keyguard.NumPadKey"
@@ -115,7 +115,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/pukEntry"
androidprv:digit="3"
/>
</LinearLayout>
@@ -131,7 +131,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/pukEntry"
androidprv:digit="4"
/>
<view class="com.android.keyguard.NumPadKey"
@@ -140,7 +140,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/pukEntry"
androidprv:digit="5"
/>
<view class="com.android.keyguard.NumPadKey"
@@ -149,7 +149,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/pukEntry"
androidprv:digit="6"
/>
</LinearLayout>
@@ -165,7 +165,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/pukEntry"
androidprv:digit="7"
/>
<view class="com.android.keyguard.NumPadKey"
@@ -174,7 +174,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/pukEntry"
androidprv:digit="8"
/>
<view class="com.android.keyguard.NumPadKey"
@@ -183,7 +183,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/pukEntry"
androidprv:digit="9"
/>
</LinearLayout>
@@ -204,7 +204,7 @@
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
- androidprv:textView="@+id/pinEntry"
+ androidprv:textView="@+id/pukEntry"
androidprv:digit="0"
/>
<ImageButton
diff --git a/packages/Keyguard/res/values-sw600dp/styles.xml b/packages/Keyguard/res/values-sw600dp/styles.xml
new file mode 100644
index 0000000..e632e76
--- /dev/null
+++ b/packages/Keyguard/res/values-sw600dp/styles.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <style name="BouncerSecurityContainer">
+ <item name="android:layout_gravity">center</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/Keyguard/res/values/styles.xml b/packages/Keyguard/res/values/styles.xml
index 4a034aa..b54ac50 100644
--- a/packages/Keyguard/res/values/styles.xml
+++ b/packages/Keyguard/res/values/styles.xml
@@ -81,4 +81,9 @@
<item name="android:mirrorForRtl">true</item>
</style>
+ <style name="BouncerSecurityContainer">
+ <item name="android:layout_marginBottom">32dp</item>
+ <item name="android:layout_gravity">center_horizontal|bottom</item>
+ </style>
+
</resources>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
index d6a4f52..4791956 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
@@ -87,7 +87,7 @@
@Override
protected int getPasswordTextViewId() {
- return R.id.pinEntry;
+ return R.id.simPinEntry;
}
@Override
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
index 04cbde1..b9c7f51 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
@@ -138,7 +138,7 @@
@Override
protected int getPasswordTextViewId() {
- return R.id.pinEntry;
+ return R.id.pukEntry;
}
@Override
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index d371d70..327df8d 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -273,7 +273,8 @@
<service
android:name=".keyguard.KeyguardService"
- android:exported="true" />
+ android:exported="true"
+ android:enabled="@bool/config_enableKeyguardService" />
<activity android:name=".Somnambulator"
android:label="@string/start_dreams"
diff --git a/packages/SystemUI/res/drawable-hdpi/search_bg_transparent.9.png b/packages/SystemUI/res/drawable-hdpi/search_bg_transparent.9.png
new file mode 100644
index 0000000..85db9c8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/search_bg_transparent.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/search_bg_transparent.9.png b/packages/SystemUI/res/drawable-mdpi/search_bg_transparent.9.png
new file mode 100644
index 0000000..c4941a6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/search_bg_transparent.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/search_bg_transparent.9.png b/packages/SystemUI/res/drawable-xhdpi/search_bg_transparent.9.png
new file mode 100644
index 0000000..4618f40
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/search_bg_transparent.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_task_shadow.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_task_shadow.9.png
new file mode 100644
index 0000000..36e7e45
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/recents_task_shadow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/search_bg_transparent.9.png b/packages/SystemUI/res/drawable-xxhdpi/search_bg_transparent.9.png
new file mode 100644
index 0000000..c0bf31d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/search_bg_transparent.9.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/flip_settings.xml b/packages/SystemUI/res/layout/flip_settings.xml
index 28d9625..f3c1b90 100644
--- a/packages/SystemUI/res/layout/flip_settings.xml
+++ b/packages/SystemUI/res/layout/flip_settings.xml
@@ -22,4 +22,5 @@
android:layout_height="wrap_content"
android:background="#5f000000"
android:animateLayoutChanges="true"
+ android:visibility="gone"
android:columnCount="@integer/quick_settings_num_columns" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
new file mode 100644
index 0000000..88b01c7
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -0,0 +1,34 @@
+<?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
+ -->
+
+<com.android.systemui.statusbar.phone.KeyguardBottomAreaView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
+ android:id="@+id/keyguard_bottom_area"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ >
+ <com.android.systemui.statusbar.policy.KeyButtonView
+ android:id="@+id/camera_button"
+ android:layout_height="80dp"
+ android:layout_width="80dp"
+ android:layout_gravity="bottom|right"
+ android:src="@drawable/ic_sysbar_camera"
+ android:scaleType="center"
+ android:contentDescription="@string/accessibility_camera_button"
+ systemui:glowBackground="@drawable/ic_sysbar_highlight_land" />
+</com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index a6fb443..2398849 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -159,18 +159,6 @@
android:visibility="gone"
android:contentDescription="@string/accessibility_search_light"
/>
-
- <com.android.systemui.statusbar.policy.KeyButtonView
- android:id="@+id/camera_button"
- android:layout_height="match_parent"
- android:layout_width="80dp"
- android:layout_gravity="center_vertical|right"
- android:src="@drawable/ic_sysbar_camera"
- android:scaleType="center"
- android:visibility="gone"
- android:contentDescription="@string/accessibility_camera_button"
- systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
- />
</FrameLayout>
<com.android.systemui.statusbar.policy.DeadZone
diff --git a/packages/SystemUI/res/layout/quick_settings.xml b/packages/SystemUI/res/layout/quick_settings.xml
deleted file mode 100644
index 75082ba..0000000
--- a/packages/SystemUI/res/layout/quick_settings.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<com.android.systemui.statusbar.phone.SettingsPanelView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/settings_panel"
- android:background="@drawable/notification_panel_bg"
- >
- <!-- TODO: Put into ScrollView -->
- <com.android.systemui.statusbar.phone.QuickSettingsScrollView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/close_handle_underlap"
- android:overScrollMode="ifContentScrolls"
- >
- <com.android.systemui.statusbar.phone.QuickSettingsContainerView
- android:id="@+id/quick_settings_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:animateLayoutChanges="true"
- android:columnCount="@integer/quick_settings_num_columns"
- />
- </com.android.systemui.statusbar.phone.QuickSettingsScrollView>
-
- <View
- android:id="@+id/handle"
- android:layout_width="match_parent"
- android:layout_height="@dimen/close_handle_height"
- android:background="@drawable/status_bar_close"
- android:visibility="invisible"
- />
-
-</com.android.systemui.statusbar.phone.SettingsPanelView >
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_search_bar.xml b/packages/SystemUI/res/layout/recents_search_bar.xml
new file mode 100644
index 0000000..915283e
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_search_bar.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/search_bg_transparent">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/recents_search_bar_label"
+ android:textColor="#99ffffff"
+ android:textSize="18sp"
+ android:textAllCaps="true" />
+</FrameLayout>
+
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 7f64032..4442bca 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -63,13 +63,6 @@
android:maxLines="2"
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
- <ImageView
- android:id="@+id/activity_icon"
- android:layout_width="@dimen/recents_task_view_activity_icon_size"
- android:layout_height="@dimen/recents_task_view_activity_icon_size"
- android:layout_gravity="center_vertical|end"
- android:padding="12dp"
- android:visibility="invisible" />
</com.android.systemui.recents.views.TaskBarView>
</com.android.systemui.recents.views.TaskView>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 8a3f090..69fbc1b 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -36,8 +36,9 @@
android:layout_gravity="bottom"
/>
- <ViewStub android:id="@+id/keyguard_flip_stub"
- android:layout="@layout/status_bar_flip_button"
+ <include
+ layout="@layout/status_bar_flip_button"
+ android:id="@+id/keyguard_flipper"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="right|top"
@@ -76,8 +77,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
- <ViewStub android:id="@+id/flip_settings_stub"
- android:layout="@layout/flip_settings"
+ <include
+ layout="@layout/flip_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
@@ -89,4 +90,8 @@
/>
</FrameLayout>
</LinearLayout>
+
+ <include
+ layout="@layout/keyguard_bottom_area"
+ android:visibility="gone" />
</com.android.systemui.statusbar.phone.NotificationPanelView><!-- end of sliding panel -->
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 56523db..8975728 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -84,6 +84,7 @@
/>
<include layout="@layout/status_bar_flip_button"
+ android:id="@+id/header_flipper"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="12dp" />
diff --git a/packages/SystemUI/res/layout/status_bar_flip_button.xml b/packages/SystemUI/res/layout/status_bar_flip_button.xml
index db672ea..b7dff8c 100644
--- a/packages/SystemUI/res/layout/status_bar_flip_button.xml
+++ b/packages/SystemUI/res/layout/status_bar_flip_button.xml
@@ -16,7 +16,6 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/settings_button_holder"
android:layout_width="50dp"
android:layout_height="50dp">
<ImageView android:id="@+id/settings_button"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 2b01a06..61d43d7 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -23,6 +23,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true"
+ android:fitsSystemWindows="true"
android:descendantFocusability="afterDescendants">
<include layout="@layout/status_bar"
@@ -33,16 +34,11 @@
android:id="@+id/panel_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginTop="@dimen/panel_holder_padding_top"
- android:layout_marginBottom="@dimen/navigation_bar_height">
+ android:layout_marginTop="@dimen/panel_holder_padding_top">
<include layout="@layout/status_bar_expanded"
android:layout_width="@dimen/notification_panel_width"
android:layout_height="wrap_content"
android:layout_gravity="start|top" />
- <ViewStub android:id="@+id/quick_settings_stub"
- android:layout="@layout/quick_settings"
- android:layout_width="@dimen/notification_panel_width"
- android:layout_height="match_parent" />
</com.android.systemui.statusbar.phone.PanelHolder>
</com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index c6bc44d..fe2224e 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -20,9 +20,6 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. -->
<resources>
- <!-- Enable quick settings on tablets -->
- <bool name="config_hasSettingsPanel">true</bool>
-
<!-- The number of columns in the QuickSettings -->
<integer name="quick_settings_num_columns">3</integer>
@@ -31,7 +28,4 @@
<!-- The number of columns that the top level tiles span in the QuickSettings -->
<integer name="quick_settings_user_time_settings_tile_span">1</integer>
-
- <!-- Enable the "flip settings" panel -->
- <bool name="config_hasFlipSettingsPanel">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index b4fafec..5b5587d 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -23,10 +23,9 @@
<dimen name="notification_panel_margin_bottom">192dp</dimen>
<dimen name="notification_panel_margin_left">16dp</dimen>
- <!-- Gravity for the notification & quick settings panels -->
- <!-- 0x31 = top|center_horizontal ; 0x800035 = end|top -->
+ <!-- Gravity for the notification panel -->
+ <!-- 0x31 = top|center_horizontal -->
<integer name="notification_panel_layout_gravity">0x31</integer>
- <integer name="settings_panel_layout_gravity">0x800035</integer>
<!-- Diameter of outer shape drawable shown in navbar search-->
<dimen name="navbar_search_outerring_diameter">430dip</dimen>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f908a1e..722ca15 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -72,7 +72,7 @@
<!-- decay duration (from size_max -> size), in ms -->
<integer name="navigation_bar_deadzone_hold">333</integer>
<integer name="navigation_bar_deadzone_decay">333</integer>
-
+
<bool name="config_dead_zone_flash">false</bool>
<!-- Min alpha % that recent items will fade to while being dismissed -->
@@ -99,11 +99,8 @@
<integer name="blinds_pop_duration_ms">10</integer>
- <!-- Disable the separate quick settings panel -->
- <bool name="config_hasSettingsPanel">true</bool>
-
- <!-- Enable the "flip settings" panel -->
- <bool name="config_hasFlipSettingsPanel">true</bool>
+ <!-- The device supports quick settings. -->
+ <bool name="config_hasQuickSettings">true</bool>
<!-- Should "4G" be shown instead of "LTE" when the network is NETWORK_TYPE_LTE? -->
<bool name="config_show4GForLTE">true</bool>
@@ -125,6 +122,9 @@
<!-- The minimum alpha for the dim applied to cards that go deeper into the stack. -->
<integer name="recents_max_task_stack_view_dim">96</integer>
+ <!-- Whether to enable KeyguardService or not -->
+ <bool name="config_enableKeyguardService">true</bool>
+
<!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
card. -->
<integer name="keyguard_max_notification_count">4</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9aacf42..0604817 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -174,18 +174,9 @@
<dimen name="notification_panel_margin_bottom">0dp</dimen>
<dimen name="notification_panel_margin_left">0dp</dimen>
- <!-- Gravity for the notification & quick settings panels -->
+ <!-- Gravity for the notification panel -->
<!-- 0x37 = fill_horizontal|top -->
<integer name="notification_panel_layout_gravity">0x37</integer>
- <integer name="settings_panel_layout_gravity">0x37</integer>
-
- <!-- Fraction of the status bar that, when dragged, will produce the quick settings panel
- instead of the notification panel. See also @dimen/settings_panel_dragzone_min.
- If zero, the settings panel will not be directly draggable from the status bar. -->
- <item type="dimen" name="settings_panel_dragzone_fraction">0%</item>
-
- <!-- Quick settings dragzone, if used, should be at least this big (may be zero). -->
- <dimen name="settings_panel_dragzone_min">100dp</dimen>
<!-- Height of the carrier/wifi name label -->
<dimen name="carrier_label_height">24dp</dimen>
@@ -245,6 +236,12 @@
<!-- The amount of space a user has to scroll to dismiss any info panes. -->
<dimen name="recents_task_stack_scroll_dismiss_info_pane_distance">50dp</dimen>
+ <!-- The height of the search bar space. -->
+ <dimen name="recents_search_bar_space_height">40dp</dimen>
+
+ <!-- The search bar edge margins. -->
+ <dimen name="recents_search_bar_space_edge_margins">12dp</dimen>
+
<!-- Used to calculate the translation animation duration, the expected amount of movement
in dps over one second of time. -->
<dimen name="recents_animation_movement_in_dps_per_second">800dp</dimen>
@@ -253,7 +250,7 @@
<dimen name="notification_stack_margin_bottom">0dp</dimen>
<!-- Space reserved for the cards behind the top card in the top stack -->
- <dimen name="top_stack_peek_amount">24dp</dimen>
+ <dimen name="top_stack_peek_amount">12dp</dimen>
<!-- Space reserved for the cards behind the top card in the bottom stack -->
<dimen name="bottom_stack_peek_amount">18dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 73e5e19..f3c956c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -511,6 +511,8 @@
<string name="recents_empty_message">RECENTS</string>
<!-- Recents: The info panel app info button string. [CHAR LIMIT=NONE] -->
<string name="recents_app_info_button_label">Application Info</string>
+ <!-- Recents: Temporary string for the button in the recents search bar. [CHAR LIMIT=NONE] -->
+ <string name="recents_search_bar_label">search</string>
<!-- Glyph to be overlaid atop the battery when the level is extremely low. Do not translate. -->
@@ -537,6 +539,9 @@
<!-- Zen mode: Summary notification content text. [CHAR LIMIT=NONE] -->
<string name="zen_mode_notification_text">Touch to show</string>
+ <!-- Zen mode: Short title. [CHAR LIMIT=40] -->
+ <string name="zen_mode_title">Do not disturb</string>
+
<!-- Text for overflow card on Keyguard when there is not enough space for all notifications on Keyguard. [CHAR LIMIT=12] -->
<plurals name="keyguard_more_overflow_text">
<item quantity="other">%d more</item>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 1832d37..8dd3f8d 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -195,6 +195,12 @@
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);
@@ -549,8 +555,9 @@
mScaleAnimation.setFloatValues(targetHeight);
mScaleAnimation.setupStartValues();
mScaleAnimation.start();
+ } else {
+ mCallback.setUserLockedChild(mCurrView, false);
}
- mCallback.setUserLockedChild(mCurrView, false);
mExpanding = false;
mExpansionStyle = NONE;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index cbfc266..081e8de 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1271,11 +1271,6 @@
// (like recents). Temporary enable/disable (e.g. the "back" button) are
// done in KeyguardHostView.
flags |= StatusBarManager.DISABLE_RECENT;
- if ((isSecure() && !mAllowNotificationsWhenSecure)
- || !ENABLE_INSECURE_STATUS_BAR_EXPAND) {
- // showing secure lockscreen; disable expanding.
- flags |= StatusBarManager.DISABLE_EXPAND;
- }
if (isSecure()) {
// showing secure lockscreen; disable ticker and switch private notifications
// to show their public versions, if available.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 4fb90cb..f2e322d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -44,6 +44,7 @@
import android.view.WindowManager;
import com.android.systemui.R;
+import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -240,12 +241,10 @@
}
/** Returns whether there is are multiple recents tasks */
- boolean hasMultipleRecentsTask() {
+ boolean hasMultipleRecentsTask(List<ActivityManager.RecentTaskInfo> tasks) {
// NOTE: Currently there's no method to get the number of non-home tasks, so we have to
// compute this ourselves
SystemServicesProxy ssp = mSystemServicesProxy;
- List<ActivityManager.RecentTaskInfo> tasks = ssp.getRecentTasks(4,
- UserHandle.CURRENT.getIdentifier());
Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
while (iter.hasNext()) {
ActivityManager.RecentTaskInfo t = iter.next();
@@ -259,6 +258,17 @@
return (tasks.size() > 1);
}
+ /** Returns whether the base intent of the top task stack was launched with the flag
+ * Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS. */
+ boolean isTopTaskExcludeFromRecents(List<ActivityManager.RecentTaskInfo> tasks) {
+ if (tasks.size() > 0) {
+ ActivityManager.RecentTaskInfo t = tasks.get(0);
+ Console.log(t.baseIntent.toString());
+ return (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
+ }
+ return false;
+ }
+
/** Converts from the device rotation to the degree */
float getDegreesForRotation(int value) {
switch (value) {
@@ -334,10 +344,18 @@
isTopTaskHome = ssp.isInHomeStack(topTask.id);
}
- // Otherwise, Recents is not the front-most activity and we should animate into it
- boolean hasMultipleTasks = hasMultipleRecentsTask();
+ // Otherwise, Recents is not the front-most activity and we should animate into it. If
+ // the activity at the root of the top task stack is excluded from recents, or if that
+ // task stack is in the home stack, then we just do a simple transition. Otherwise, we
+ // animate to the rects defined by the Recents service, which can differ depending on the
+ // number of items in the list.
+ List<ActivityManager.RecentTaskInfo> recentTasks =
+ ssp.getRecentTasks(4, UserHandle.CURRENT.getIdentifier());
+ boolean hasMultipleTasks = hasMultipleRecentsTask(recentTasks);
+ boolean isTaskExcludedFromRecents = isTopTaskExcludeFromRecents(recentTasks);
Rect taskRect = hasMultipleTasks ? mMultipleCountFirstTaskRect : mSingleCountFirstTaskRect;
- if (!isTopTaskHome && taskRect != null && taskRect.width() > 0 && taskRect.height() > 0) {
+ if (!isTopTaskHome && !isTaskExcludedFromRecents &&
+ (taskRect != null) && (taskRect.width() > 0) && (taskRect.height() > 0)) {
// Loading from thumbnail
Bitmap thumbnail;
Bitmap firstThumbnail = loadFirstTaskThumbnail();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 64770a4..72d9a52 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -29,6 +29,7 @@
public static final boolean EnableTaskFiltering = true;
public static final boolean EnableTaskStackClipping = false;
public static final boolean EnableInfoPane = true;
+ public static final boolean EnableSearchButton = false;
// This disables the bitmap and icon caches
public static final boolean DisableBackgroundCache = false;
@@ -84,6 +85,9 @@
public static final int TaskStackOverscrollRange = 150;
public static final int FilterStartDelay = 25;
+ // The amount to inverse scale the movement if we are overscrolling
+ public static final float TouchOverscrollScaleFactor = 3f;
+
// The padding will be applied to the smallest dimension, and then applied to all sides
public static final float StackPaddingPct = 0.15f;
// The overlap height relative to the task height
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 5e5b841..d54df13 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -44,6 +44,8 @@
public int taskStackMaxDim;
public int taskViewInfoPaneAnimDuration;
public int taskViewRoundedCornerRadiusPx;
+ public int searchBarSpaceHeightPx;
+ public int searchBarSpaceEdgeMarginsPx;
public boolean launchedWithThumbnailAnimation;
@@ -92,6 +94,9 @@
res.getInteger(R.integer.recents_animate_task_view_info_pane_duration);
taskViewRoundedCornerRadiusPx =
res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
+ searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
+ searchBarSpaceEdgeMarginsPx =
+ res.getDimensionPixelSize(R.dimen.recents_search_bar_space_edge_margins);
}
/** Updates the system insets */
@@ -99,6 +104,26 @@
systemInsets.set(insets);
}
+ /** Returns the search bar bounds in the specified orientation */
+ public void getSearchBarBounds(int width, int height,
+ Rect searchBarSpaceBounds, Rect searchBarBounds) {
+ // Return empty rects if search is not enabled
+ if (!Constants.DebugFlags.App.EnableSearchButton) {
+ searchBarSpaceBounds.set(0, 0, 0, 0);
+ searchBarBounds.set(0, 0, 0, 0);
+ return;
+ }
+
+ // Calculate the search bar bounds, and account for the system insets
+ int edgeMarginPx = searchBarSpaceEdgeMarginsPx;
+ int availableWidth = width - systemInsets.left - systemInsets.right;
+ searchBarSpaceBounds.set(0, 0, availableWidth, 2 * edgeMarginPx + searchBarSpaceHeightPx);
+
+ // Inset from the search bar space to get the search bar bounds
+ searchBarBounds.set(searchBarSpaceBounds);
+ searchBarBounds.inset(edgeMarginPx, edgeMarginPx);
+ }
+
/** Converts from DPs to PXs */
public int pxFromDp(float size) {
return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
index 06ca9e2..36b761e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
@@ -66,22 +66,33 @@
Bundle replyData = new Bundle();
TaskViewTransform transform;
+ // Get the search bar bounds so that we can account for its height in the children
+ Rect searchBarSpaceBounds = new Rect();
+ Rect searchBarBounds = new Rect();
+ RecentsConfiguration config = RecentsConfiguration.getInstance();
+ config.getSearchBarBounds(windowRect.width(), windowRect.height(),
+ searchBarSpaceBounds, searchBarBounds);
+
// Calculate the target task rect for when there is one task
// NOTE: Since the nav bar height is already accounted for in the windowRect, don't
// pass in a bottom inset
stack.addTask(new Task());
- tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top, 0);
+ tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top -
+ systemInsets.bottom - searchBarSpaceBounds.height(), 0);
tsv.boundScroll();
transform = tsv.getStackTransform(0, tsv.getStackScroll());
+ transform.rect.offset(0, searchBarSpaceBounds.height());
replyData.putParcelable(AlternateRecentsComponent.KEY_SINGLE_TASK_STACK_RECT,
new Rect(transform.rect));
// Also calculate the target task rect when there are multiple tasks
stack.addTask(new Task());
- tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top, 0);
+ tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top -
+ systemInsets.bottom - searchBarSpaceBounds.height(), 0);
tsv.setStackScrollRaw(Integer.MAX_VALUE);
tsv.boundScroll();
transform = tsv.getStackTransform(1, tsv.getStackScroll());
+ transform.rect.offset(0, searchBarSpaceBounds.height());
replyData.putParcelable(AlternateRecentsComponent.KEY_MULTIPLE_TASK_STACK_RECT,
new Rect(transform.rect));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index fd0f6d1..da265e1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -415,7 +415,10 @@
ActivityInfo info = ssp.getActivityInfo(t.baseIntent.getComponent(), t.userId);
String activityLabel = (t.activityLabel == null ? ssp.getActivityLabel(info) :
t.activityLabel.toString());
- Bitmap activityIcon = t.activityIcon;
+ BitmapDrawable activityIcon = null;
+ if (t.activityIcon != null) {
+ activityIcon = new BitmapDrawable(res, t.activityIcon);
+ }
boolean isForemostTask = (i == (taskCount - 1));
// Create a new task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
index 7f0d9ee..33ac0a8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
@@ -19,6 +19,8 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AppGlobals;
+import android.app.SearchManager;
+import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -26,11 +28,15 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.Log;
import java.util.ArrayList;
import java.util.List;
@@ -45,6 +51,7 @@
PackageManager mPm;
IPackageManager mIpm;
UserManager mUm;
+ SearchManager mSm;
String mPackage;
Bitmap mDummyIcon;
@@ -55,6 +62,7 @@
mPm = context.getPackageManager();
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
mIpm = AppGlobals.getPackageManager();
+ mSm = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
mPackage = context.getPackageName();
if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
@@ -199,4 +207,28 @@
}
return icon;
}
+
+
+ /**
+ * Composes an intent to launch the global search activity.
+ */
+ public Intent getGlobalSearchIntent(Rect sourceBounds) {
+ if (mSm == null) return null;
+
+ // Try and get the global search activity
+ ComponentName globalSearchActivity = mSm.getGlobalSearchActivity();
+ if (globalSearchActivity == null) return null;
+
+ // Bundle the source of the search
+ Bundle appSearchData = new Bundle();
+ appSearchData.putString("source", mPackage);
+
+ // Compose the intent and Start the search activity
+ Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setComponent(globalSearchActivity);
+ intent.putExtra(SearchManager.APP_DATA, appSearchData);
+ intent.setSourceBounds(sourceBounds);
+ return intent;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index ff062f6c..1566a49 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -18,6 +18,7 @@
import android.content.Intent;
import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
@@ -69,8 +70,8 @@
public TaskKey key;
public Drawable applicationIcon;
+ public Drawable activityIcon;
public String activityLabel;
- public Bitmap activityIcon;
public Bitmap thumbnail;
public boolean isActive;
public int userId;
@@ -82,7 +83,7 @@
}
public Task(int id, boolean isActive, Intent intent, String activityTitle,
- Bitmap activityIcon, int userId) {
+ BitmapDrawable activityIcon, int userId) {
this.key = new TaskKey(id, intent, userId);
this.activityLabel = activityTitle;
this.activityIcon = activityIcon;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index b054a22..a04cd3e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -27,8 +27,11 @@
import android.net.Uri;
import android.os.UserHandle;
import android.provider.Settings;
+import android.view.Gravity;
+import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
+import android.widget.TextView;
import com.android.systemui.recents.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
@@ -36,6 +39,7 @@
import com.android.systemui.recents.model.SpaceNode;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.R;
import java.util.ArrayList;
@@ -53,11 +57,16 @@
// The space partitioning root of this container
SpaceNode mBSP;
+ // Search bar view
+ View mSearchBar;
// Recents view callbacks
RecentsViewCallbacks mCb;
+ LayoutInflater mInflater;
+
public RecentsView(Context context) {
super(context);
+ mInflater = LayoutInflater.from(context);
setWillNotDraw(false);
}
@@ -71,12 +80,22 @@
mBSP = n;
// Create and add all the stacks for this partition of space.
+ boolean hasTasks = false;
removeAllViews();
ArrayList<TaskStack> stacks = mBSP.getStacks();
for (TaskStack stack : stacks) {
TaskStackView stackView = new TaskStackView(getContext(), stack);
stackView.setCallbacks(this);
addView(stackView);
+ hasTasks |= (stack.getTaskCount() > 0);
+ }
+
+ // Create the search bar (and hide it if we have no recent tasks)
+ if (Constants.DebugFlags.App.EnableSearchButton) {
+ createSearchBar();
+ if (!hasTasks) {
+ mSearchBar.setVisibility(View.GONE);
+ }
}
}
@@ -85,29 +104,45 @@
// Get the first stack view
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
- TaskStackView stackView = (TaskStackView) getChildAt(i);
- TaskStack stack = stackView.mStack;
- ArrayList<Task> tasks = stack.getTasks();
+ View child = getChildAt(i);
+ if (child instanceof TaskStackView) {
+ TaskStackView stackView = (TaskStackView) child;
+ TaskStack stack = stackView.mStack;
+ ArrayList<Task> tasks = stack.getTasks();
- // Get the first task in the stack
- if (!tasks.isEmpty()) {
- Task task = tasks.get(tasks.size() - 1);
- TaskView tv = null;
+ // Get the first task in the stack
+ if (!tasks.isEmpty()) {
+ Task task = tasks.get(tasks.size() - 1);
+ TaskView tv = null;
- // Try and use the first child task view as the source of the launch animation
- if (stackView.getChildCount() > 0) {
- TaskView stv = (TaskView) stackView.getChildAt(stackView.getChildCount() - 1);
- if (stv.getTask() == task) {
- tv = stv;
+ // Try and use the first child task view as the source of the launch animation
+ if (stackView.getChildCount() > 0) {
+ TaskView stv = (TaskView) stackView.getChildAt(stackView.getChildCount() - 1);
+ if (stv.getTask() == task) {
+ tv = stv;
+ }
}
+ onTaskLaunched(stackView, tv, stack, task);
+ return true;
}
- onTaskLaunched(stackView, tv, stack, task);
- return true;
}
}
return false;
}
+ /** Creates and adds the search bar */
+ void createSearchBar() {
+ // Create a temporary search bar
+ mSearchBar = mInflater.inflate(R.layout.recents_search_bar, this, false);
+ mSearchBar.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onSearchTriggered();
+ }
+ });
+ addView(mSearchBar);
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
@@ -120,16 +155,26 @@
Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
Constants.DebugFlags.App.TimeRecentsStartupKey, "RecentsView.onMeasure");
- // We measure our stack views sans the status bar. It will handle the nav bar itself.
+ // Get the search bar bounds so that we can account for its height in the children
+ Rect searchBarSpaceBounds = new Rect();
+ Rect searchBarBounds = new Rect();
RecentsConfiguration config = RecentsConfiguration.getInstance();
+ config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
+ searchBarSpaceBounds, searchBarBounds);
+ if (mSearchBar != null) {
+ mSearchBar.measure(MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), widthMode),
+ MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), heightMode));
+ }
+
+ // We measure our stack views sans the status bar. It will handle the nav bar itself.
int childWidth = width - config.systemInsets.right;
- int childHeight = height - config.systemInsets.top;
+ int childHeight = height - config.systemInsets.top - searchBarSpaceBounds.height();
// Measure each child
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
+ View child = getChildAt(i);
+ if (child instanceof TaskStackView && child.getVisibility() != GONE) {
child.measure(MeasureSpec.makeMeasureSpec(childWidth, widthMode),
MeasureSpec.makeMeasureSpec(childHeight, heightMode));
}
@@ -145,18 +190,30 @@
Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
Constants.DebugFlags.App.TimeRecentsStartupKey, "RecentsView.onLayout");
- // We offset our stack views by the status bar height. It will handle the nav bar itself.
+ // Get the search bar bounds so that we can account for its height in the children
+ Rect searchBarSpaceBounds = new Rect();
+ Rect searchBarBounds = new Rect();
RecentsConfiguration config = RecentsConfiguration.getInstance();
- top += config.systemInsets.top;
+ config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
+ searchBarSpaceBounds, searchBarBounds);
+ if (mSearchBar != null) {
+ mSearchBar.layout(config.systemInsets.left + searchBarSpaceBounds.left,
+ config.systemInsets.top + searchBarSpaceBounds.top,
+ config.systemInsets.left + mSearchBar.getMeasuredWidth(),
+ config.systemInsets.top + mSearchBar.getMeasuredHeight());
+ }
+
+ // We offset our stack views by the status bar height. It will handle the nav bar itself.
+ top += config.systemInsets.top + searchBarSpaceBounds.height();
// Layout each child
// XXX: Based on the space node for that task view
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- final int width = child.getMeasuredWidth();
- final int height = child.getMeasuredHeight();
+ View child = getChildAt(i);
+ if (child instanceof TaskStackView && child.getVisibility() != GONE) {
+ int width = child.getMeasuredWidth();
+ int height = child.getMeasuredHeight();
child.layout(left, top, left + width, top + height);
}
}
@@ -188,9 +245,12 @@
// Get the first stack view
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
- TaskStackView stackView = (TaskStackView) getChildAt(i);
- if (stackView.closeOpenInfoPanes()) {
- return true;
+ View child = getChildAt(i);
+ if (child instanceof TaskStackView) {
+ TaskStackView stackView = (TaskStackView) child;
+ if (stackView.closeOpenInfoPanes()) {
+ return true;
+ }
}
}
}
@@ -266,7 +326,6 @@
b, offsetX, offsetY);
}
-
if (task.isActive) {
// Bring an active task to the foreground
RecentsTaskLoader.getInstance().getSystemServicesProxy()
@@ -315,4 +374,24 @@
TaskStackBuilder.create(getContext())
.addNextIntentWithParentStack(intent).startActivities();
}
+
+ public void onSearchTriggered() {
+ // Get the search bar source bounds
+ Rect searchBarSpaceBounds = new Rect();
+ Rect searchBarBounds = new Rect();
+ RecentsConfiguration config = RecentsConfiguration.getInstance();
+ config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(),
+ searchBarSpaceBounds, searchBarBounds);
+
+ // Get the search intent and start it
+ Intent searchIntent = RecentsTaskLoader.getInstance().getSystemServicesProxy()
+ .getGlobalSearchIntent(searchBarBounds);
+ if (searchIntent != null) {
+ try {
+ getContext().startActivity(searchIntent);
+ } catch (ActivityNotFoundException anfe) {
+ Console.logError(getContext(), "Could not start Search activity");
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index c9a6d67..124f11e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -31,7 +31,6 @@
Task mTask;
ImageView mApplicationIcon;
- ImageView mActivityIcon;
TextView mActivityDescription;
public TaskBarView(Context context) {
@@ -54,23 +53,22 @@
protected void onFinishInflate() {
// Initialize the icon and description views
mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
- mActivityIcon = (ImageView) findViewById(R.id.activity_icon);
mActivityDescription = (TextView) findViewById(R.id.activity_description);
}
/** Binds the bar view to the task */
void rebindToTask(Task t, boolean animate) {
mTask = t;
- if (t.applicationIcon != null) {
+ // If an activity icon is defined, then we use that as the primary icon to show in the bar,
+ // otherwise, we fall back to the application icon
+ if (t.activityIcon != null) {
+ mApplicationIcon.setImageDrawable(t.activityIcon);
+ } else if (t.applicationIcon != null) {
mApplicationIcon.setImageDrawable(t.applicationIcon);
- mActivityDescription.setText(t.activityLabel);
- if (t.activityIcon != null) {
- mActivityIcon.setImageBitmap(t.activityIcon);
- mActivityIcon.setVisibility(View.VISIBLE);
- }
- if (animate) {
- // XXX: Investigate how expensive it will be to create a second bitmap and crossfade
- }
+ }
+ mActivityDescription.setText(t.activityLabel);
+ if (animate) {
+ // XXX: Investigate how expensive it will be to create a second bitmap and crossfade
}
}
@@ -78,8 +76,6 @@
void unbindFromTask() {
mTask = null;
mApplicationIcon.setImageDrawable(null);
- mActivityIcon.setImageBitmap(null);
- mActivityIcon.setVisibility(View.INVISIBLE);
mActivityDescription.setText("");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
index 233e38c..a81d01c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
@@ -70,10 +70,8 @@
/** Updates the positions of each of the items to fit in the rect specified */
void updateContents(Rect visibleRect) {
// Offset the app info button
- LayoutParams lp = (LayoutParams) mAppInfoButton.getLayoutParams();
- lp.topMargin = visibleRect.top +
- (visibleRect.height() - mAppInfoButton.getMeasuredHeight()) / 2;
- requestLayout();
+ mAppInfoButton.setTranslationY(visibleRect.top +
+ (visibleRect.height() - mAppInfoButton.getMeasuredHeight()) / 2);
}
/** Sets the circular clip radius on this panel */
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 ee92b16..a77e61d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -395,9 +395,12 @@
return false;
}
- /** Returns whether the current scroll is out of bounds */
+ /** Returns whether the specified scroll is out of bounds */
+ boolean isScrollOutOfBounds(int scroll) {
+ return (scroll < mMinScroll) || (scroll > mMaxScroll);
+ }
boolean isScrollOutOfBounds() {
- return (getStackScroll() < 0) || (getStackScroll() > mMaxScroll);
+ return isScrollOutOfBounds(getStackScroll());
}
/** Updates the min and max virtual scroll bounds */
@@ -556,7 +559,14 @@
int smallestDimension = Math.min(width, height);
int padding = (int) (Constants.Values.TaskStackView.StackPaddingPct * smallestDimension / 2f);
- mStackRect.inset(padding, padding);
+ if (Constants.DebugFlags.App.EnableSearchButton) {
+ // Don't need to pad the top since we have some padding on the search bar already
+ mStackRect.left += padding;
+ mStackRect.right -= padding;
+ mStackRect.bottom -= padding;
+ } else {
+ mStackRect.inset(padding, padding);
+ }
mStackRectSansPeek.set(mStackRect);
mStackRectSansPeek.top += Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height();
@@ -1275,7 +1285,12 @@
}
}
if (mIsScrolling) {
- mSv.setStackScroll(mSv.getStackScroll() + deltaY);
+ int curStackScroll = mSv.getStackScroll();
+ if (mSv.isScrollOutOfBounds(curStackScroll + deltaY)) {
+ // Scale the touch if we are overscrolling
+ deltaY /= Constants.Values.TaskStackView.TouchOverscrollScaleFactor;
+ }
+ mSv.setStackScroll(curStackScroll + deltaY);
if (mSv.isScrollOutOfBounds()) {
mVelocityTracker.clear();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index fdf4dbf..56f83df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -54,6 +54,7 @@
private boolean mMaxHeightNeedsUpdate;
private NotificationActivator mActivator;
private LatestItemView.OnActivatedListener mOnActivatedListener;
+ private boolean mSelfInitiatedLayout;
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -162,6 +163,11 @@
}
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;
@@ -171,6 +177,14 @@
lp.height = oldHeight;
setLayoutParams(lp);
mMaxExpandHeight = getMeasuredHeight();
+ mSelfInitiatedLayout = false;
+ }
+
+ @Override
+ public void requestLayout() {
+ if (!mSelfInitiatedLayout) {
+ super.requestLayout();
+ }
}
/**
@@ -257,4 +271,11 @@
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();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
new file mode 100644
index 0000000..3cc22ef
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -0,0 +1,232 @@
+/*
+ * 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.phone;
+
+import android.app.ActivityManagerNative;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.MediaStore;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityManager;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
+
+import com.android.systemui.R;
+
+/**
+ * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
+ * text.
+ */
+public class KeyguardBottomAreaView extends FrameLayout {
+
+ final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
+
+ private View mCameraButton;
+ private float mCameraDragDistance;
+ private PowerManager mPowerManager;
+ private int mScaledTouchSlop;
+
+ public KeyguardBottomAreaView(Context context) {
+ super(context);
+ }
+
+ public KeyguardBottomAreaView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mCameraButton = findViewById(R.id.camera_button);
+ watchForDevicePolicyChanges();
+ watchForAccessibilityChanges();
+ updateCameraVisibility();
+ mCameraDragDistance = getResources().getDimension(R.dimen.camera_drag_distance);
+ mScaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ }
+
+ private void updateCameraVisibility() {
+ boolean visible = !isCameraDisabledByDpm();
+ mCameraButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+
+ private boolean isCameraDisabledByDpm() {
+ final DevicePolicyManager dpm =
+ (DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
+ if (dpm != null) {
+ try {
+ final int userId = ActivityManagerNative.getDefault().getCurrentUser().id;
+ final int disabledFlags = dpm.getKeyguardDisabledFeatures(null, userId);
+ final boolean disabledBecauseKeyguardSecure =
+ (disabledFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0
+ && KeyguardTouchDelegate.getInstance(getContext()).isSecure();
+ return dpm.getCameraDisabled(null) || disabledBecauseKeyguardSecure;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Can't get userId", e);
+ }
+ }
+ return false;
+ }
+
+ private void watchForDevicePolicyChanges() {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
+ getContext().registerReceiver(new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ updateCameraVisibility();
+ }
+ });
+ }
+ }, filter);
+ }
+
+ private void watchForAccessibilityChanges() {
+ final AccessibilityManager am =
+ (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+
+ // Set the initial state
+ enableAccessibility(am.isTouchExplorationEnabled());
+
+ // Watch for changes
+ am.addTouchExplorationStateChangeListener(
+ new AccessibilityManager.TouchExplorationStateChangeListener() {
+ @Override
+ public void onTouchExplorationStateChanged(boolean enabled) {
+ enableAccessibility(enabled);
+ }
+ });
+ }
+
+ private void enableAccessibility(boolean touchExplorationEnabled) {
+
+ // Add a touch handler or accessibility click listener for camera button.
+ if (touchExplorationEnabled) {
+ mCameraButton.setOnTouchListener(null);
+ mCameraButton.setOnClickListener(mCameraClickListener);
+ } else {
+ mCameraButton.setOnTouchListener(mCameraTouchListener);
+ mCameraButton.setOnClickListener(null);
+ }
+ }
+
+ private void launchCamera() {
+ mContext.startActivityAsUser(
+ new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE),
+ UserHandle.CURRENT);
+ }
+
+ private final OnClickListener mCameraClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ launchCamera();
+ }
+ };
+
+ private final OnTouchListener mCameraTouchListener = new OnTouchListener() {
+ private float mStartX;
+ private boolean mTouchSlopReached;
+ private boolean mSkipCancelAnimation;
+
+ @Override
+ public boolean onTouch(final View cameraButtonView, MotionEvent event) {
+ float realX = event.getRawX();
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mStartX = realX;
+ mTouchSlopReached = false;
+ mSkipCancelAnimation = false;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (realX > mStartX) {
+ realX = mStartX;
+ }
+ if (realX < mStartX - mCameraDragDistance) {
+ cameraButtonView.setPressed(true);
+ mPowerManager.userActivity(event.getEventTime(), false);
+ } else {
+ cameraButtonView.setPressed(false);
+ }
+ if (realX < mStartX - mScaledTouchSlop) {
+ mTouchSlopReached = true;
+ }
+ cameraButtonView.setTranslationX(Math.max(realX - mStartX,
+ -mCameraDragDistance));
+ break;
+ case MotionEvent.ACTION_UP:
+ if (realX < mStartX - mCameraDragDistance) {
+ launchCamera();
+ cameraButtonView.animate().x(-cameraButtonView.getWidth())
+ .setInterpolator(new AccelerateInterpolator(2f)).withEndAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ cameraButtonView.setTranslationX(0);
+ }
+ });
+ mSkipCancelAnimation = true;
+ }
+ if (realX < mStartX - mScaledTouchSlop) {
+ mTouchSlopReached = true;
+ }
+ if (!mTouchSlopReached) {
+ mSkipCancelAnimation = true;
+ cameraButtonView.animate().translationX(-mCameraDragDistance / 2).
+ setInterpolator(new DecelerateInterpolator()).withEndAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ cameraButtonView.animate().translationX(0).
+ setInterpolator(new AccelerateInterpolator());
+ }
+ });
+ }
+ case MotionEvent.ACTION_CANCEL:
+ cameraButtonView.setPressed(false);
+ if (!mSkipCancelAnimation) {
+ cameraButtonView.animate().translationX(0)
+ .setInterpolator(new AccelerateInterpolator(2f));
+ }
+ break;
+ }
+ return true;
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index f2054a2..cf31b44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -83,7 +83,7 @@
}
public void onScreenTurnedOff() {
- if (mKeyguardView != null && mRoot.getVisibility() == View.VISIBLE) {
+ if (mKeyguardView != null && mRoot != null && mRoot.getVisibility() == View.VISIBLE) {
mKeyguardView.onPause();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index d26b32f..a0582ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -82,7 +82,6 @@
setKeyButtonViewQuiescentAlpha(mView.getMenuButton(), alpha, animate);
setKeyButtonViewQuiescentAlpha(mView.getSearchLight(), KEYGUARD_QUIESCENT_ALPHA, animate);
- setKeyButtonViewQuiescentAlpha(mView.getCameraButton(), KEYGUARD_QUIESCENT_ALPHA, animate);
applyBackButtonQuiescentAlpha(mode, animate);
@@ -98,7 +97,6 @@
public void applyBackButtonQuiescentAlpha(int mode, boolean animate) {
float backAlpha = 0;
backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getSearchLight());
- backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getCameraButton());
backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getHomeButton());
backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getRecentsButton());
backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getMenuButton());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 7ca672d..3fae3f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -21,36 +21,24 @@
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
-import android.app.ActivityManagerNative;
import android.app.StatusBarManager;
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.provider.MediaStore;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -63,8 +51,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import static com.android.systemui.statusbar.phone.KeyguardTouchDelegate.OnKeyguardConnectionListener;
-
public class NavigationBarView extends LinearLayout {
final static boolean DEBUG = false;
final static String TAG = "PhoneStatusBar/NavigationBarView";
@@ -98,16 +84,9 @@
final static boolean WORKAROUND_INVALID_LAYOUT = true;
final static int MSG_CHECK_INVALID_LAYOUT = 8686;
- private final float mCameraDragDistance;
-
- // used to disable the camera icon in navbar when disabled by DPM
- private boolean mCameraDisabledByDpm;
-
// performs manual animation in sync with layout transitions
private final NavTransitionListener mTransitionListener = new NavTransitionListener();
- private final PowerManager mPowerManager;
-
private class NavTransitionListener implements TransitionListener {
private boolean mBackTransitioning;
private boolean mHomeAppearing;
@@ -157,112 +136,12 @@
private final OnClickListener mAccessibilityClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
- if (v.getId() == R.id.camera_button) {
- KeyguardTouchDelegate.getInstance(getContext()).launchCamera();
- } else if (v.getId() == R.id.search_light) {
+ if (v.getId() == R.id.search_light) {
KeyguardTouchDelegate.getInstance(getContext()).showAssistant();
}
}
};
- private final int mScaledTouchSlop;
-
- private final OnTouchListener mCameraTouchListener = new OnTouchListener() {
- private float mStartX;
- private boolean mTouchSlopReached;
- private boolean mSkipCancelAnimation;
-
- @Override
- public boolean onTouch(final View cameraButtonView, MotionEvent event) {
- float realX = event.getRawX();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- // disable search gesture while interacting with camera
- mDelegateHelper.setDisabled(true);
- mBarTransitions.setContentVisible(false);
- mStartX = realX;
- mTouchSlopReached = false;
- mSkipCancelAnimation = false;
- break;
- case MotionEvent.ACTION_MOVE:
- if (realX > mStartX) {
- realX = mStartX;
- }
- if (realX < mStartX - mCameraDragDistance) {
- ((KeyButtonView) cameraButtonView).setPressed(true);
- mPowerManager.userActivity(event.getEventTime(), false);
- } else {
- ((KeyButtonView) cameraButtonView).setPressed(false);
- }
- if (realX < mStartX - mScaledTouchSlop) {
- mTouchSlopReached = true;
- }
- cameraButtonView.setTranslationX(Math.max(realX - mStartX,
- -mCameraDragDistance));
- break;
- case MotionEvent.ACTION_UP:
- if (realX < mStartX - mCameraDragDistance) {
- mContext.startActivityAsUser(
- new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE),
- UserHandle.CURRENT);
- cameraButtonView.animate().x(-cameraButtonView.getWidth())
- .setInterpolator(new AccelerateInterpolator(2f)).withEndAction(
- new Runnable() {
- @Override
- public void run() {
- cameraButtonView.setTranslationX(0);
- }
- });
- mSkipCancelAnimation = true;
- }
- if (realX < mStartX - mScaledTouchSlop) {
- mTouchSlopReached = true;
- }
- if (!mTouchSlopReached) {
- mSkipCancelAnimation = true;
- cameraButtonView.animate().translationX(-mCameraDragDistance / 2).
- setInterpolator(new DecelerateInterpolator()).withEndAction(
- new Runnable() {
- @Override
- public void run() {
- cameraButtonView.animate().translationX(0).
- setInterpolator(new AccelerateInterpolator());
- }
- });
- }
- case MotionEvent.ACTION_CANCEL:
- ((KeyButtonView) cameraButtonView).setPressed(false);
- mDelegateHelper.setDisabled(false);
- mBarTransitions.setContentVisible(true);
- if (!mSkipCancelAnimation) {
- cameraButtonView.animate().translationX(0)
- .setInterpolator(new AccelerateInterpolator(2f));
- }
- break;
- }
- return true;
- }
- };
-
- private final OnKeyguardConnectionListener mKeyguardConnectionListener =
- new OnKeyguardConnectionListener() {
- @Override
- public void onKeyguardServiceConnected(
- KeyguardTouchDelegate keyguardTouchDelegate) {
- post(new Runnable() {
- @Override
- public void run() {
- mCameraDisabledByDpm = isCameraDisabledByDpm();
- }
- });
- }
-
- @Override
- public void onKeyguardServiceDisconnected(
- KeyguardTouchDelegate keyguardTouchDelegate) {
- }
- };
-
private class H extends Handler {
public void handleMessage(Message m) {
switch (m.what) {
@@ -301,28 +180,6 @@
getIcons(res);
mBarTransitions = new NavigationBarTransitions(this);
-
- KeyguardTouchDelegate.addListener(mKeyguardConnectionListener);
- mCameraDisabledByDpm = isCameraDisabledByDpm();
- watchForDevicePolicyChanges();
- mCameraDragDistance = res.getDimension(R.dimen.camera_drag_distance);
- mScaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- }
-
- private void watchForDevicePolicyChanges() {
- final IntentFilter filter = new IntentFilter();
- filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
- getContext().registerReceiver(new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- post(new Runnable() {
- @Override
- public void run() {
- mCameraDisabledByDpm = isCameraDisabledByDpm();
- }
- });
- }
- }, filter);
}
public BarTransitions getBarTransitions() {
@@ -381,11 +238,6 @@
return mCurrentView.findViewById(R.id.search_light);
}
- // shown when keyguard is visible and camera is available
- public View getCameraButton() {
- return mCurrentView.findViewById(R.id.camera_button);
- }
-
private void getIcons(Resources res) {
mBackIcon = res.getDrawable(R.drawable.ic_sysbar_back);
mBackLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_land);
@@ -475,9 +327,7 @@
getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
final boolean showSearch = disableHome && !disableSearch;
- final boolean showCamera = showSearch && !mCameraDisabledByDpm;
setVisibleOrGone(getSearchLight(), showSearch);
- setVisibleOrGone(getCameraButton(), showCamera);
mBarTransitions.applyBackButtonQuiescentAlpha(mBarTransitions.getMode(), true /*animate*/);
}
@@ -488,24 +338,6 @@
}
}
- private boolean isCameraDisabledByDpm() {
- final DevicePolicyManager dpm =
- (DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
- if (dpm != null) {
- try {
- final int userId = ActivityManagerNative.getDefault().getCurrentUser().id;
- final int disabledFlags = dpm.getKeyguardDisabledFeatures(null, userId);
- final boolean disabledBecauseKeyguardSecure =
- (disabledFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0
- && KeyguardTouchDelegate.getInstance(getContext()).isSecure();
- return dpm.getCameraDisabled(null) || disabledBecauseKeyguardSecure;
- } catch (RemoteException e) {
- Log.e(TAG, "Can't get userId", e);
- }
- }
- return false;
- }
-
public void setSlippery(boolean newSlippery) {
WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
if (lp != null) {
@@ -572,25 +404,12 @@
// Add a touch handler or accessibility click listener for camera and search buttons
// for all view orientations.
final OnClickListener onClickListener = touchEnabled ? mAccessibilityClickListener : null;
- final OnTouchListener onTouchListener = touchEnabled ? null : mCameraTouchListener;
- boolean hasCamera = false;
for (int i = 0; i < mRotatedViews.length; i++) {
- final View cameraButton = mRotatedViews[i].findViewById(R.id.camera_button);
final View searchLight = mRotatedViews[i].findViewById(R.id.search_light);
- if (cameraButton != null) {
- hasCamera = true;
- cameraButton.setOnTouchListener(onTouchListener);
- cameraButton.setOnClickListener(onClickListener);
- }
if (searchLight != null) {
searchLight.setOnClickListener(onClickListener);
}
}
- if (hasCamera) {
- // Warm up KeyguardTouchDelegate so it's ready by the time the camera button is touched.
- // This will connect to KeyguardService so that touch events are processed.
- KeyguardTouchDelegate.getInstance(getContext());
- }
}
public boolean isVertical() {
@@ -727,7 +546,6 @@
dumpButton(pw, "rcnt", getRecentsButton());
dumpButton(pw, "menu", getMenuButton());
dumpButton(pw, "srch", getSearchLight());
- dumpButton(pw, "cmra", getCameraButton());
pw.println(" }");
}
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 45ac50b..5d75801 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -38,6 +38,7 @@
private int[] mTempChildLocation = new int[2];
private View mNotificationParent;
private boolean mTrackingSettings;
+ private float mExpandedHeight = -1;
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -173,6 +174,8 @@
* @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();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 0266144c..328a1728 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -46,7 +46,6 @@
}
public static final boolean BRAKES = false;
- private boolean mRubberbandingEnabled = true;
private float mSelfExpandVelocityPx; // classic value: 2000px/s
private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
@@ -68,14 +67,12 @@
private float mExpandBrakingDistancePx = 150; // XXX Resource
private float mBrakingSpeedPx = 150; // XXX Resource
- private View mHandleView;
private float mPeekHeight;
private float mInitialOffsetOnTouch;
private float mExpandedFraction = 0;
private float mExpandedHeight = 0;
private boolean mJustPeeked;
private boolean mClosing;
- private boolean mRubberbanding;
private boolean mTracking;
private int mTrackingPointer;
private int mTouchSlop;
@@ -214,7 +211,6 @@
public void run() {
if (mTimeAnimator != null && mTimeAnimator.isStarted()) {
mTimeAnimator.end();
- mRubberbanding = false;
mClosing = false;
onExpandingFinished();
}
@@ -225,12 +221,9 @@
protected int mMaxPanelHeight = 0;
private String mViewName;
protected float mInitialTouchY;
+ protected float mInitialTouchX;
protected float mFinalTouchY;
- public void setRubberbandingEnabled(boolean enable) {
- mRubberbandingEnabled = enable;
- }
-
protected void onExpandingFinished() {
}
@@ -260,12 +253,7 @@
mTimeAnimator.start();
- mRubberbanding = mRubberbandingEnabled // is it enabled at all?
- && mExpandedHeight > getMaxPanelHeight() // are we past the end?
- && mVel >= -mFlingGestureMinDistPx; // was this not possibly a "close" gesture?
- if (mRubberbanding) {
- mClosing = true;
- } else if (mVel == 0) {
+ if (mVel == 0) {
// if the panel is less than halfway open, close it
mClosing = (mFinalTouchY / getMaxPanelHeight()) < 0.5f;
} else {
@@ -308,10 +296,6 @@
float h = mExpandedHeight + mVel * dt;
- if (mRubberbanding && h < fh) {
- h = fh;
- }
-
if (DEBUG) logf("tick: new h=%d closing=%s", (int) h, mClosing?"true":"false");
setExpandedHeightInternal(h);
@@ -320,7 +304,7 @@
if (mVel == 0
|| (mClosing && mExpandedHeight == 0)
- || ((mRubberbanding || !mClosing) && mExpandedHeight == fh)) {
+ || (!mClosing && mExpandedHeight == fh)) {
post(mStopAnimator);
}
} else {
@@ -358,8 +342,7 @@
mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
mPeekHeight = res.getDimension(R.dimen.peek_height)
- + getPaddingBottom() // our window might have a dropshadow
- - (mHandleView == null ? 0 : mHandleView.getPaddingTop()); // the handle might have a topshadow
+ + getPaddingBottom(); // our window might have a dropshadow
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
@@ -393,16 +376,14 @@
mTrackingPointer = event.getPointerId(pointerIndex);
}
final float y = event.getY(pointerIndex);
+ final float x = event.getX(pointerIndex);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mTracking = true;
- if (mHandleView != null) {
- mHandleView.setPressed(true);
- postInvalidate(); // catch the press state change
- }
mInitialTouchY = y;
+ mInitialTouchX = x;
initVelocityTracker();
trackMovement(event);
mTimeAnimator.cancel(); // end any outstanding animations
@@ -420,9 +401,11 @@
// gesture is ongoing, find a new pointer to track
final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
final float newY = event.getY(newIndex);
+ final float newX = event.getX(newIndex);
mTrackingPointer = event.getPointerId(newIndex);
mInitialOffsetOnTouch = mExpandedHeight;
mInitialTouchY = newY;
+ mInitialTouchX = newX;
}
break;
@@ -447,10 +430,6 @@
mFinalTouchY = y;
mTracking = false;
mTrackingPointer = -1;
- if (mHandleView != null) {
- mHandleView.setPressed(false);
- postInvalidate(); // catch the press state change
- }
onTrackingStopped();
trackMovement(event);
@@ -541,23 +520,17 @@
pointerIndex = 0;
mTrackingPointer = event.getPointerId(pointerIndex);
}
+ final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);
boolean scrolledToBottom = isScrolledToBottom();
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- if (mHandleView != null) {
- mHandleView.setPressed(true);
- // catch the press state change
- postInvalidate();
- }
mInitialTouchY = y;
+ mInitialTouchX = x;
initVelocityTracker();
trackMovement(event);
mTimeAnimator.cancel(); // end any outstanding animations
- if (mExpandedHeight == 0 || y > getContentHeight()) {
- return true;
- }
break;
case MotionEvent.ACTION_POINTER_UP:
final int upPointer = event.getPointerId(event.getActionIndex());
@@ -565,8 +538,8 @@
// gesture is ongoing, find a new pointer to track
final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
mTrackingPointer = event.getPointerId(newIndex);
- final float newY = event.getY(newIndex);
- mInitialTouchY = newY;
+ mInitialTouchX = event.getX(newIndex);
+ mInitialTouchY = event.getY(newIndex);
}
break;
@@ -574,9 +547,10 @@
final float h = y - mInitialTouchY;
trackMovement(event);
if (scrolledToBottom) {
- if (h < -mTouchSlop) {
+ if (h < -mTouchSlop && h < -Math.abs(x - mInitialTouchX)) {
mInitialOffsetOnTouch = mExpandedHeight;
mInitialTouchY = y;
+ mInitialTouchX = x;
mTracking = true;
onTrackingStarted();
return true;
@@ -605,7 +579,6 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mHandleView = findViewById(R.id.handle);
loadDimens();
}
@@ -631,10 +604,6 @@
return mViewName;
}
- public View getHandle() {
- return mHandleView;
- }
-
// Rubberbands the panel to hold its contents.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
@@ -648,14 +617,14 @@
if (newHeight != mMaxPanelHeight) {
mMaxPanelHeight = newHeight;
// If the user isn't actively poking us, let's rubberband to the content
- if (!mTracking && !mRubberbanding && !mTimeAnimator.isStarted()
+ if (!mTracking && !mTimeAnimator.isStarted()
&& mExpandedHeight > 0 && mExpandedHeight != mMaxPanelHeight
&& mMaxPanelHeight > 0) {
mExpandedHeight = mMaxPanelHeight;
}
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(
- getDesiredMeasureHeight(), MeasureSpec.AT_MOST);
+ getDesiredMeasureHeight(), MeasureSpec.AT_MOST);
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
@@ -666,7 +635,6 @@
public void setExpandedHeight(float height) {
if (DEBUG) logf("setExpandedHeight(%.1f)", height);
- mRubberbanding = false;
if (mTimeAnimator.isStarted()) {
post(mStopAnimator);
}
@@ -686,7 +654,7 @@
float currentMaxPanelHeight = getMaxPanelHeight();
// If the user isn't actively poking us, let's update the height
- if (!mTracking && !mRubberbanding && !mTimeAnimator.isStarted()
+ if (!mTracking && !mTimeAnimator.isStarted()
&& mExpandedHeight > 0 && currentMaxPanelHeight != mExpandedHeight) {
setExpandedHeightInternal(currentMaxPanelHeight);
}
@@ -708,13 +676,13 @@
}
if (h < 0) h = 0;
- if (!(mRubberbandingEnabled && (mTracking || mRubberbanding)) && h > fh) h = fh;
+ if (h > fh) h = fh;
mExpandedHeight = h;
if (DEBUG) {
- logf("setExpansion: height=%.1f fh=%.1f tracking=%s rubber=%s", h, fh,
- mTracking ? "T" : "f", mRubberbanding ? "T" : "f");
+ logf("setExpansion: height=%.1f fh=%.1f tracking=%s", h, fh,
+ mTracking ? "T" : "f");
}
onHeightUpdated(mExpandedHeight);
@@ -793,7 +761,6 @@
mClosing = true;
onExpandingStarted();
// collapse() should never be a rubberband, even if an animation is already running
- mRubberbanding = false;
fling(-mSelfCollapseVelocityPx, /*always=*/ true);
}
}
@@ -817,14 +784,13 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s"
- + " tracking=%s rubberbanding=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s"
+ + " tracking=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s"
+ "]",
this.getClass().getSimpleName(),
getExpandedHeight(),
getMaxPanelHeight(),
mClosing?"T":"f",
mTracking?"T":"f",
- mRubberbanding?"T":"f",
mJustPeeked?"T":"f",
mPeekAnimator, ((mPeekAnimator!=null && mPeekAnimator.isStarted())?" (started)":""),
mTimeAnimator, ((mTimeAnimator!=null && mTimeAnimator.isStarted())?" (started)":"")
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 d577070..2d96c5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -78,6 +78,7 @@
import android.view.ViewGroup.LayoutParams;
import android.view.ViewPropertyAnimator;
import android.view.ViewStub;
+import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
@@ -220,15 +221,14 @@
// settings
QuickSettings mQS;
- boolean mHasSettingsPanel, mHasFlipSettings;
- SettingsPanelView mSettingsPanel;
+ boolean mHasQuickSettings;
View mFlipSettingsView;
QuickSettingsContainerView mSettingsContainer;
- int mSettingsPanelGravity;
// top bar
View mNotificationPanelHeader;
View mKeyguardStatusView;
+ View mKeyguardBottomArea;
int mKeyguardMaxNotificationCount;
View mDateTimeView;
View mClearButton;
@@ -315,9 +315,6 @@
mHeaderFlipper.userSetup(userSetup);
mKeyguardFlipper.userSetup(userSetup);
- if (mSettingsPanel != null) {
- mSettingsPanel.setEnabled(userSetup);
- }
if (userSetup != mUserSetup) {
mUserSetup = userSetup;
if (!mUserSetup && mStatusBarView != null)
@@ -617,6 +614,7 @@
mNotificationPanelHeader = mStatusBarWindow.findViewById(R.id.header);
mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
+ mKeyguardBottomArea = mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
mClearButton = mStatusBarWindow.findViewById(R.id.clear_all_button);
mClearButton.setOnClickListener(mClearButtonListener);
@@ -625,8 +623,7 @@
mClearButton.setEnabled(false);
mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date);
- mHasSettingsPanel = res.getBoolean(R.bool.config_hasSettingsPanel);
- mHasFlipSettings = res.getBoolean(R.bool.config_hasFlipSettingsPanel);
+ mHasQuickSettings = res.getBoolean(R.bool.config_hasQuickSettings);
mDateTimeView = mNotificationPanelHeader.findViewById(R.id.datetime);
if (mDateTimeView != null) {
@@ -634,10 +631,8 @@
mDateTimeView.setEnabled(true);
}
- mHeaderFlipper = new FlipperButton(mNotificationPanelHeader
- .findViewById(R.id.settings_button_holder));
- ViewStub flipStub = (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_flip_stub);
- mKeyguardFlipper = new FlipperButton(flipStub.inflate());
+ mHeaderFlipper = new FlipperButton(mStatusBarWindow.findViewById(R.id.header_flipper));
+ mKeyguardFlipper =new FlipperButton(mStatusBarWindow.findViewById(R.id.keyguard_flipper));
if (!mNotificationPanelIsFullScreenWidth) {
mNotificationPanel.setSystemUiVisibility(
@@ -715,37 +710,11 @@
// Quick Settings (where available, some restrictions apply)
mNotificationPadding = mContext.getResources()
.getDimensionPixelSize(R.dimen.notification_side_padding);
- if (mHasSettingsPanel) {
- // first, figure out where quick settings should be inflated
- final View settings_stub;
- if (mHasFlipSettings) {
- // a version of quick settings that flips around behind the notifications
- settings_stub = mStatusBarWindow.findViewById(R.id.flip_settings_stub);
- if (settings_stub != null) {
- mFlipSettingsView = ((ViewStub)settings_stub).inflate();
- mFlipSettingsView.setVisibility(View.GONE);
- mFlipSettingsView.setVerticalScrollBarEnabled(false);
- }
- } else {
- // full quick settings panel
- settings_stub = mStatusBarWindow.findViewById(R.id.quick_settings_stub);
- if (settings_stub != null) {
- mSettingsPanel = (SettingsPanelView) ((ViewStub)settings_stub).inflate();
- } else {
- mSettingsPanel = (SettingsPanelView) mStatusBarWindow.findViewById(R.id.settings_panel);
- }
-
- if (mSettingsPanel != null) {
- if (!ActivityManager.isHighEndGfx()) {
- mSettingsPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
- R.color.notification_panel_solid_background)));
- }
- }
- }
-
- // wherever you find it, Quick Settings needs a container to survive
+ if (mHasQuickSettings) {
+ // Quick Settings needs a container to survive
mSettingsContainer = (QuickSettingsContainerView)
mStatusBarWindow.findViewById(R.id.quick_settings_container);
+ mFlipSettingsView = mSettingsContainer;
if (mSettingsContainer != null) {
mQS = new QuickSettings(mContext, mSettingsContainer);
if (!mNotificationPanelIsFullScreenWidth) {
@@ -753,9 +722,6 @@
View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS
| View.STATUS_BAR_DISABLE_SYSTEM_INFO);
}
- if (mSettingsPanel != null) {
- mSettingsPanel.setQuickSettings(mQS);
- }
mQS.setService(this);
mQS.setBar(mStatusBarView);
mQS.setup(mNetworkController, mBluetoothController, mBatteryController,
@@ -784,6 +750,11 @@
}
public boolean onSettingsEvent(MotionEvent event) {
+ userActivity();
+ if (mSettingsClosing
+ && mFlipSettingsViewAnim != null && mFlipSettingsViewAnim.isRunning()) {
+ return true;
+ }
if (mSettingsTracker != null) {
mSettingsTracker.addMovement(event);
}
@@ -851,15 +822,15 @@
}
private void positionSettings(float dy) {
- final int h = mFlipSettingsView.getMeasuredHeight();
- final int ph = mNotificationPanel.getMeasuredHeight();
if (mSettingsClosing) {
+ final int ph = mNotificationPanel.getMeasuredHeight();
dy = Math.min(Math.max(-ph, dy), 0);
mFlipSettingsView.setTranslationY(dy);
mStackScroller.setTranslationY(ph + dy);
} else {
- dy = Math.min(Math.max(0, dy), ph);
- mFlipSettingsView.setTranslationY(-h + dy - mNotificationPadding * 2);
+ final int h = mFlipSettingsView.getBottom();
+ dy = Math.min(Math.max(0, dy), h);
+ mFlipSettingsView.setTranslationY(-h + dy);
mStackScroller.setTranslationY(dy);
}
}
@@ -1397,8 +1368,7 @@
+ " any=" + any + " clearable=" + clearable);
}
- if (mHasFlipSettings
- && mFlipSettingsView != null
+ if (mFlipSettingsView != null
&& mFlipSettingsView.getVisibility() == View.VISIBLE
&& mStackScroller.getVisibility() != View.VISIBLE) {
// the flip settings panel is unequivocally showing; we should not be shown
@@ -1758,7 +1728,7 @@
}
mNotificationPanel.expand();
- if (mHasFlipSettings && mStackScroller.getVisibility() != View.VISIBLE) {
+ if (mStackScroller.getVisibility() != View.VISIBLE) {
flipToNotifications();
}
@@ -1771,17 +1741,15 @@
mHeaderFlipper.cancel();
mKeyguardFlipper.cancel();
if (mClearButtonAnim != null) mClearButtonAnim.cancel();
-
mStackScroller.setVisibility(View.VISIBLE);
final int h = mNotificationPanel.getMeasuredHeight();
final float settingsY = mSettingsTracker != null ? mFlipSettingsView.getTranslationY() : 0;
final float scrollerY = mSettingsTracker != null ? mStackScroller.getTranslationY() : h;
mScrollViewAnim = start(
- startDelay(0,
interpolator(mDecelerateInterpolator,
ObjectAnimator.ofFloat(mStackScroller, View.TRANSLATION_Y, scrollerY, 0)
.setDuration(FLIP_DURATION)
- )));
+ ));
mFlipSettingsViewAnim = start(
setVisibilityWhenDone(
interpolator(mDecelerateInterpolator,
@@ -1814,14 +1782,10 @@
// Settings are not available in setup
if (!mUserSetup) return;
- if (mHasFlipSettings) {
- mNotificationPanel.expand();
- if (mFlipSettingsView.getVisibility() != View.VISIBLE
- || mFlipSettingsView.getTranslationY() < 0) {
- flipToSettings();
- }
- } else if (mSettingsPanel != null) {
- mSettingsPanel.expand();
+ mNotificationPanel.expand();
+ if (mFlipSettingsView.getVisibility() != View.VISIBLE
+ || mFlipSettingsView.getTranslationY() < 0) {
+ flipToSettings();
}
if (false) postStartTracing();
@@ -1879,12 +1843,10 @@
}
public void flipPanels() {
- if (mHasFlipSettings) {
- if (mFlipSettingsView.getVisibility() != View.VISIBLE) {
- flipToSettings();
- } else {
- flipToNotifications();
- }
+ if (mFlipSettingsView.getVisibility() != View.VISIBLE) {
+ flipToSettings();
+ } else {
+ flipToNotifications();
}
}
@@ -1907,21 +1869,19 @@
// Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
mStatusBarView.collapseAllPanels(/*animate=*/ false);
- if (mHasFlipSettings) {
- // reset things to their proper state
- if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
- if (mScrollViewAnim != null) mScrollViewAnim.cancel();
- if (mClearButtonAnim != null) mClearButtonAnim.cancel();
+ // reset things to their proper state
+ if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
+ if (mScrollViewAnim != null) mScrollViewAnim.cancel();
+ if (mClearButtonAnim != null) mClearButtonAnim.cancel();
- mStackScroller.setVisibility(View.VISIBLE);
- mNotificationPanel.setVisibility(View.GONE);
- mFlipSettingsView.setVisibility(View.GONE);
+ mStackScroller.setVisibility(View.VISIBLE);
+ mNotificationPanel.setVisibility(View.GONE);
+ mFlipSettingsView.setVisibility(View.GONE);
- setAreThereNotifications(); // show the clear button
+ setAreThereNotifications(); // show the clear button
- mHeaderFlipper.reset();
- mKeyguardFlipper.reset();
- }
+ mHeaderFlipper.reset();
+ mKeyguardFlipper.reset();
mExpandedVisible = false;
if (mNavigationBarView != null)
@@ -2111,7 +2071,8 @@
private void checkBarModes() {
if (mDemoMode) return;
int sbMode = mStatusBarMode;
- if (panelsEnabled() && (mInteractingWindows & StatusBarManager.WINDOW_STATUS_BAR) != 0) {
+ if (panelsEnabled() && (mInteractingWindows & StatusBarManager.WINDOW_STATUS_BAR) != 0
+ && !mOnKeyguard) {
// if panels are expandable, force the status bar opaque on any interaction
sbMode = MODE_OPAQUE;
}
@@ -2366,12 +2327,6 @@
pw.print (" ");
mNotificationPanel.dump(fd, pw, args);
}
- if (mSettingsPanel != null) {
- pw.println(" mSettingsPanel=" +
- mSettingsPanel + " params=" + mSettingsPanel.getLayoutParams().debug(""));
- pw.print (" ");
- mSettingsPanel.dump(fd, pw, args);
- }
if (DUMPTRUCK) {
synchronized (mNotificationData) {
@@ -2474,13 +2429,6 @@
lp.setMarginStart(mNotificationPanelMarginPx);
mNotificationPanel.setLayoutParams(lp);
- if (mSettingsPanel != null) {
- lp = (FrameLayout.LayoutParams) mSettingsPanel.getLayoutParams();
- lp.gravity = mSettingsPanelGravity;
- lp.setMarginEnd(mNotificationPanelMarginPx);
- mSettingsPanel.setLayoutParams(lp);
- }
-
if (ENABLE_HEADS_UP && mHeadsUpNotificationView != null) {
mHeadsUpNotificationView.setMargin(mNotificationPanelMarginPx);
mStackScroller.getLocationOnScreen(mStackScrollerPosition);
@@ -2536,7 +2484,7 @@
private View.OnClickListener mSettingsButtonListener = new View.OnClickListener() {
public void onClick(View v) {
- if (mHasSettingsPanel) {
+ if (mHasQuickSettings) {
animateExpandSettingsPanel();
} else {
startActivityDismissingKeyguard(
@@ -2734,11 +2682,6 @@
if (mNotificationPanelGravity <= 0) {
mNotificationPanelGravity = Gravity.START | Gravity.TOP;
}
- mSettingsPanelGravity = res.getInteger(R.integer.settings_panel_layout_gravity);
- Log.d(TAG, "mSettingsPanelGravity = " + mSettingsPanelGravity);
- if (mSettingsPanelGravity <= 0) {
- mSettingsPanelGravity = Gravity.END | Gravity.TOP;
- }
mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
mNotificationHeaderHeight = res.getDimensionPixelSize(R.dimen.notification_panel_header_height);
@@ -2972,22 +2915,26 @@
flipToNotifications();
}
mKeyguardStatusView.setVisibility(View.VISIBLE);
+ mKeyguardBottomArea.setVisibility(View.VISIBLE);
mNotificationPanelHeader.setVisibility(View.GONE);
mKeyguardFlipper.setVisibility(View.VISIBLE);
mSettingsContainer.setKeyguardShowing(true);
updateRowStates();
+ checkBarModes();
}
public void hideKeyguard() {
mOnKeyguard = false;
mKeyguardStatusView.setVisibility(View.GONE);
+ mKeyguardBottomArea.setVisibility(View.GONE);
mNotificationPanelHeader.setVisibility(View.VISIBLE);
mKeyguardFlipper.setVisibility(View.GONE);
mSettingsContainer.setKeyguardShowing(false);
updateRowStates();
instantCollapseNotificationPanel();
+ checkBarModes();
}
public void userActivity() {
@@ -3016,8 +2963,14 @@
}
private void instantExpandNotificationsPanel() {
- mExpandedVisible = true;
- mNotificationPanel.setExpandedFraction(1);
+ mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ mNotificationPanel.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ mNotificationPanel.setExpandedFraction(1);
+ }
+ });
}
private void instantCollapseNotificationPanel() {
@@ -3035,16 +2988,16 @@
return mKeyguardMaxNotificationCount;
}
+ public NavigationBarView getNavigationBarView() {
+ return mNavigationBarView;
+ }
+
/**
* @return a ViewGroup that spans the entire panel which contains the quick settings
*/
public ViewGroup getQuickSettingsOverlayParent() {
- if (mHasSettingsPanel) {
- if (mHasFlipSettings) {
- return mNotificationPanel;
- } else {
- return mSettingsPanel;
- }
+ if (mHasQuickSettings) {
+ return mNotificationPanel;
} else {
return null;
}
@@ -3070,7 +3023,7 @@
mSettingsButton = (ImageView) holder.findViewById(R.id.settings_button);
if (mSettingsButton != null) {
mSettingsButton.setOnClickListener(mSettingsButtonListener);
- if (mHasSettingsPanel) {
+ if (mHasQuickSettings) {
// the settings panel is hiding behind this button
mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings);
mSettingsButton.setVisibility(View.VISIBLE);
@@ -3080,11 +3033,9 @@
mSettingsButton.setImageResource(R.drawable.ic_notify_settings);
}
}
- if (mHasFlipSettings) {
- mNotificationButton = (ImageView) holder.findViewById(R.id.notification_button);
- if (mNotificationButton != null) {
- mNotificationButton.setOnClickListener(mNotificationButtonListener);
- }
+ mNotificationButton = (ImageView) holder.findViewById(R.id.notification_button);
+ if (mNotificationButton != null) {
+ mNotificationButton.setOnClickListener(mNotificationButtonListener);
}
}
@@ -3099,7 +3050,7 @@
}
public void userSetup(boolean userSetup) {
- if (mSettingsButton != null && mHasFlipSettings) {
+ if (mSettingsButton != null) {
mSettingsButton.setVisibility(userSetup ? View.VISIBLE : View.INVISIBLE);
}
}
@@ -3128,25 +3079,27 @@
mSettingsButtonAnim = start(
setVisibilityWhenDone(
ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f)
- .setDuration(FLIP_DURATION),
+ .setDuration(FLIP_DURATION_OUT),
mStackScroller, View.INVISIBLE));
mNotificationButton.setVisibility(View.VISIBLE);
mNotificationButtonAnim = start(
- ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f)
- .setDuration(FLIP_DURATION));
+ startDelay(FLIP_DURATION_OUT,
+ ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f)
+ .setDuration(FLIP_DURATION_IN)));
}
public void flipToNotifications() {
mNotificationButtonAnim = start(
setVisibilityWhenDone(
ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 0f)
- .setDuration(FLIP_DURATION),
+ .setDuration(FLIP_DURATION_OUT),
mNotificationButton, View.INVISIBLE));
mSettingsButton.setVisibility(View.VISIBLE);
mSettingsButtonAnim = start(
- ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f)
- .setDuration(FLIP_DURATION));
+ startDelay(FLIP_DURATION_OUT,
+ ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f)
+ .setDuration(FLIP_DURATION_IN)));
}
public void cancel() {
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 583fc3e..6ba18b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -38,13 +38,10 @@
PhoneStatusBar mBar;
int mScrimColor;
int mScrimColorKeyguard;
- float mSettingsPanelDragzoneFrac;
- float mSettingsPanelDragzoneMin;
- boolean mFullWidthNotifications;
PanelView mFadingPanel = null;
PanelView mLastFullyOpenedPanel = null;
- PanelView mNotificationPanel, mSettingsPanel;
+ PanelView mNotificationPanel;
private boolean mShouldFade;
private final PhoneStatusBarTransitions mBarTransitions;
@@ -54,13 +51,6 @@
Resources res = getContext().getResources();
mScrimColor = res.getColor(R.color.notification_panel_scrim_color);
mScrimColorKeyguard = res.getColor(R.color.notification_panel_scrim_color_keyguard);
- mSettingsPanelDragzoneMin = res.getDimension(R.dimen.settings_panel_dragzone_min);
- try {
- mSettingsPanelDragzoneFrac = res.getFraction(R.dimen.settings_panel_dragzone_fraction, 1, 1);
- } catch (NotFoundException ex) {
- mSettingsPanelDragzoneFrac = 0f;
- }
- mFullWidthNotifications = mSettingsPanelDragzoneFrac <= 0f;
mBarTransitions = new PhoneStatusBarTransitions(this);
}
@@ -72,15 +62,8 @@
mBar = bar;
}
- public boolean hasFullWidthNotifications() {
- return mFullWidthNotifications;
- }
-
@Override
public void onAttachedToWindow() {
- for (PanelView pv : mPanels) {
- pv.setRubberbandingEnabled(!mFullWidthNotifications);
- }
mBarTransitions.init();
}
@@ -89,10 +72,7 @@
super.addPanel(pv);
if (pv.getId() == R.id.notification_panel) {
mNotificationPanel = pv;
- } else if (pv.getId() == R.id.settings_panel){
- mSettingsPanel = pv;
}
- pv.setRubberbandingEnabled(!mFullWidthNotifications);
}
@Override
@@ -117,34 +97,10 @@
@Override
public PanelView selectPanelForTouch(MotionEvent touch) {
- final float x = touch.getX();
- final boolean isLayoutRtl = isLayoutRtl();
-
- if (mFullWidthNotifications) {
- // No double swiping. If either panel is open, nothing else can be pulled down.
- return ((mSettingsPanel == null ? 0 : mSettingsPanel.getExpandedHeight())
- + mNotificationPanel.getExpandedHeight() > 0)
- ? null
- : mNotificationPanel;
- }
-
- // We split the status bar into thirds: the left 2/3 are for notifications, and the
- // right 1/3 for quick settings. If you pull the status bar down a second time you'll
- // toggle panels no matter where you pull it down.
-
- final float w = getMeasuredWidth();
- float region = (w * mSettingsPanelDragzoneFrac);
-
- if (DEBUG) {
- Log.v(TAG, String.format(
- "w=%.1f frac=%.3f region=%.1f min=%.1f x=%.1f w-x=%.1f",
- w, mSettingsPanelDragzoneFrac, region, mSettingsPanelDragzoneMin, x, (w-x)));
- }
-
- if (region < mSettingsPanelDragzoneMin) region = mSettingsPanelDragzoneMin;
-
- final boolean showSettings = isLayoutRtl ? (x < region) : (w - region < x);
- return showSettings ? mSettingsPanel : mNotificationPanel;
+ // No double swiping. If either panel is open, nothing else can be pulled down.
+ return mNotificationPanel.getExpandedHeight() > 0
+ ? null
+ : mNotificationPanel;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsContainerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsContainerView.java
index 02e9c0d..c44cb0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsContainerView.java
@@ -62,18 +62,19 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mScrim = new ScrimView(mContext);
- addView(mScrim);
- mScrim.setAlpha(sShowScrim ? 1 : 0);
+ if (sShowScrim) {
+ mScrim = new ScrimView(mContext);
+ addView(mScrim);
+ }
// TODO: Setup the layout transitions
LayoutTransition transitions = getLayoutTransition();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (mScrim.getAlpha() == 1) {
- mScrim.animate().alpha(0).setDuration(1000).start();
+ if (mScrim != null) {
sShowScrim = false;
+ removeView(mScrim);
}
return super.onTouchEvent(event);
}
@@ -144,7 +145,9 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- mScrim.bringToFront();
+ if (mScrim != null) {
+ mScrim.bringToFront();
+ }
final int N = getChildCount();
final boolean isLayoutRtl = isLayoutRtl();
final int width = getWidth();
@@ -206,6 +209,7 @@
}
private static final class ScrimView extends View {
+ private static final int SCRIM = 0x4f000000;
private static final int COLOR = 0xaf4285f4;
private final Paint mLinePaint;
@@ -240,6 +244,7 @@
final int h = getMeasuredHeight();
final int f = mStrokeWidth * 3 / 4;
+ canvas.drawColor(SCRIM);
canvas.drawPath(line(f, h / 2, w - f, h / 2), mLinePaint);
canvas.drawPath(line(w / 2, f, w / 2, h - f), mLinePaint);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index 9b25046..e1ef83a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -608,7 +608,7 @@
Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
mZenModeState.enabled = mode != Settings.Global.ZEN_MODE_OFF;
mZenModeState.zenMode = mode;
- mZenModeState.label = ZenModeView.MODE_LABEL;
+ mZenModeState.label = mContext.getString(R.string.zen_mode_title);
mZenModeState.iconId = R.drawable.stat_sys_zen_limited;
mZenModeCallback.refreshView(mZenModeTile, mZenModeState);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
deleted file mode 100644
index c10a0d4..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.EventLog;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-
-import com.android.systemui.EventLogTags;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.GestureRecorder;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RotationLockController;
-
-public class SettingsPanelView extends PanelView {
- public static final boolean DEBUG_GESTURES = true;
-
- private QuickSettings mQS;
- private QuickSettingsContainerView mQSContainer;
-
- Drawable mHandleBar;
- int mHandleBarHeight;
- View mHandleView;
-
- public SettingsPanelView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mQSContainer = (QuickSettingsContainerView) findViewById(R.id.quick_settings_container);
-
- Resources resources = getContext().getResources();
- mHandleBar = resources.getDrawable(R.drawable.status_bar_close);
- mHandleBarHeight = resources.getDimensionPixelSize(R.dimen.close_handle_height);
- mHandleView = findViewById(R.id.handle);
- }
-
- public void setQuickSettings(QuickSettings qs) {
- mQS = qs;
- }
-
- @Override
- public void setBar(PanelBar panelBar) {
- super.setBar(panelBar);
-
- if (mQS != null) {
- mQS.setBar(panelBar);
- }
- }
-
- public void setImeWindowStatus(boolean visible) {
- if (mQS != null) {
- mQS.setImeWindowStatus(visible);
- }
- }
-
- public void setup(NetworkController networkController, BluetoothController bluetoothController,
- BatteryController batteryController, LocationController locationController,
- RotationLockController rotationLockController) {
- if (mQS != null) {
- mQS.setup(networkController, bluetoothController, batteryController,
- locationController, rotationLockController);
- }
- }
-
- void updateResources() {
- if (mQS != null) {
- mQS.updateResources();
- }
- if (mQSContainer != null) {
- mQSContainer.updateResources();
- }
- requestLayout();
- }
-
- @Override
- public void fling(float vel, boolean always) {
- GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
- if (gr != null) {
- gr.tag(
- "fling " + ((vel > 0) ? "open" : "closed"),
- "settings,v=" + vel);
- }
- super.fling(vel, always);
- }
-
- public void setService(PhoneStatusBar phoneStatusBar) {
- if (mQS != null) {
- mQS.setService(phoneStatusBar);
- }
- }
-
- @Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
- event.getText()
- .add(getContext().getString(R.string.accessibility_desc_quick_settings));
- return true;
- }
-
- return super.dispatchPopulateAccessibilityEvent(event);
- }
-
- // We draw the handle ourselves so that it's always glued to the bottom of the window.
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- if (changed) {
- final int pl = getPaddingLeft();
- final int pr = getPaddingRight();
- mHandleBar.setBounds(pl, 0, getWidth() - pr, (int) mHandleBarHeight);
- }
- }
-
- @Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
- final int off = (int) (getHeight() - mHandleBarHeight - getPaddingBottom());
- canvas.translate(0, off);
- mHandleBar.setState(mHandleView.getDrawableState());
- mHandleBar.draw(canvas);
- canvas.translate(0, -off);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (DEBUG_GESTURES) {
- if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
- EventLog.writeEvent(EventLogTags.SYSUI_QUICKPANEL_TOUCH,
- event.getActionMasked(), (int) event.getX(), (int) event.getY());
- }
- }
- return super.onTouchEvent(event);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 460f122..501d3f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -56,7 +56,7 @@
private boolean mScreenOn = false;
private KeyguardBouncer mBouncer;
private boolean mShowing;
- private boolean mOccluded = false;
+ private boolean mOccluded;
public StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback,
LockPatternUtils lockPatternUtils) {
@@ -103,10 +103,8 @@
}
public void showBouncer() {
- if (!mOccluded) {
- mBouncer.show();
- updateBackButtonState();
- }
+ mBouncer.show();
+ updateBackButtonState();
}
/**
@@ -156,13 +154,8 @@
public void setOccluded(boolean occluded) {
mOccluded = occluded;
- if (occluded) {
- mPhoneStatusBar.hideKeyguard();
- mBouncer.hide();
- } else {
- showBouncerOrKeyguard();
- }
mStatusBarWindowManager.setKeyguardOccluded(occluded);
+ updateBackButtonState();
}
/**
@@ -174,6 +167,7 @@
mStatusBarWindowManager.setKeyguardShowing(false);
mBouncer.hide();
mViewMediatorCallback.keyguardGone();
+ updateBackButtonState();
}
/**
@@ -215,6 +209,11 @@
} else {
mContainer.setSystemUiVisibility(vis | View.STATUS_BAR_DISABLE_BACK);
}
+ if (!(mShowing && !mOccluded) || mBouncer.isShowing()) {
+ mPhoneStatusBar.getNavigationBarView().setVisibility(View.VISIBLE);
+ } else {
+ mPhoneStatusBar.getNavigationBarView().setVisibility(View.GONE);
+ }
}
public boolean onMenuPressed() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index 716e326..d175d7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -99,10 +99,14 @@
}
private void adjustScreenOrientation(State state) {
- if (!state.isKeyguardShowingAndNotOccluded() || mKeyguardScreenRotation) {
- mLp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
+ if (state.isKeyguardShowingAndNotOccluded()) {
+ if (mKeyguardScreenRotation) {
+ mLp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
+ } else {
+ mLp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+ }
} else {
- mLp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+ mLp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
}
}
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 1d675bd..c5dae85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -52,6 +53,16 @@
}
@Override
+ protected boolean fitSystemWindows(Rect insets) {
+ if (getFitsSystemWindows()) {
+ setPadding(insets.left, insets.top, insets.right, insets.bottom);
+ } else {
+ setPadding(0, 0, 0, 0);
+ }
+ return true;
+ }
+
+ @Override
protected void onAttachedToWindow () {
super.onAttachedToWindow();
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 49cf78b..c1662ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java
@@ -42,13 +42,13 @@
import android.widget.TextView;
import android.widget.Toast;
+import com.android.systemui.R;
import com.android.systemui.statusbar.phone.ZenModeView.Adapter.ExitCondition;
public class ZenModeView extends RelativeLayout {
private static final String TAG = ZenModeView.class.getSimpleName();
private static final boolean DEBUG = false;
- public static final String MODE_LABEL = "Limited interruptions";
public static final int BACKGROUND = 0xff282828;
private static final Typeface CONDENSED =
@@ -91,7 +91,7 @@
LayoutParams lp = null;
mModeText = new TextView(mContext);
- mModeText.setText(MODE_LABEL);
+ mModeText.setText(R.string.zen_mode_title);
mModeText.setId(android.R.id.title);
mModeText.setTextColor(GRAY);
mModeText.setTypeface(CONDENSED);
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 9800bc9..36d94a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -20,7 +20,6 @@
import android.content.res.Configuration;
import android.graphics.Canvas;
-import android.graphics.Outline;
import android.graphics.Paint;
import android.util.AttributeSet;
@@ -31,6 +30,7 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.widget.OverScroller;
import com.android.systemui.ExpandHelper;
@@ -55,7 +55,7 @@
private static final int INVALID_POINTER = -1;
private SwipeHelper mSwipeHelper;
- private boolean mSwipingInProgress = true;
+ private boolean mSwipingInProgress;
private int mCurrentStackHeight = Integer.MAX_VALUE;
private int mOwnScrollY;
private int mMaxLayoutHeight;
@@ -73,7 +73,6 @@
private int mSidePaddings;
private Paint mDebugPaint;
- private int mBackgroundRoundedRectCornerRadius;
private int mContentHeight;
private int mCollapsedSize;
private int mBottomStackPeekSize;
@@ -145,9 +144,6 @@
mSidePaddings = context.getResources()
.getDimensionPixelSize(R.dimen.notification_side_padding);
- mBackgroundRoundedRectCornerRadius = context.getResources()
- .getDimensionPixelSize(
- com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
mCollapsedSize = context.getResources()
.getDimensionPixelSize(R.dimen.notification_row_min_height);
mBottomStackPeekSize = context.getResources()
@@ -177,18 +173,23 @@
View child = getChildAt(i);
float width = child.getMeasuredWidth();
float height = child.getMeasuredHeight();
- int oldWidth = child.getWidth();
- int oldHeight = child.getHeight();
child.layout((int) (centerX - width / 2.0f),
0,
(int) (centerX + width / 2.0f),
(int) height);
- updateChildOutline(child, width, height, oldWidth, oldHeight);
}
setMaxLayoutHeight(getHeight() - mEmptyMarginBottom);
- updateScrollPositionIfNecessary();
- updateChildren();
updateContentHeight();
+ getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ updateScrollPositionIfNecessary();
+ updateChildren();
+ getViewTreeObserver().removeOnPreDrawListener(this);
+ return true;
+ }
+ });
+
}
public void setChildLocationsChangedListener(OnChildLocationsChangedListener listener) {
@@ -227,7 +228,6 @@
mCurrentStackScrollState.setScrollY(mOwnScrollY);
mStackScrollAlgorithm.getStackScrollState(mCurrentStackScrollState);
mCurrentStackScrollState.apply();
- mOwnScrollY = mCurrentStackScrollState.getScrollY();
if (mListener != null) {
mListener.onChildLocationsChanged(this);
}
@@ -240,31 +240,6 @@
return false;
}
- private void updateChildOutline(View child,
- float width,
- float height,
- int oldWidth,
- int oldHeight) {
- // 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.
- View container = child.findViewById(R.id.container);
- if (container != null && (oldWidth != width || oldHeight != height)) {
- Outline outline = getOutlineForSize(container.getLeft(),
- container.getTop(),
- container.getWidth(),
- container.getHeight());
- child.setOutline(outline);
- }
- }
-
- private Outline getOutlineForSize(int leftInset, int topInset, int width, int height) {
- Outline result = new Outline();
- result.setRoundRect(leftInset, topInset, leftInset + width, topInset + height,
- mBackgroundRoundedRectCornerRadius);
- return result;
- }
-
private void updateScrollPositionIfNecessary() {
int scrollRange = getScrollRange();
if (scrollRange < mOwnScrollY) {
@@ -284,7 +259,7 @@
*
* @return either the layout height or the externally defined height, whichever is smaller
*/
- private float getLayoutHeight() {
+ private int getLayoutHeight() {
return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
}
@@ -640,14 +615,48 @@
private int getScrollRange() {
int scrollRange = 0;
- if (getChildCount() > 0) {
+ View firstChild = getFirstChildNotGone();
+ if (firstChild != null) {
int contentHeight = getContentHeight();
- scrollRange = Math.max(0,
- contentHeight - mMaxLayoutHeight + mBottomStackPeekSize);
+ 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
+ // end state.
+ scrollRange = Math.max(scrollRange, firstChildMaxExpandHeight - mCollapsedSize);
+ }
}
return scrollRange;
}
+ /**
+ * @return the first child which has visibility unequal to GONE
+ */
+ private View getFirstChildNotGone() {
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != View.GONE) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ private int getMaxExpandHeight(View view) {
+ if (view instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ return row.getMaximumAllowedExpandHeight();
+ }
+ return view.getHeight();
+ }
+
private int getContentHeight() {
return mContentHeight;
}
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 431f6fe..a7363f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -20,6 +20,7 @@
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
@@ -47,13 +48,14 @@
private StackIndentationFunctor mTopStackIndentationFunctor;
private StackIndentationFunctor mBottomStackIndentationFunctor;
- private float mLayoutHeight;
+ private int mLayoutHeight;
private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState();
private boolean mIsExpansionChanging;
private int mFirstChildMaxHeight;
private boolean mIsExpanded;
private View mFirstChildWhileExpanding;
private boolean mExpandedOnStart;
+ private int mTopStackTotalSize;
public StackScrollAlgorithm(Context context) {
initConstants(context);
@@ -72,16 +74,16 @@
mZDistanceBetweenElements = context.getResources()
.getDimensionPixelSize(R.dimen.z_distance_between_notifications);
mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements;
-
+ mTopStackTotalSize = mCollapsedSize + mPaddingBetweenElements;
mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
MAX_ITEMS_IN_TOP_STACK,
mTopStackPeekSize,
- mCollapsedSize + mPaddingBetweenElements,
+ mTopStackTotalSize,
0.5f);
mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
MAX_ITEMS_IN_BOTTOM_STACK,
mBottomStackPeekSize,
- mBottomStackPeekSize,
+ mCollapsedSize + mPaddingBetweenElements + mBottomStackPeekSize,
0.5f);
}
@@ -94,14 +96,17 @@
// First we reset the view states to their default values.
resultState.resetViewStates();
- // The first element is always in there so it's initialized with 1.0f;
- algorithmState.itemsInTopStack = 1.0f;
+ algorithmState.itemsInTopStack = 0.0f;
algorithmState.partialInTop = 0.0f;
algorithmState.lastTopStackIndex = 0;
- algorithmState.scrollY = resultState.getScrollY();
+ algorithmState.scrolledPixelsTop = 0;
algorithmState.itemsInBottomStack = 0.0f;
+ algorithmState.partialInBottom = 0.0f;
+
updateVisibleChildren(resultState, algorithmState);
+ algorithmState.scrollY = getAlgorithmScrollPosition(resultState, algorithmState);
+
// Phase 1:
findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState);
@@ -110,9 +115,42 @@
// Phase 3:
updateZValuesForState(resultState, algorithmState);
+ }
- // write the algorithm state to the result
- resultState.setScrollY(algorithmState.scrollY);
+ /**
+ * 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;
}
/**
@@ -141,13 +179,12 @@
*/
private void updatePositionsForState(StackScrollState resultState,
StackScrollAlgorithmState algorithmState) {
- float stackHeight = getLayoutHeight();
// The starting position of the bottom stack peek
- float bottomPeekStart = stackHeight - mBottomStackPeekSize;
+ float bottomPeekStart = mLayoutHeight - mBottomStackPeekSize;
// The position where the bottom stack starts.
- float transitioningPositionStart = bottomPeekStart - mCollapsedSize;
+ float bottomStackStart = bottomPeekStart - mCollapsedSize;
// The y coordinate of the current child.
float currentYPosition = 0.0f;
@@ -160,76 +197,110 @@
for (int i = 0; i < childCount; i++) {
View child = algorithmState.visibleChildren.get(i);
StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
- childViewState.yTranslation = currentYPosition;
childViewState.location = StackScrollState.ViewState.LOCATION_UNKNOWN;
int childHeight = child.getHeight();
- // The y position after this element
- float nextYPosition = currentYPosition + childHeight + mPaddingBetweenElements;
float yPositionInScrollViewAfterElement = yPositionInScrollView
+ childHeight
+ mPaddingBetweenElements;
- float scrollOffset = yPositionInScrollViewAfterElement - algorithmState.scrollY;
- if (i < algorithmState.lastTopStackIndex) {
+ float scrollOffset = yPositionInScrollView - algorithmState.scrollY + mCollapsedSize;
+
+ if (i == algorithmState.lastTopStackIndex + 1) {
+ // Normally the position of this child is the position in the regular scrollview,
+ // but if the two stacks are very close to each other,
+ // then have have to push it even more upwards to the position of the bottom
+ // stack start.
+ currentYPosition = Math.min(scrollOffset, bottomStackStart);
+ }
+ childViewState.yTranslation = currentYPosition;
+
+ // The y position after this element
+ float nextYPosition = currentYPosition + childHeight +
+ mPaddingBetweenElements;
+
+ if (i <= algorithmState.lastTopStackIndex) {
// Case 1:
// We are in the top Stack
- nextYPosition = updateStateForTopStackChild(algorithmState,
- numberOfElementsCompletelyIn,
- i, childViewState);
- } else if (i == algorithmState.lastTopStackIndex) {
+ updateStateForTopStackChild(algorithmState,
+ numberOfElementsCompletelyIn, i, childHeight, childViewState, scrollOffset);
+ clampYTranslation(childViewState, childHeight);
+ // check if we are overlapping with the bottom stack
+ if (childViewState.yTranslation + childHeight + mPaddingBetweenElements
+ >= bottomStackStart && !mIsExpansionChanging) {
+ // TODO: handle overlapping sizes with end stack better
+ // we just collapse this element
+ childViewState.height = mCollapsedSize;
+ }
+ } else if (nextYPosition >= bottomStackStart) {
// Case 2:
- // First element of regular scrollview comes next, so the position is just the
- // scrolling position
- nextYPosition = updateStateForFirstScrollingChild(transitioningPositionStart,
- childViewState, scrollOffset);
- } else if (nextYPosition >= transitioningPositionStart) {
- if (currentYPosition >= transitioningPositionStart) {
- // Case 3:
+ // We are in the bottom stack.
+ if (currentYPosition >= bottomStackStart) {
// According to the regular scroll view we are fully translated out of the
// bottom of the screen so we are fully in the bottom stack
- nextYPosition = updateStateForChildFullyInBottomStack(algorithmState,
- transitioningPositionStart, childViewState, childHeight);
+ updateStateForChildFullyInBottomStack(algorithmState,
+ bottomStackStart, childViewState, childHeight);
} else {
- // Case 4:
// According to the regular scroll view we are currently translating out of /
// into the bottom of the screen
- nextYPosition = updateStateForChildTransitioningInBottom(
- algorithmState, stackHeight, transitioningPositionStart,
- currentYPosition, childViewState,
- childHeight, nextYPosition);
+ updateStateForChildTransitioningInBottom(algorithmState,
+ bottomStackStart, bottomPeekStart, currentYPosition,
+ childViewState, childHeight);
}
} else {
+ // Case 3:
+ // We are in the regular scroll area.
childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA;
+ clampYTranslation(childViewState, childHeight);
}
+
// The first card is always rendered.
if (i == 0) {
childViewState.alpha = 1.0f;
+ childViewState.yTranslation = 0;
childViewState.location = StackScrollState.ViewState.LOCATION_FIRST_CARD;
}
if (childViewState.location == StackScrollState.ViewState.LOCATION_UNKNOWN) {
Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
}
- nextYPosition = Math.max(0, nextYPosition);
- currentYPosition = nextYPosition;
+ currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
yPositionInScrollView = yPositionInScrollViewAfterElement;
}
}
/**
- * Update the state for the first child which is in the regular scrolling area.
+ * Clamp the yTranslation both up and down to valid positions.
*
- * @param transitioningPositionStart the transition starting position of the bottom stack
* @param childViewState the view state of the child
- * @param scrollOffset the position in the regular scroll view after this child
- * @return the next child position
+ * @param childHeight the height of this child
*/
- private float updateStateForFirstScrollingChild(float transitioningPositionStart,
- StackScrollState.ViewState childViewState, float scrollOffset) {
- childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_PEEKING;
- if (scrollOffset < transitioningPositionStart) {
- return scrollOffset;
- } else {
- return transitioningPositionStart;
- }
+ private void clampYTranslation(StackScrollState.ViewState childViewState, int childHeight) {
+ clampPositionToBottomStackStart(childViewState, childHeight);
+ clampPositionToTopStackEnd(childViewState, childHeight);
+ }
+
+ /**
+ * Clamp the yTranslation of the child down such that its end is at most on the beginning of
+ * the bottom stack.
+ *
+ * @param childViewState the view state of the child
+ * @param childHeight the height of this child
+ */
+ private void clampPositionToBottomStackStart(StackScrollState.ViewState childViewState,
+ int childHeight) {
+ childViewState.yTranslation = Math.min(childViewState.yTranslation,
+ mLayoutHeight - mBottomStackPeekSize - childHeight);
+ }
+
+ /**
+ * Clamp the yTranslation of the child up such that its end is at lest on the end of the top
+ * stack.
+ *
+ * @param childViewState the view state of the child
+ * @param childHeight the height of this child
+ */
+ private void clampPositionToTopStackEnd(StackScrollState.ViewState childViewState,
+ int childHeight) {
+ childViewState.yTranslation = Math.max(childViewState.yTranslation,
+ mCollapsedSize - childHeight);
}
private int getMaxAllowedChildHeight(View child) {
@@ -240,41 +311,35 @@
return child.getHeight();
}
- private float updateStateForChildTransitioningInBottom(StackScrollAlgorithmState algorithmState,
- float stackHeight, float transitioningPositionStart, float currentYPosition,
- StackScrollState.ViewState childViewState, int childHeight, float nextYPosition) {
- float newSize = transitioningPositionStart + mCollapsedSize - currentYPosition;
- newSize = Math.min(childHeight, newSize);
- // Transitioning element on top of bottom stack:
- algorithmState.partialInBottom = 1.0f - (
- (stackHeight - mBottomStackPeekSize - nextYPosition) / mCollapsedSize);
- // Our element can be expanded, so we might even have to scroll further than
- // mCollapsedSize
- algorithmState.partialInBottom = Math.min(1.0f, algorithmState.partialInBottom);
- float offset = mBottomStackIndentationFunctor.getValue(
- algorithmState.partialInBottom);
- nextYPosition = transitioningPositionStart + offset;
- algorithmState.itemsInBottomStack += algorithmState.partialInBottom;
- // TODO: only temporarily collapse
- if (childHeight != (int) newSize) {
- childViewState.height = (int) newSize;
- }
- childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA;
+ private void updateStateForChildTransitioningInBottom(StackScrollAlgorithmState algorithmState,
+ float transitioningPositionStart, float bottomPeakStart, float currentYPosition,
+ StackScrollState.ViewState childViewState, int childHeight) {
- return nextYPosition;
+ // This is the transitioning element on top of bottom stack, calculate how far we are in.
+ algorithmState.partialInBottom = 1.0f - (
+ (transitioningPositionStart - currentYPosition) / (childHeight +
+ mPaddingBetweenElements));
+
+ // 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;
+
+ // We want at least to be at the end of the top stack when collapsing
+ clampPositionToTopStackEnd(childViewState, childHeight);
+ childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA;
}
- private float updateStateForChildFullyInBottomStack(StackScrollAlgorithmState algorithmState,
+ private void updateStateForChildFullyInBottomStack(StackScrollAlgorithmState algorithmState,
float transitioningPositionStart, StackScrollState.ViewState childViewState,
int childHeight) {
- float nextYPosition;
+ float currentYPosition;
algorithmState.itemsInBottomStack += 1.0f;
if (algorithmState.itemsInBottomStack < MAX_ITEMS_IN_BOTTOM_STACK) {
// We are visually entering the bottom stack
- nextYPosition = transitioningPositionStart
- + mBottomStackIndentationFunctor.getValue(
- algorithmState.itemsInBottomStack);
+ currentYPosition = transitioningPositionStart
+ + mBottomStackIndentationFunctor.getValue(algorithmState.itemsInBottomStack);
childViewState.location = StackScrollState.ViewState.LOCATION_BOTTOM_STACK_PEEKING;
} else {
// we are fully inside the stack
@@ -285,43 +350,56 @@
childViewState.alpha = 1.0f - algorithmState.partialInBottom;
}
childViewState.location = StackScrollState.ViewState.LOCATION_BOTTOM_STACK_HIDDEN;
- nextYPosition = transitioningPositionStart + mBottomStackPeekSize;
+ currentYPosition = mLayoutHeight;
}
- // TODO: only temporarily collapse
- if (childHeight != mCollapsedSize) {
- childViewState.height = mCollapsedSize;
- }
- return nextYPosition;
+ childViewState.yTranslation = currentYPosition - childHeight;
+ clampPositionToTopStackEnd(childViewState, childHeight);
}
- private float updateStateForTopStackChild(StackScrollAlgorithmState algorithmState,
- int numberOfElementsCompletelyIn, int i, StackScrollState.ViewState childViewState) {
+ private void updateStateForTopStackChild(StackScrollAlgorithmState algorithmState,
+ int numberOfElementsCompletelyIn, int i, int childHeight,
+ StackScrollState.ViewState childViewState, float scrollOffset) {
- float nextYPosition = 0;
// First we calculate the index relative to the current stack window of size at most
// {@link #MAX_ITEMS_IN_TOP_STACK}
- int paddedIndex = i
+ int paddedIndex = i - 1
- Math.max(numberOfElementsCompletelyIn - MAX_ITEMS_IN_TOP_STACK, 0);
if (paddedIndex >= 0) {
+
// We are currently visually entering the top stack
- nextYPosition = mCollapsedSize + mPaddingBetweenElements -
- mTopStackIndentationFunctor.getValue(
- algorithmState.itemsInTopStack - i - 1);
- nextYPosition = Math.min(nextYPosition, mLayoutHeight - mCollapsedSize
- - mBottomStackPeekSize);
- if (paddedIndex == 0) {
- childViewState.alpha = 1.0f - algorithmState.partialInTop;
- childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_HIDDEN;
+ float distanceToStack = childHeight - algorithmState.scrolledPixelsTop;
+ if (i == algorithmState.lastTopStackIndex && distanceToStack > mTopStackTotalSize) {
+
+ // Child is currently translating into stack but not yet inside slow down zone.
+ // Handle it like the regular scrollview.
+ childViewState.yTranslation = scrollOffset;
} else {
- childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_PEEKING;
+ // Apply stacking logic.
+ float numItemsBefore;
+ if (i == algorithmState.lastTopStackIndex) {
+ numItemsBefore = 1.0f - (distanceToStack / mTopStackTotalSize);
+ } else {
+ numItemsBefore = algorithmState.itemsInTopStack - i;
+ }
+ // The end position of the current child
+ float currentChildEndY = mCollapsedSize + mTopStackTotalSize -
+ mTopStackIndentationFunctor.getValue(numItemsBefore);
+ childViewState.yTranslation = currentChildEndY - childHeight;
}
+ childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_PEEKING;
} else {
- // We are hidden behind the top card and faded out, so we can hide ourselves.
- childViewState.alpha = 0.0f;
+ if (paddedIndex == -1) {
+ childViewState.alpha = 1.0f - algorithmState.partialInTop;
+ } else {
+ // We are hidden behind the top card and faded out, so we can hide ourselves.
+ childViewState.alpha = 0.0f;
+ }
+ childViewState.yTranslation = mCollapsedSize - childHeight;
childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_HIDDEN;
}
- return nextYPosition;
+
+
}
/**
@@ -347,10 +425,22 @@
+ childHeight
+ mPaddingBetweenElements;
if (yPositionInScrollView < algorithmState.scrollY) {
- if (yPositionInScrollViewAfterElement <= algorithmState.scrollY) {
+ if (i == 0 && algorithmState.scrollY == mCollapsedSize) {
+
+ // The starting position of the bottom stack peek
+ int bottomPeekStart = mLayoutHeight - mBottomStackPeekSize;
+ // Collapse and expand the first child while the shade is being expanded
+ float maxHeight = mIsExpansionChanging && child == mFirstChildWhileExpanding
+ ? mFirstChildMaxHeight
+ : childHeight;
+ childViewState.height = (int) Math.max(Math.min(bottomPeekStart, maxHeight),
+ mCollapsedSize);
+ algorithmState.itemsInTopStack = 1.0f;
+
+ } else if (yPositionInScrollViewAfterElement < algorithmState.scrollY) {
// According to the regular scroll view we are fully off screen
algorithmState.itemsInTopStack += 1.0f;
- if (childHeight != mCollapsedSize) {
+ if (i == 0) {
childViewState.height = mCollapsedSize;
}
} else {
@@ -360,45 +450,27 @@
- mPaddingBetweenElements
- algorithmState.scrollY;
+ if (i == 0) {
+ newSize += mCollapsedSize;
+ }
+
// How much did we scroll into this child
- algorithmState.partialInTop = (mCollapsedSize - newSize) / (mCollapsedSize
+ algorithmState.scrolledPixelsTop = childHeight - newSize;
+ algorithmState.partialInTop = (algorithmState.scrolledPixelsTop) / (childHeight
+ mPaddingBetweenElements);
// Our element can be expanded, so this can get negative
algorithmState.partialInTop = Math.max(0.0f, algorithmState.partialInTop);
algorithmState.itemsInTopStack += algorithmState.partialInTop;
- // TODO: handle overlapping sizes with end stack
newSize = Math.max(mCollapsedSize, newSize);
- // TODO: only temporarily collapse
- if (newSize != childHeight) {
+ if (i == 0) {
childViewState.height = (int) newSize;
-
- // We decrease scrollY by the same amount we made this child smaller.
- // The new scroll position is therefore the start of the element
- algorithmState.scrollY = (int) yPositionInScrollView;
- resultState.setScrollY(algorithmState.scrollY);
}
- if (childHeight > mCollapsedSize) {
- // If we are just resizing this child, this element is not treated to be
- // transitioning into the stack and therefore it is the last element in
- // the stack.
- algorithmState.lastTopStackIndex = i;
- break;
- }
+ algorithmState.lastTopStackIndex = i;
+ break;
}
} else {
- algorithmState.lastTopStackIndex = i;
- if (i == 0) {
-
- // The starting position of the bottom stack peek
- float bottomPeekStart = getLayoutHeight() - mBottomStackPeekSize;
- // Collapse and expand the first child while the shade is being expanded
- float maxHeight = mIsExpansionChanging && child == mFirstChildWhileExpanding
- ? mFirstChildMaxHeight
- : childHeight;
- childViewState.height = (int) Math.max(Math.min(bottomPeekStart, maxHeight),
- mCollapsedSize);
- }
+ algorithmState.lastTopStackIndex = i - 1;
// We are already past the stack so we can end the loop
break;
}
@@ -435,11 +507,11 @@
}
}
- public float getLayoutHeight() {
+ public int getLayoutHeight() {
return mLayoutHeight;
}
- public void setLayoutHeight(float layoutHeight) {
+ public void setLayoutHeight(int layoutHeight) {
this.mLayoutHeight = layoutHeight;
}
@@ -512,10 +584,12 @@
public float partialInTop;
/**
+ * The number of pixels the last child in the top stack has scrolled in to the stack
+ */
+ public float scrolledPixelsTop;
+
+ /**
* The last item index which is in the top stack.
- * NOTE: In the top stack the item after the transitioning element is also in the stack!
- * This is needed to ensure a smooth transition between the y position in the regular
- * scrollview and the one in the stack.
*/
public int lastTopStackIndex;
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 06a08f3..9742e65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -16,10 +16,14 @@
package com.android.systemui.statusbar.stack;
+import android.graphics.Outline;
+import android.graphics.Rect;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+import com.android.systemui.R;
+
import java.util.HashMap;
import java.util.Map;
@@ -34,6 +38,10 @@
private final ViewGroup mHostView;
private Map<View, 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;
@@ -46,6 +54,10 @@
public StackScrollState(ViewGroup hostView) {
mHostView = hostView;
mStateMap = new HashMap<View, 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() {
@@ -83,10 +95,17 @@
*/
public void apply() {
int numChildren = mHostView.getChildCount();
+ float previousNotificationEnd = 0;
+ float previousNotificationStart = 0;
for (int i = 0; i < numChildren; i++) {
View child = mHostView.getChildAt(i);
ViewState state = mStateMap.get(child);
- if (state != null) {
+ if (state == null) {
+ Log.wtf(CHILD_NOT_FOUND_TAG, "No child state was found when applying this state " +
+ "to the hostView");
+ continue;
+ }
+ if (!state.gone) {
float alpha = child.getAlpha();
float yTranslation = child.getTranslationY();
float zTranslation = child.getTranslationZ();
@@ -117,7 +136,7 @@
// apply visibility
int oldVisibility = child.getVisibility();
int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE;
- if (newVisibility != oldVisibility && !state.gone) {
+ if (newVisibility != oldVisibility) {
child.setVisibility(newVisibility);
}
@@ -135,13 +154,94 @@
if (height != newHeight) {
applyNewHeight(child, newHeight);
}
- } else {
- Log.wtf(CHILD_NOT_FOUND_TAG, "No child state was found when applying this state " +
- "to the hostView");
+
+ // apply clipping and shadow
+ float newNotificationEnd = newYTranslation + newHeight;
+ updateChildClippingAndShadow(child, newHeight,
+ newNotificationEnd - (previousNotificationEnd - mChildDividerHeight),
+ newHeight - (previousNotificationStart - newYTranslation));
+
+ previousNotificationStart = newYTranslation;
+ previousNotificationEnd = newNotificationEnd;
}
}
}
+ /**
+ * Updates the shadow outline and the clipping for a view.
+ *
+ * @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
+ */
+ private void updateChildClippingAndShadow(View child, int realHeight, float clipHeight,
+ float shadowHeight) {
+ if (realHeight > shadowHeight) {
+ updateChildOutline(child, realHeight, shadowHeight);
+ } else {
+ updateChildOutline(child, realHeight, realHeight);
+ }
+ if (realHeight > clipHeight) {
+ updateChildClip(child, realHeight, clipHeight);
+ } else {
+ child.setClipBounds(null);
+ }
+ }
+
+ /**
+ * Updates the clipping of a view
+ *
+ * @param child the view to update
+ * @param height the currently applied height of the view
+ * @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);
+ }
+ }
+
+ /**
+ * 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;
diff --git a/packages/services/PacProcessor/Android.mk b/packages/services/PacProcessor/Android.mk
index d9566d5..79f8a1f 100644
--- a/packages/services/PacProcessor/Android.mk
+++ b/packages/services/PacProcessor/Android.mk
@@ -25,7 +25,9 @@
LOCAL_PACKAGE_NAME := PacProcessor
LOCAL_CERTIFICATE := platform
-LOCAL_REQUIRED_MODULES := libjni_pacprocessor
+LOCAL_JNI_SHARED_LIBRARIES := libjni_pacprocessor
+
+LOCAL_MULTILIB := 32
include $(BUILD_PACKAGE)
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 2cf94d0..0550dd4 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -3580,6 +3580,7 @@
SwipeDismissLayout layout, float progress, float translate) {
WindowManager.LayoutParams newParams = getAttributes();
newParams.x = (int) translate;
+ newParams.alpha = 1 - progress;
setAttributes(newParams);
int flags = 0;
@@ -3595,6 +3596,7 @@
public void onSwipeCancelled(SwipeDismissLayout layout) {
WindowManager.LayoutParams newParams = getAttributes();
newParams.x = 0;
+ newParams.alpha = 1;
setAttributes(newParams);
setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN | FLAG_LAYOUT_NO_LIMITS);
}
diff --git a/rs/java/android/renderscript/ScriptIntrinsicResize.java b/rs/java/android/renderscript/ScriptIntrinsicResize.java
new file mode 100644
index 0000000..fe56699
--- /dev/null
+++ b/rs/java/android/renderscript/ScriptIntrinsicResize.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 android.renderscript;
+
+/**
+ * Intrinsic for performing a resize of a 2D allocation.
+ */
+public final class ScriptIntrinsicResize extends ScriptIntrinsic {
+ private Allocation mInput;
+
+ private ScriptIntrinsicResize(long id, RenderScript rs) {
+ super(id, rs);
+ }
+
+ /**
+ * Supported elements types are {@link Element#U8}, {@link
+ * Element#U8_2}, {@link Element#U8_3}, {@link Element#U8_4}
+ *
+ * @param rs The RenderScript context
+ *
+ * @return ScriptIntrinsicResize
+ */
+ public static ScriptIntrinsicResize create(RenderScript rs) {
+ long id = rs.nScriptIntrinsicCreate(12, 0);
+ ScriptIntrinsicResize si = new ScriptIntrinsicResize(id, rs);
+ return si;
+
+ }
+
+ /**
+ * Set the input of the resize.
+ * Must match the element type supplied during create.
+ *
+ * @param ain The input allocation.
+ */
+ public void setInput(Allocation ain) {
+ Element e = ain.getElement();
+ if (!e.isCompatible(Element.U8(mRS)) &&
+ !e.isCompatible(Element.U8_2(mRS)) &&
+ !e.isCompatible(Element.U8_3(mRS)) &&
+ !e.isCompatible(Element.U8_4(mRS))) {
+ throw new RSIllegalArgumentException("Unsuported element type.");
+ }
+
+ mInput = ain;
+ setVar(0, ain);
+ }
+
+ /**
+ * Get a FieldID for the input field of this intrinsic.
+ *
+ * @return Script.FieldID The FieldID object.
+ */
+ public Script.FieldID getFieldID_Input() {
+ return createFieldID(0, null);
+ }
+
+
+ /**
+ * Resize copy the input allocation to the output specified. The
+ * Allocation is rescaled if necessary using bi-cubic
+ * interpolation.
+ *
+ * @param aout Output allocation. Element type must match
+ * current input. Must not be same as input.
+ */
+ public void forEach_bicubic(Allocation aout) {
+ if (aout == mInput) {
+ throw new RSIllegalArgumentException("Output cannot be same as Input.");
+ }
+ forEach_bicubic(aout, null);
+ }
+
+ /**
+ * Resize copy the input allocation to the output specified. The
+ * Allocation is rescaled if necessary using bi-cubic
+ * interpolation.
+ *
+ * @param aout Output allocation. Element type must match
+ * current input.
+ * @param opt LaunchOptions for clipping
+ */
+ public void forEach_bicubic(Allocation aout, Script.LaunchOptions opt) {
+ forEach(0, null, aout, null, opt);
+ }
+
+ /**
+ * Get a KernelID for this intrinsic kernel.
+ *
+ * @return Script.KernelID The KernelID object.
+ */
+ public Script.KernelID getKernelID_bicubic() {
+ return createKernelID(0, 2, null, null);
+ }
+
+
+}
+
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 671b43de..18a2e31 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -47,24 +47,29 @@
using namespace android;
-#define PER_ARRAY_TYPE(flag, fnc, ...) { \
+#define PER_ARRAY_TYPE(flag, fnc, readonly, ...) { \
jint len = 0; \
void *ptr = NULL; \
size_t typeBytes = 0; \
+ jint relFlag = 0; \
+ if (readonly) { \
+ /* The on-release mode should only be JNI_ABORT for read-only accesses. */ \
+ relFlag = JNI_ABORT; \
+ } \
switch(dataType) { \
case RS_TYPE_FLOAT_32: \
len = _env->GetArrayLength((jfloatArray)data); \
ptr = _env->GetFloatArrayElements((jfloatArray)data, flag); \
typeBytes = 4; \
fnc(__VA_ARGS__); \
- _env->ReleaseFloatArrayElements((jfloatArray)data, (jfloat *)ptr, JNI_ABORT); \
+ _env->ReleaseFloatArrayElements((jfloatArray)data, (jfloat *)ptr, relFlag); \
return; \
case RS_TYPE_FLOAT_64: \
len = _env->GetArrayLength((jdoubleArray)data); \
ptr = _env->GetDoubleArrayElements((jdoubleArray)data, flag); \
typeBytes = 8; \
fnc(__VA_ARGS__); \
- _env->ReleaseDoubleArrayElements((jdoubleArray)data, (jdouble *)ptr, JNI_ABORT);\
+ _env->ReleaseDoubleArrayElements((jdoubleArray)data, (jdouble *)ptr, relFlag); \
return; \
case RS_TYPE_SIGNED_8: \
case RS_TYPE_UNSIGNED_8: \
@@ -72,7 +77,7 @@
ptr = _env->GetByteArrayElements((jbyteArray)data, flag); \
typeBytes = 1; \
fnc(__VA_ARGS__); \
- _env->ReleaseByteArrayElements((jbyteArray)data, (jbyte*)ptr, JNI_ABORT); \
+ _env->ReleaseByteArrayElements((jbyteArray)data, (jbyte*)ptr, relFlag); \
return; \
case RS_TYPE_SIGNED_16: \
case RS_TYPE_UNSIGNED_16: \
@@ -80,7 +85,7 @@
ptr = _env->GetShortArrayElements((jshortArray)data, flag); \
typeBytes = 2; \
fnc(__VA_ARGS__); \
- _env->ReleaseShortArrayElements((jshortArray)data, (jshort *)ptr, JNI_ABORT); \
+ _env->ReleaseShortArrayElements((jshortArray)data, (jshort *)ptr, relFlag); \
return; \
case RS_TYPE_SIGNED_32: \
case RS_TYPE_UNSIGNED_32: \
@@ -88,7 +93,7 @@
ptr = _env->GetIntArrayElements((jintArray)data, flag); \
typeBytes = 4; \
fnc(__VA_ARGS__); \
- _env->ReleaseIntArrayElements((jintArray)data, (jint *)ptr, JNI_ABORT); \
+ _env->ReleaseIntArrayElements((jintArray)data, (jint *)ptr, relFlag); \
return; \
case RS_TYPE_SIGNED_64: \
case RS_TYPE_UNSIGNED_64: \
@@ -96,7 +101,7 @@
ptr = _env->GetLongArrayElements((jlongArray)data, flag); \
typeBytes = 8; \
fnc(__VA_ARGS__); \
- _env->ReleaseLongArrayElements((jlongArray)data, (jlong *)ptr, JNI_ABORT); \
+ _env->ReleaseLongArrayElements((jlongArray)data, (jlong *)ptr, relFlag); \
return; \
default: \
break; \
@@ -672,6 +677,7 @@
}
+// Copies from the Java object data into the Allocation pointed to by _alloc.
static void
nAllocationData1D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint offset, jint lod,
jint count, jobject data, jint sizeBytes, jint dataType)
@@ -679,9 +685,10 @@
RsAllocation *alloc = (RsAllocation *)_alloc;
LOG_API("nAllocation1DData, con(%p), adapter(%p), offset(%i), count(%i), sizeBytes(%i), dataType(%i)",
(RsContext)con, (RsAllocation)alloc, offset, count, sizeBytes, dataType);
- PER_ARRAY_TYPE(NULL, rsAllocation1DData, (RsContext)con, alloc, offset, lod, count, ptr, sizeBytes);
+ PER_ARRAY_TYPE(NULL, rsAllocation1DData, true, (RsContext)con, alloc, offset, lod, count, ptr, sizeBytes);
}
+// Copies from the Java array data into the Allocation pointed to by alloc.
static void
// native void rsnAllocationElementData1D(long con, long id, int xoff, int compIdx, byte[] d, int sizeBytes);
nAllocationElementData1D(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jint offset, jint lod, jint compIdx, jbyteArray data, jint sizeBytes)
@@ -693,6 +700,7 @@
_env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
}
+// Copies from the Java object data into the Allocation pointed to by _alloc.
static void
nAllocationData2D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xoff, jint yoff, jint lod, jint _face,
jint w, jint h, jobject data, jint sizeBytes, jint dataType)
@@ -701,9 +709,11 @@
RsAllocationCubemapFace face = (RsAllocationCubemapFace)_face;
LOG_API("nAllocation2DData, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i) type(%i)",
(RsContext)con, alloc, xoff, yoff, w, h, sizeBytes, dataType);
- PER_ARRAY_TYPE(NULL, rsAllocation2DData, (RsContext)con, alloc, xoff, yoff, lod, face, w, h, ptr, sizeBytes, 0);
+ PER_ARRAY_TYPE(NULL, rsAllocation2DData, true, (RsContext)con, alloc, xoff, yoff, lod, face, w, h, ptr, sizeBytes, 0);
}
+// Copies from the Allocation pointed to by srcAlloc into the Allocation
+// pointed to by dstAlloc.
static void
nAllocationData2D_alloc(JNIEnv *_env, jobject _this, jlong con,
jlong dstAlloc, jint dstXoff, jint dstYoff,
@@ -728,6 +738,7 @@
srcMip, srcFace);
}
+// Copies from the Java object data into the Allocation pointed to by _alloc.
static void
nAllocationData3D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xoff, jint yoff, jint zoff, jint lod,
jint w, jint h, jint d, jobject data, int sizeBytes, int dataType)
@@ -735,9 +746,11 @@
RsAllocation *alloc = (RsAllocation *)_alloc;
LOG_API("nAllocation3DData, con(%p), alloc(%p), xoff(%i), yoff(%i), zoff(%i), lod(%i), w(%i), h(%i), d(%i), sizeBytes(%i)",
(RsContext)con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, sizeBytes);
- PER_ARRAY_TYPE(NULL, rsAllocation3DData, (RsContext)con, alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
+ PER_ARRAY_TYPE(NULL, rsAllocation3DData, true, (RsContext)con, alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
}
+// Copies from the Allocation pointed to by srcAlloc into the Allocation
+// pointed to by dstAlloc.
static void
nAllocationData3D_alloc(JNIEnv *_env, jobject _this, jlong con,
jlong dstAlloc, jint dstXoff, jint dstYoff, jint dstZoff,
@@ -761,14 +774,16 @@
}
+// Copies from the Allocation pointed to by _alloc into the Java object data.
static void
nAllocationRead(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jobject data, int dataType)
{
RsAllocation *alloc = (RsAllocation *)_alloc;
LOG_API("nAllocationRead, con(%p), alloc(%p)", (RsContext)con, (RsAllocation)alloc);
- PER_ARRAY_TYPE(0, rsAllocationRead, (RsContext)con, alloc, ptr, len * typeBytes);
+ PER_ARRAY_TYPE(0, rsAllocationRead, false, (RsContext)con, alloc, ptr, len * typeBytes);
}
+// Copies from the Allocation pointed to by _alloc into the Java object data.
static void
nAllocationRead1D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint offset, jint lod,
jint count, jobject data, int sizeBytes, int dataType)
@@ -776,9 +791,10 @@
RsAllocation *alloc = (RsAllocation *)_alloc;
LOG_API("nAllocation1DRead, con(%p), adapter(%p), offset(%i), count(%i), sizeBytes(%i), dataType(%i)",
(RsContext)con, alloc, offset, count, sizeBytes, dataType);
- PER_ARRAY_TYPE(0, rsAllocation1DRead, (RsContext)con, alloc, offset, lod, count, ptr, sizeBytes);
+ PER_ARRAY_TYPE(0, rsAllocation1DRead, false, (RsContext)con, alloc, offset, lod, count, ptr, sizeBytes);
}
+// Copies from the Allocation pointed to by _alloc into the Java object data.
static void
nAllocationRead2D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xoff, jint yoff, jint lod, jint _face,
jint w, jint h, jobject data, int sizeBytes, int dataType)
@@ -787,7 +803,7 @@
RsAllocationCubemapFace face = (RsAllocationCubemapFace)_face;
LOG_API("nAllocation2DRead, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i) type(%i)",
(RsContext)con, alloc, xoff, yoff, w, h, sizeBytes, dataType);
- PER_ARRAY_TYPE(0, rsAllocation2DRead, (RsContext)con, alloc, xoff, yoff, lod, face, w, h, ptr, sizeBytes, 0);
+ PER_ARRAY_TYPE(0, rsAllocation2DRead, false, (RsContext)con, alloc, xoff, yoff, lod, face, w, h, ptr, sizeBytes, 0);
}
static jlong
@@ -1023,7 +1039,7 @@
jint len = _env->GetArrayLength(data);
jbyte *ptr = _env->GetByteArrayElements(data, NULL);
rsScriptGetVarV((RsContext)con, (RsScript)script, slot, ptr, len);
- _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
+ _env->ReleaseByteArrayElements(data, ptr, 0);
}
static void
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index bce85ce..7249985 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -106,22 +106,33 @@
.append("Kernel: ")
.append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"))
.append("\n").toString();
+ final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
String recovery = RecoverySystem.handleAftermath();
if (recovery != null && db != null) {
db.addText("SYSTEM_RECOVERY_LOG", headers + recovery);
}
+ String lastKmsgFooter = "";
+ if (bootReason != null) {
+ lastKmsgFooter = new StringBuilder(512)
+ .append("\n")
+ .append("Boot info:\n")
+ .append("Last boot reason: ").append(bootReason).append("\n")
+ .toString();
+ }
+
if (SystemProperties.getLong("ro.runtime.firstboot", 0) == 0) {
String now = Long.toString(System.currentTimeMillis());
SystemProperties.set("ro.runtime.firstboot", now);
if (db != null) db.addText("SYSTEM_BOOT", headers);
// Negative sizes mean to take the *tail* of the file (see FileUtils.readTextFile())
- addFileToDropBox(db, prefs, headers, "/proc/last_kmsg",
- -LOG_SIZE, "SYSTEM_LAST_KMSG");
- addFileToDropBox(db, prefs, headers, "/sys/fs/pstore/console-ramoops",
- -LOG_SIZE, "SYSTEM_LAST_KMSG");
+ addFileWithFootersToDropBox(db, prefs, headers, lastKmsgFooter,
+ "/proc/last_kmsg", -LOG_SIZE, "SYSTEM_LAST_KMSG");
+ addFileWithFootersToDropBox(db, prefs, headers, lastKmsgFooter,
+ "/sys/fs/pstore/console-ramoops", -LOG_SIZE,
+ "SYSTEM_LAST_KMSG");
addFileToDropBox(db, prefs, headers, "/cache/recovery/log",
-LOG_SIZE, "SYSTEM_RECOVERY_LOG");
addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_console",
@@ -161,6 +172,14 @@
private static void addFileToDropBox(
DropBoxManager db, SharedPreferences prefs,
String headers, String filename, int maxSize, String tag) throws IOException {
+ addFileWithFootersToDropBox(db, prefs, headers, "", filename, maxSize,
+ tag);
+ }
+
+ private static void addFileWithFootersToDropBox(
+ DropBoxManager db, SharedPreferences prefs,
+ String headers, String footers, String filename, int maxSize,
+ String tag) throws IOException {
if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled
File file = new File(filename);
@@ -176,7 +195,7 @@
}
Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")");
- db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n"));
+ db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n") + footers);
}
private static void addAuditErrorsToDropBox(DropBoxManager db, SharedPreferences prefs,
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index e6163bd..62deec2 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -450,6 +450,27 @@
if (provider == null) {
Slog.e(TAG, "no geofence provider found");
}
+
+ String[] testProviderStrings = resources.getStringArray(
+ com.android.internal.R.array.config_testLocationProviders);
+ for (String testProviderString : testProviderStrings) {
+ String fragments[] = testProviderString.split(",");
+ String name = fragments[0].trim();
+ if (mProvidersByName.get(name) != null) {
+ throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
+ }
+ ProviderProperties properties = new ProviderProperties(
+ Boolean.parseBoolean(fragments[1]) /* requiresNetwork */,
+ Boolean.parseBoolean(fragments[2]) /* requiresSatellite */,
+ Boolean.parseBoolean(fragments[3]) /* requiresCell */,
+ Boolean.parseBoolean(fragments[4]) /* hasMonetaryCost */,
+ Boolean.parseBoolean(fragments[5]) /* supportsAltitude */,
+ Boolean.parseBoolean(fragments[6]) /* supportsSpeed */,
+ Boolean.parseBoolean(fragments[7]) /* supportsBearing */,
+ Integer.parseInt(fragments[8]) /* powerRequirement */,
+ Integer.parseInt(fragments[9]) /* accuracy */);
+ addTestProviderLocked(name, properties);
+ }
}
/**
@@ -2204,7 +2225,6 @@
long identity = Binder.clearCallingIdentity();
synchronized (mLock) {
- MockProvider provider = new MockProvider(name, this, properties);
// remove the real provider if we are replacing GPS or network provider
if (LocationManager.GPS_PROVIDER.equals(name)
|| LocationManager.NETWORK_PROVIDER.equals(name)
@@ -2214,18 +2234,23 @@
removeProviderLocked(p);
}
}
- if (mProvidersByName.get(name) != null) {
- throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
- }
- addProviderLocked(provider);
- mMockProviders.put(name, provider);
- mLastLocation.put(name, null);
- mLastLocationCoarseInterval.put(name, null);
+ addTestProviderLocked(name, properties);
updateProvidersLocked();
}
Binder.restoreCallingIdentity(identity);
}
+ private void addTestProviderLocked(String name, ProviderProperties properties) {
+ if (mProvidersByName.get(name) != null) {
+ throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
+ }
+ MockProvider provider = new MockProvider(name, this, properties);
+ addProviderLocked(provider);
+ mMockProviders.put(name, provider);
+ mLastLocation.put(name, null);
+ mLastLocationCoarseInterval.put(name, null);
+ }
+
@Override
public void removeTestProvider(String provider) {
checkMockPermissionsSafe();
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 9629bd1..705862a 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -153,7 +153,7 @@
*/
private NativeDaemonConnector mConnector;
- private final Handler mMainHandler = new Handler();
+ private final Handler mFgHandler;
private IBatteryStats mBatteryStats;
@@ -203,6 +203,9 @@
private NetworkManagementService(Context context, String socket) {
mContext = context;
+ // make sure this is on the same looper as our NativeDaemonConnector for sync purposes
+ mFgHandler = new Handler(FgThread.get().getLooper());
+
if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
return;
}
@@ -271,14 +274,17 @@
*/
private void notifyInterfaceStatusChanged(String iface, boolean up) {
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
}
/**
@@ -287,14 +293,17 @@
*/
private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
}
/**
@@ -302,14 +311,17 @@
*/
private void notifyInterfaceAdded(String iface) {
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceAdded(iface);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).interfaceAdded(iface);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
}
/**
@@ -322,14 +334,17 @@
mActiveQuotas.remove(iface);
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceRemoved(iface);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).interfaceRemoved(iface);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
}
/**
@@ -337,14 +352,17 @@
*/
private void notifyLimitReached(String limitName, String iface) {
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).limitReached(limitName, iface);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).limitReached(limitName, iface);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
}
/**
@@ -357,15 +375,18 @@
}
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(
- Integer.toString(type), active, tsNanos);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(
+ Integer.toString(type), active, tsNanos);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
boolean report = false;
synchronized (mIdleTimerLock) {
@@ -456,14 +477,17 @@
*/
private void notifyAddressUpdated(String iface, LinkAddress address) {
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).addressUpdated(iface, address);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).addressUpdated(iface, address);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
}
/**
@@ -471,14 +495,17 @@
*/
private void notifyAddressRemoved(String iface, LinkAddress address) {
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).addressRemoved(iface, address);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).addressRemoved(iface, address);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
}
/**
@@ -486,14 +513,18 @@
*/
private void notifyInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) {
final int length = mObservers.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceDnsServerInfo(iface, lifetime, addresses);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).interfaceDnsServerInfo(iface, lifetime,
+ addresses);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mObservers.finishBroadcast();
}
- mObservers.finishBroadcast();
}
//
@@ -509,7 +540,7 @@
mConnectedSignal.countDown();
mConnectedSignal = null;
} else {
- mMainHandler.post(new Runnable() {
+ mFgHandler.post(new Runnable() {
@Override
public void run() {
prepareNativeDaemon();
@@ -1270,7 +1301,7 @@
if (ConnectivityManager.isNetworkTypeMobile(type)) {
mNetworkActive = false;
}
- mMainHandler.post(new Runnable() {
+ mFgHandler.post(new Runnable() {
@Override public void run() {
notifyInterfaceClassActivity(type, true, SystemClock.elapsedRealtimeNanos());
}
@@ -1297,7 +1328,7 @@
throw e.rethrowAsParcelableException();
}
mActiveIdleTimers.remove(iface);
- mMainHandler.post(new Runnable() {
+ mFgHandler.post(new Runnable() {
@Override public void run() {
notifyInterfaceClassActivity(params.type, false,
SystemClock.elapsedRealtimeNanos());
@@ -1880,14 +1911,17 @@
private void reportNetworkActive() {
final int length = mNetworkActivityListeners.beginBroadcast();
- for (int i = 0; i < length; i++) {
- try {
- mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive();
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive();
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
}
+ } finally {
+ mNetworkActivityListeners.finishBroadcast();
}
- mNetworkActivityListeners.finishBroadcast();
}
/** {@inheritDoc} */
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
new file mode 100644
index 0000000..8a30e50
--- /dev/null
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -0,0 +1,136 @@
+/*
+ * 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;
+
+import android.Manifest.permission;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.net.INetworkScoreService;
+import android.net.NetworkKey;
+import android.net.NetworkScorerAppManager;
+import android.net.RssiCurve;
+import android.net.ScoredNetwork;
+import android.text.TextUtils;
+
+import com.android.internal.R;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Backing service for {@link android.net.NetworkScoreManager}.
+ * @hide
+ */
+public class NetworkScoreService extends INetworkScoreService.Stub {
+ private static final String TAG = "NetworkScoreService";
+
+ /** SharedPreference bit set to true after the service is first initialized. */
+ private static final String PREF_SCORING_PROVISIONED = "is_provisioned";
+
+ private final Context mContext;
+
+ // TODO: Delete this temporary class once we have a real place for scores.
+ private final Map<NetworkKey, RssiCurve> mScoredNetworks;
+
+ public NetworkScoreService(Context context) {
+ mContext = context;
+ mScoredNetworks = new HashMap<>();
+ }
+
+ /** Called when the system is ready to run third-party code but before it actually does so. */
+ void systemReady() {
+ SharedPreferences prefs = mContext.getSharedPreferences(TAG, Context.MODE_PRIVATE);
+ if (!prefs.getBoolean(PREF_SCORING_PROVISIONED, false)) {
+ // On first run, we try to initialize the scorer to the one configured at build time.
+ // This will be a no-op if the scorer isn't actually valid.
+ String defaultPackage = mContext.getResources().getString(
+ R.string.config_defaultNetworkScorerPackageName);
+ if (!TextUtils.isEmpty(defaultPackage)) {
+ NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage);
+ }
+ prefs.edit().putBoolean(PREF_SCORING_PROVISIONED, true).apply();
+ }
+ }
+
+ @Override
+ public boolean updateScores(ScoredNetwork[] networks) {
+ if (!NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid())) {
+ throw new SecurityException("Caller with UID " + getCallingUid() +
+ " is not the active scorer.");
+ }
+
+ // TODO: Propagate these scores down to the network subsystem layer instead of just holding
+ // them in memory.
+ for (ScoredNetwork network : networks) {
+ mScoredNetworks.put(network.networkKey, network.rssiCurve);
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean clearScores() {
+ // Only the active scorer or the system (who can broadcast BROADCAST_SCORE_NETWORKS) should
+ // be allowed to flush all scores.
+ if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
+ mContext.checkCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS) ==
+ PackageManager.PERMISSION_GRANTED) {
+ clearInternal();
+ return true;
+ } else {
+ throw new SecurityException(
+ "Caller is neither the active scorer nor the scorer manager.");
+ }
+ }
+
+ @Override
+ public boolean setActiveScorer(String packageName) {
+ mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG);
+ // Preemptively clear scores even though the set operation could fail. We do this for safety
+ // as scores should never be compared across apps; in practice, Settings should only be
+ // allowing valid apps to be set as scorers, so failure here should be rare.
+ clearInternal();
+ return NetworkScorerAppManager.setActiveScorer(mContext, packageName);
+ }
+
+ /** Clear scores. Callers are responsible for checking permissions as appropriate. */
+ private void clearInternal() {
+ // TODO: Propagate the flush down to the network subsystem layer.
+ mScoredNetworks.clear();
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
+ String currentScorer = NetworkScorerAppManager.getActiveScorer(mContext);
+ if (currentScorer == null) {
+ writer.println("Scoring is disabled.");
+ return;
+ }
+ writer.println("Current scorer: " + currentScorer);
+ if (mScoredNetworks.isEmpty()) {
+ writer.println("No networks scored.");
+ } else {
+ for (Map.Entry<NetworkKey, RssiCurve> entry : mScoredNetworks.entrySet()) {
+ writer.println(entry.getKey() + ": " + entry.getValue());
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7607419..ecbb0d9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7240,6 +7240,9 @@
if (r == null) {
return null;
}
+ if (callback == null) {
+ throw new IllegalArgumentException("callback must not be null");
+ }
return mStackSupervisor.createActivityContainer(r, callback);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index d596472..60adfb0 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -139,7 +139,7 @@
boolean forceNewConfig; // force re-create with new config next time
int launchCount; // count of launches since last state
long lastLaunchTime; // time of last lauch of this activity
- ArrayList<ActivityStack> mChildContainers = new ArrayList<ActivityStack>();
+ ArrayList<ActivityContainer> mChildContainers = new ArrayList<ActivityContainer>();
String stringName; // for caching of toString().
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8dd4317..0acde09 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -36,6 +36,8 @@
import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES;
import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.CONTAINER_STATE_HAS_SURFACE;
+
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService.ItemMatcher;
@@ -1066,9 +1068,9 @@
private void setVisibile(ActivityRecord r, boolean visible) {
r.visible = visible;
mWindowManager.setAppVisibility(r.appToken, visible);
- final ArrayList<ActivityStack> containers = r.mChildContainers;
+ final ArrayList<ActivityContainer> containers = r.mChildContainers;
for (int containerNdx = containers.size() - 1; containerNdx >= 0; --containerNdx) {
- ActivityContainer container = containers.get(containerNdx).mActivityContainer;
+ ActivityContainer container = containers.get(containerNdx);
container.setVisible(visible);
}
}
@@ -1270,6 +1272,7 @@
* occurred and the activity will be notified immediately.
*/
void notifyActivityDrawnLocked(ActivityRecord r) {
+ mActivityContainer.setDrawn();
if ((r == null)
|| (mUndrawnActivitiesBelowTopTranslucent.remove(r) &&
mUndrawnActivitiesBelowTopTranslucent.isEmpty())) {
@@ -1336,7 +1339,8 @@
if (ActivityManagerService.DEBUG_LOCKSCREEN) mService.logLockScreen("");
ActivityRecord parent = mActivityContainer.mParentActivity;
- if (parent != null && parent.state != ActivityState.RESUMED) {
+ if ((parent != null && parent.state != ActivityState.RESUMED) ||
+ !mActivityContainer.isAttached()) {
// Do not resume this stack if its parent is not resumed.
// TODO: If in a loop, make sure that parent stack resumeTopActivity is called 1st.
return false;
@@ -2624,6 +2628,20 @@
return r;
}
+ void finishAllActivitiesLocked() {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (r.finishing) {
+ continue;
+ }
+ Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r);
+ finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
+ }
+ }
+ }
+
final boolean navigateUpToLocked(IBinder token, Intent destIntent, int resultCode,
Intent resultData) {
final ActivityRecord srec = ActivityRecord.forToken(token);
@@ -2862,7 +2880,6 @@
}
if (activityRemoved) {
mStackSupervisor.resumeTopActivitiesLocked();
-
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 63f9d09..111f010 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -96,7 +96,6 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -225,11 +224,11 @@
// TODO: Add listener for removal of references.
/** Mapping from (ActivityStack/TaskStack).mStackId to their current state */
- SparseArray<WeakReference<ActivityContainer>> mActivityContainers =
- new SparseArray<WeakReference<ActivityContainer>>();
+ SparseArray<ActivityContainer> mActivityContainers = new SparseArray<ActivityContainer>();
/** Mapping from displayId to display current state */
- private SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<ActivityDisplay>();
+ private final SparseArray<ActivityDisplay> mActivityDisplays =
+ new SparseArray<ActivityDisplay>();
InputManagerInternal mInputManagerInternal;
@@ -265,7 +264,7 @@
mActivityDisplays.put(displayId, activityDisplay);
}
- createStackOnDisplay(null, HOME_STACK_ID, Display.DEFAULT_DISPLAY);
+ createStackOnDisplay(HOME_STACK_ID, Display.DEFAULT_DISPLAY);
mHomeStack = mFocusedStack = mLastFocusedStack = getStack(HOME_STACK_ID);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
@@ -1386,7 +1385,7 @@
}
// Need to create an app stack for this user.
- int stackId = createStackOnDisplay(null, getNextStackId(), Display.DEFAULT_DISPLAY);
+ int stackId = createStackOnDisplay(getNextStackId(), Display.DEFAULT_DISPLAY);
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG, "adjustStackFocus: New stack r=" + r +
" stackId=" + stackId);
mFocusedStack = getStack(stackId);
@@ -2154,14 +2153,9 @@
}
ActivityStack getStack(int stackId) {
- WeakReference<ActivityContainer> weakReference = mActivityContainers.get(stackId);
- if (weakReference != null) {
- ActivityContainer activityContainer = weakReference.get();
- if (activityContainer != null) {
- return activityContainer.mStack;
- } else {
- mActivityContainers.remove(stackId);
- }
+ ActivityContainer activityContainer = mActivityContainers.get(stackId);
+ if (activityContainer != null) {
+ return activityContainer.mStack;
}
return null;
}
@@ -2191,49 +2185,26 @@
return null;
}
- ActivityContainer createActivityContainer(ActivityRecord parentActivity, int stackId,
+ ActivityContainer createActivityContainer(ActivityRecord parentActivity,
IActivityContainerCallback callback) {
- ActivityContainer activityContainer = new ActivityContainer(parentActivity, stackId,
- callback);
- mActivityContainers.put(stackId, new WeakReference<ActivityContainer>(activityContainer));
- if (parentActivity != null) {
- parentActivity.mChildContainers.add(activityContainer.mStack);
- }
+ ActivityContainer activityContainer = new VirtualActivityContainer(parentActivity, callback);
+ mActivityContainers.put(activityContainer.mStackId, activityContainer);
+ parentActivity.mChildContainers.add(activityContainer);
return activityContainer;
}
- ActivityContainer createActivityContainer(ActivityRecord parentActivity,
- IActivityContainerCallback callback) {
- return createActivityContainer(parentActivity, getNextStackId(), callback);
- }
-
void removeChildActivityContainers(ActivityRecord parentActivity) {
- for (int ndx = mActivityContainers.size() - 1; ndx >= 0; --ndx) {
- final ActivityContainer container = mActivityContainers.valueAt(ndx).get();
- if (container == null) {
- mActivityContainers.removeAt(ndx);
- continue;
- }
- if (container.mParentActivity != parentActivity) {
- continue;
- }
-
- ActivityStack stack = container.mStack;
- ActivityRecord top = stack.topRunningNonDelayedActivityLocked(null);
- if (top != null) {
- // TODO: Make sure the next activity doesn't start up when top is destroyed.
- stack.destroyActivityLocked(top, true, true, "stack parent destroyed");
- }
- mActivityContainers.removeAt(ndx);
- container.detachLocked();
+ final ArrayList<ActivityContainer> childStacks = parentActivity.mChildContainers;
+ for (int containerNdx = childStacks.size() - 1; containerNdx >= 0; --containerNdx) {
+ ActivityContainer container = childStacks.remove(containerNdx);
+ container.release();
}
}
void deleteActivityContainer(IActivityContainer container) {
ActivityContainer activityContainer = (ActivityContainer)container;
if (activityContainer != null) {
- activityContainer.mStack.destroyActivitiesLocked(null, true,
- "deleteActivityContainer");
+ activityContainer.mStack.finishAllActivitiesLocked();
final ActivityRecord parent = activityContainer.mParentActivity;
if (parent != null) {
parent.mChildContainers.remove(activityContainer);
@@ -2244,14 +2215,14 @@
}
}
- private int createStackOnDisplay(ActivityRecord parentActivity, int stackId, int displayId) {
+ private int createStackOnDisplay(int stackId, int displayId) {
ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
if (activityDisplay == null) {
return -1;
}
- ActivityContainer activityContainer =
- createActivityContainer(parentActivity, stackId, null);
+ ActivityContainer activityContainer = new ActivityContainer(stackId);
+ mActivityContainers.put(stackId, activityContainer);
activityContainer.attachToDisplayLocked(activityDisplay);
return stackId;
}
@@ -2334,9 +2305,9 @@
}
boolean shutdownLocked(int timeout) {
- boolean timedout = false;
goingToSleepLocked();
+ boolean timedout = false;
final long endTime = System.currentTimeMillis() + timeout;
while (true) {
boolean cantShutdown = false;
@@ -3030,24 +3001,26 @@
class ActivityContainer extends IActivityContainer.Stub {
final int mStackId;
- final IActivityContainerCallback mCallback;
+ IActivityContainerCallback mCallback = null;
final ActivityStack mStack;
- final ActivityRecord mParentActivity;
- final String mIdString;
+ ActivityRecord mParentActivity = null;
+ String mIdString;
boolean mVisible = true;
/** Display this ActivityStack is currently on. Null if not attached to a Display. */
ActivityDisplay mActivityDisplay;
- ActivityContainer(ActivityRecord parentActivity, int stackId,
- IActivityContainerCallback callback) {
+ final static int CONTAINER_STATE_HAS_SURFACE = 0;
+ final static int CONTAINER_STATE_NO_SURFACE = 1;
+ final static int CONTAINER_STATE_FINISHING = 2;
+ int mContainerState = CONTAINER_STATE_HAS_SURFACE;
+
+ ActivityContainer(int stackId) {
synchronized (mService) {
mStackId = stackId;
mStack = new ActivityStack(this);
- mParentActivity = parentActivity;
- mCallback = callback;
- mIdString = "ActivtyContainer{" + mStackId + ", parent=" + mParentActivity + "}";
+ mIdString = "ActivtyContainer{" + mStackId + "}";
if (DEBUG_STACK) Slog.d(TAG, "Creating " + this);
}
}
@@ -3097,6 +3070,14 @@
}
}
+ @Override
+ public void release() {
+ mContainerState = CONTAINER_STATE_FINISHING;
+ mStack.finishAllActivitiesLocked();
+ detachLocked();
+ mWindowManager.removeStack(mStackId);
+ }
+
private void detachLocked() {
if (DEBUG_STACK) Slog.d(TAG, "detachLocked: " + this + " from display="
+ mActivityDisplay + " Callers=" + Debug.getCallers(2));
@@ -3110,13 +3091,6 @@
}
@Override
- public void detachFromDisplay() {
- synchronized (mService) {
- detachLocked();
- }
- }
-
- @Override
public final int startActivity(Intent intent) {
mService.enforceNotIsolatedCaller("ActivityContainer.startActivity");
int userId = mService.handleIncomingUser(Binder.getCallingPid(),
@@ -3149,23 +3123,8 @@
}
@Override
- public void attachToSurface(Surface surface, int width, int height, int density) {
+ public void setSurface(Surface surface, int width, int height, int density) {
mService.enforceNotIsolatedCaller("ActivityContainer.attachToSurface");
-
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mService) {
- ActivityDisplay activityDisplay =
- new ActivityDisplay(surface, width, height, density);
- mActivityDisplays.put(activityDisplay.mDisplayId, activityDisplay);
- attachToDisplayLocked(activityDisplay);
- mStack.resumeTopActivityLocked(null);
- }
- if (DEBUG_STACK) Slog.d(TAG, "attachToSurface: " + this + " to display="
- + mActivityDisplay);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
}
ActivityStackSupervisor getOuter() {
@@ -3184,6 +3143,7 @@
}
}
+ // TODO: Make sure every change to ActivityRecord.visible results in a call to this.
void setVisible(boolean visible) {
if (mVisible != visible) {
mVisible = visible;
@@ -3194,52 +3154,114 @@
}
}
+ void setDrawn() {
+ }
+
@Override
public String toString() {
return mIdString + (mActivityDisplay == null ? "N" : "A");
}
}
+ private class VirtualActivityContainer extends ActivityContainer {
+ Surface mSurface;
+ boolean mDrawn = false;
+
+ VirtualActivityContainer(ActivityRecord parent, IActivityContainerCallback callback) {
+ super(getNextStackId());
+ mParentActivity = parent;
+ mCallback = callback;
+ mContainerState = CONTAINER_STATE_NO_SURFACE;
+ mIdString = "VirtualActivtyContainer{" + mStackId + ", parent=" + mParentActivity + "}";
+ }
+
+ @Override
+ public void setSurface(Surface surface, int width, int height, int density) {
+ super.setSurface(surface, width, height, density);
+
+ synchronized (mService) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ setSurfaceLocked(surface, width, height, density);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
+ private void setSurfaceLocked(Surface surface, int width, int height, int density) {
+ if (mContainerState == CONTAINER_STATE_FINISHING) {
+ return;
+ }
+ VirtualActivityDisplay virtualActivityDisplay =
+ (VirtualActivityDisplay) mActivityDisplay;
+ if (virtualActivityDisplay == null) {
+ virtualActivityDisplay =
+ new VirtualActivityDisplay(width, height, density);
+ mActivityDisplay = virtualActivityDisplay;
+ mActivityDisplays.put(virtualActivityDisplay.mDisplayId, virtualActivityDisplay);
+ attachToDisplayLocked(virtualActivityDisplay);
+ }
+
+ if (mSurface != null) {
+ mSurface.release();
+ }
+
+ mSurface = surface;
+ if (surface != null) {
+ mStack.resumeTopActivityLocked(null);
+ } else {
+ mContainerState = CONTAINER_STATE_NO_SURFACE;
+ ((VirtualActivityDisplay) mActivityDisplay).setSurface(null);
+// if (mStack.mPausingActivity == null && mStack.mResumedActivity != null) {
+// mStack.startPausingLocked(false, true);
+// }
+ }
+
+ setSurfaceIfReady();
+
+ if (DEBUG_STACK) Slog.d(TAG, "setSurface: " + this + " to display="
+ + virtualActivityDisplay);
+ }
+
+ @Override
+ void setDrawn() {
+ synchronized (mService) {
+ mDrawn = true;
+ setSurfaceIfReady();
+ }
+ }
+
+ private void setSurfaceIfReady() {
+ if (DEBUG_STACK) Slog.v(TAG, "setSurfaceIfReady: mDrawn=" + mDrawn +
+ " mContainerState=" + mContainerState + " mSurface=" + mSurface);
+ if (mDrawn && mSurface != null && mContainerState == CONTAINER_STATE_NO_SURFACE) {
+ ((VirtualActivityDisplay) mActivityDisplay).setSurface(mSurface);
+ mContainerState = CONTAINER_STATE_HAS_SURFACE;
+ }
+ }
+ }
+
/** Exactly one of these classes per Display in the system. Capable of holding zero or more
* attached {@link ActivityStack}s */
- final class ActivityDisplay {
+ class ActivityDisplay {
/** Actual Display this object tracks. */
int mDisplayId;
Display mDisplay;
DisplayInfo mDisplayInfo = new DisplayInfo();
- Surface mSurface;
/** All of the stacks on this display. Order matters, topmost stack is in front of all other
* stacks, bottommost behind. Accessed directly by ActivityManager package classes */
final ArrayList<ActivityStack> mStacks = new ArrayList<ActivityStack>();
- /** If this display is for an ActivityView then the VirtualDisplay created for it is stored
- * here. */
- VirtualDisplay mVirtualDisplay;
+ ActivityDisplay() {
+ }
ActivityDisplay(int displayId) {
init(mDisplayManager.getDisplay(displayId));
}
- ActivityDisplay(Surface surface, int width, int height, int density) {
- DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
- long ident = Binder.clearCallingIdentity();
- try {
- mVirtualDisplay = dm.createVirtualDisplay(mService.mContext,
- VIRTUAL_DISPLAY_BASE_NAME, width, height, density, surface,
- DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
- DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- init(mVirtualDisplay.getDisplay());
- mSurface = surface;
-
- mWindowManager.handleDisplayAdded(mDisplayId);
- }
-
- private void init(Display display) {
+ void init(Display display) {
mDisplay = display;
mDisplayId = display.getDisplayId();
mDisplay.getDisplayInfo(mDisplayInfo);
@@ -3255,11 +3277,6 @@
if (DEBUG_STACK) Slog.v(TAG, "detachActivitiesLocked: detaching " + stack
+ " from displayId=" + mDisplayId);
mStacks.remove(stack);
- if (mStacks.isEmpty() && mVirtualDisplay != null) {
- mVirtualDisplay.release();
- mVirtualDisplay = null;
- }
- mSurface.release();
}
void getBounds(Point bounds) {
@@ -3270,8 +3287,42 @@
@Override
public String toString() {
- return "ActivityDisplay={" + mDisplayId + (mVirtualDisplay == null ? "" : "V")
- + " numStacks=" + mStacks.size() + "}";
+ return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
+ }
+ }
+
+ class VirtualActivityDisplay extends ActivityDisplay {
+ VirtualDisplay mVirtualDisplay;
+
+ VirtualActivityDisplay(int width, int height, int density) {
+ DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
+ mVirtualDisplay = dm.createVirtualDisplay(mService.mContext, VIRTUAL_DISPLAY_BASE_NAME,
+ width, height, density, null, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
+
+ init(mVirtualDisplay.getDisplay());
+
+ mWindowManager.handleDisplayAdded(mDisplayId);
+ }
+
+ void setSurface(Surface surface) {
+ if (mVirtualDisplay != null) {
+ mVirtualDisplay.setSurface(surface);
+ }
+ }
+
+ @Override
+ void detachActivitiesLocked(ActivityStack stack) {
+ super.detachActivitiesLocked(stack);
+ if (mVirtualDisplay != null) {
+ mVirtualDisplay.release();
+ mVirtualDisplay = null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "VirtualActivityDisplay={" + mDisplayId + "}";
}
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 071417b..6697b60 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -517,6 +517,16 @@
return -1;
}
+ private void setVirtualDisplaySurfaceInternal(IBinder appToken, Surface surface) {
+ synchronized (mSyncRoot) {
+ if (mVirtualDisplayAdapter == null) {
+ return;
+ }
+
+ mVirtualDisplayAdapter.setVirtualDisplaySurfaceLocked(appToken, surface);
+ }
+ }
+
private void releaseVirtualDisplayInternal(IBinder appToken) {
synchronized (mSyncRoot) {
if (mVirtualDisplayAdapter == null) {
@@ -1221,9 +1231,6 @@
throw new IllegalArgumentException("width, height, and densityDpi must be "
+ "greater than 0");
}
- if (surface == null) {
- throw new IllegalArgumentException("surface must not be null");
- }
if (callingUid != Process.SYSTEM_UID &&
(flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) {
if (mContext.checkCallingPermission(android.Manifest.permission.CAPTURE_VIDEO_OUTPUT)
@@ -1255,6 +1262,16 @@
}
@Override // Binder call
+ public void setVirtualDisplaySurface(IBinder appToken, Surface surface) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ setVirtualDisplaySurfaceInternal(appToken, surface);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
public void releaseVirtualDisplay(IBinder appToken) {
final long token = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 95ca0d2..a165f26 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -69,6 +69,13 @@
return device;
}
+ public void setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface) {
+ VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
+ if (device != null) {
+ device.setSurfaceLocked(surface);
+ }
+ }
+
public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) {
VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
if (device != null) {
@@ -144,6 +151,17 @@
}
}
+ public void setSurfaceLocked(Surface surface) {
+ if (mSurface != surface) {
+ if ((mSurface != null) != (surface != null)) {
+ sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
+ }
+ sendTraversalRequestLocked();
+ mSurface = surface;
+ mInfo = null;
+ }
+ }
+
@Override
public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
if (mInfo == null) {
@@ -171,6 +189,7 @@
}
mInfo.type = Display.TYPE_VIRTUAL;
mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
+ mInfo.state = mSurface != null ? Display.STATE_ON : Display.STATE_OFF;
mInfo.ownerUid = mOwnerUid;
mInfo.ownerPackageName = mOwnerPackageName;
}
diff --git a/services/core/java/com/android/server/media/MediaRouteProviderProxy.java b/services/core/java/com/android/server/media/MediaRouteProviderProxy.java
new file mode 100644
index 0000000..d314ea7
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaRouteProviderProxy.java
@@ -0,0 +1,379 @@
+/*
+ * 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.media;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.media.routeprovider.IRouteConnection;
+import android.media.routeprovider.IRouteProvider;
+import android.media.routeprovider.IRouteProviderCallback;
+import android.media.routeprovider.RouteProviderService;
+import android.media.routeprovider.RouteRequest;
+import android.media.session.RouteEvent;
+import android.media.session.RouteInfo;
+import android.media.session.Session;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * System representation and interface to a MediaRouteProvider. This class is
+ * not thread safe so all calls should be made on the main thread.
+ */
+public class MediaRouteProviderProxy {
+ private static final String TAG = "MRPProxy";
+ private static final boolean DEBUG = true;
+
+ private static final int MAX_RETRIES = 3;
+
+ private final Object mLock = new Object();
+ private final Context mContext;
+ private final String mId;
+ private final ComponentName mComponentName;
+ private final int mUserId;
+
+ private Intent mBindIntent;
+ // Interfaces declared in the manifest
+ private ArrayList<String> mInterfaces;
+ private ArrayList<RouteConnectionRecord> mConnections = new ArrayList<RouteConnectionRecord>();
+ private Handler mHandler = new Handler();
+
+ private IRouteProvider mBinder;
+ private boolean mRunning;
+ private boolean mInterested;
+ private boolean mBound;
+ private int mRetryCount;
+
+ private RoutesListener mRouteListener;
+
+ public MediaRouteProviderProxy(Context context, String id, ComponentName component, int uid,
+ ArrayList<String> interfaces) {
+ mContext = context;
+ mId = id;
+ mComponentName = component;
+ mUserId = uid;
+ mInterfaces = interfaces;
+ mBindIntent = new Intent(RouteProviderService.SERVICE_INTERFACE);
+ mBindIntent.setComponent(mComponentName);
+ }
+
+ /**
+ * Send any cleanup messages and unbind from the media route provider
+ */
+ public void stop() {
+ if (mRunning) {
+ mRunning = false;
+ mRetryCount = 0;
+ updateBinding();
+ }
+ }
+
+ /**
+ * Bind to the media route provider and perform any setup needed
+ */
+ public void start() {
+ if (!mRunning) {
+ mRunning = true;
+ updateBinding();
+ }
+ }
+
+ /**
+ * Set whether or not this provider is currently interesting to the system.
+ * In the future this may take a list of interfaces instead.
+ *
+ * @param interested True if we want to connect to this provider
+ */
+ public void setInterested(boolean interested) {
+ mInterested = interested;
+ updateBinding();
+ }
+
+ /**
+ * Set a listener to get route updates on.
+ *
+ * @param listener The listener to receive updates on.
+ */
+ public void setRoutesListener(RoutesListener listener) {
+ mRouteListener = listener;
+ }
+
+ /**
+ * Send a request to the Provider to get all the routes that the session can
+ * use.
+ *
+ * @param record The session to get routes for.
+ * @param requestId An id to identify this request.
+ */
+ public void getRoutes(MediaSessionRecord record, final int requestId) {
+ // TODO change routes to have a system global id and maintain a mapping
+ // to the original route
+ if (mBinder == null) {
+ Log.wtf(TAG, "Attempted to call getRoutes without a binder connection");
+ return;
+ }
+ List<RouteRequest> requests = record.getRouteRequests();
+ final String sessionId = record.getSessionInfo().getId();
+ try {
+ mBinder.getAvailableRoutes(requests, new ResultReceiver(mHandler) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode != RouteProviderService.RESULT_SUCCESS) {
+ // ignore failures, just means no routes were generated
+ return;
+ }
+ ArrayList<RouteInfo> routes
+ = resultData.getParcelableArrayList(RouteProviderService.KEY_ROUTES);
+ ArrayList<RouteInfo> sysRoutes = new ArrayList<RouteInfo>();
+ for (int i = 0; i < routes.size(); i++) {
+ RouteInfo route = routes.get(i);
+ RouteInfo.Builder bob = new RouteInfo.Builder(route);
+ bob.setProviderId(mId);
+ sysRoutes.add(bob.build());
+ }
+ if (mRouteListener != null) {
+ mRouteListener.onRoutesUpdated(sessionId, sysRoutes, requestId);
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error in getRoutes", e);
+ }
+ }
+
+ /**
+ * Try connecting again if we've been disconnected.
+ */
+ public void rebindIfDisconnected() {
+ if (mBinder == null && shouldBind()) {
+ unbind();
+ bind();
+ }
+ }
+
+ /**
+ * Send a request to connect to a route.
+ *
+ * @param session The session that is trying to connect.
+ * @param route The route it is connecting to.
+ * @param request The request with the connection parameters.
+ * @return true if the request was sent, false otherwise.
+ */
+ public boolean connectToRoute(MediaSessionRecord session, final RouteInfo route,
+ final RouteRequest request) {
+ final String sessionId = session.getSessionInfo().getId();
+ try {
+ mBinder.connect(route, request, new ResultReceiver(mHandler) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode != RouteProviderService.RESULT_SUCCESS) {
+ // TODO handle connection failure
+ return;
+ }
+ IBinder binder = resultData.getBinder(RouteProviderService.KEY_CONNECTION);
+ IRouteConnection connection = null;
+ if (binder != null) {
+ connection = IRouteConnection.Stub.asInterface(binder);
+ }
+
+ if (connection != null) {
+ RouteConnectionRecord record = new RouteConnectionRecord(
+ connection);
+ mConnections.add(record);
+ if (mRouteListener != null) {
+ mRouteListener.onRouteConnected(sessionId, route, request, record);
+ }
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error connecting to route.", e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Check if this is the provider you're looking for.
+ */
+ public boolean hasComponentName(String packageName, String className) {
+ return mComponentName.getPackageName().equals(packageName)
+ && mComponentName.getClassName().equals(className);
+ }
+
+ /**
+ * Get the unique id for this provider.
+ *
+ * @return The provider's id.
+ */
+ public String getId() {
+ return mId;
+ }
+
+ private void updateBinding() {
+ if (shouldBind()) {
+ bind();
+ } else {
+ unbind();
+ }
+ }
+
+ private boolean shouldBind() {
+ return mRunning && mInterested;
+ }
+
+ private void bind() {
+ if (!mBound) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Binding");
+ }
+
+ try {
+ mBound = mContext.bindServiceAsUser(mBindIntent, mServiceConn,
+ Context.BIND_AUTO_CREATE, new UserHandle(mUserId));
+ if (!mBound && DEBUG) {
+ Slog.d(TAG, this + ": Bind failed");
+ }
+ } catch (SecurityException ex) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Bind failed", ex);
+ }
+ }
+ }
+ }
+
+ private void unbind() {
+ if (mBound) {
+ if (DEBUG) {
+ Slog.d(TAG, this + ": Unbinding");
+ }
+
+ mBound = false;
+ mContext.unbindService(mServiceConn);
+ }
+ }
+
+ private RouteConnectionRecord getConnectionLocked(IBinder binder) {
+ for (int i = mConnections.size() - 1; i >= 0; i--) {
+ RouteConnectionRecord record = mConnections.get(i);
+ if (record.isConnection(binder)) {
+ return record;
+ }
+ }
+ return null;
+ }
+
+ private ServiceConnection mServiceConn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mBinder = IRouteProvider.Stub.asInterface(service);
+ if (DEBUG) {
+ Slog.d(TAG, "Connected to route provider");
+ }
+ try {
+ mBinder.registerCallback(mCbStub);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error registering callback on route provider. Retry count: "
+ + mRetryCount, e);
+ if (mRetryCount < MAX_RETRIES) {
+ mRetryCount++;
+ rebindIfDisconnected();
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mBinder = null;
+ if (DEBUG) {
+ Slog.d(TAG, "Disconnected from route provider");
+ }
+ }
+
+ };
+
+ private IRouteProviderCallback.Stub mCbStub = new IRouteProviderCallback.Stub() {
+ @Override
+ public void onConnectionStateChanged(IRouteConnection connection, int state)
+ throws RemoteException {
+ // TODO
+ }
+
+ @Override
+ public void onRouteEvent(RouteEvent event) throws RemoteException {
+ synchronized (mLock) {
+ RouteConnectionRecord record = getConnectionLocked(event.getConnection());
+ Log.d(TAG, "Received route event for record " + record);
+ if (record != null) {
+ record.sendEvent(event);
+ }
+ }
+ }
+
+ @Override
+ public void onConnectionTerminated(IRouteConnection connection) throws RemoteException {
+ synchronized (mLock) {
+ RouteConnectionRecord record = getConnectionLocked(connection.asBinder());
+ if (record != null) {
+ record.disconnect();
+ mConnections.remove(record);
+ }
+ }
+ }
+
+ @Override
+ public void onRoutesChanged() throws RemoteException {
+ // TODO
+ }
+ };
+
+ /**
+ * Listener for receiving responses to route requests on the provider.
+ */
+ public interface RoutesListener {
+ /**
+ * Called when routes have been returned from a request to getRoutes.
+ *
+ * @param record The session that the routes were requested for.
+ * @param routes The matching routes returned by the provider.
+ * @param reqId The request id this is responding to.
+ */
+ public void onRoutesUpdated(String sessionId, ArrayList<RouteInfo> routes,
+ int reqId);
+
+ /**
+ * Called when a route has successfully connected.
+ *
+ * @param session The session that was connected.
+ * @param route The route it connected to.
+ * @param options The options that were used for the connection.
+ * @param connection The connection instance that was created.
+ */
+ public void onRouteConnected(String sessionId, RouteInfo route,
+ RouteRequest options, RouteConnectionRecord connection);
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaRouteProviderWatcher.java b/services/core/java/com/android/server/media/MediaRouteProviderWatcher.java
new file mode 100644
index 0000000..cf1d95ab
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaRouteProviderWatcher.java
@@ -0,0 +1,229 @@
+/*
+ * 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.media;
+
+import android.Manifest;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.media.routeprovider.RouteProviderService;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.UUID;
+
+/**
+ * Watches for media route provider services to be installed. Adds a provider to
+ * the media session service for each registered service. For now just run all
+ * providers. In the future define a policy for when to run providers.
+ */
+public class MediaRouteProviderWatcher {
+ private static final String TAG = "MRPWatcher";
+ private static final boolean DEBUG = true; // Log.isLoggable(TAG,
+ // Log.DEBUG);
+
+ private final Context mContext;
+ private final Callback mCallback;
+ private final Handler mHandler;
+ private final int mUserId;
+ private final PackageManager mPackageManager;
+
+ private final ArrayList<MediaRouteProviderProxy> mProviders =
+ new ArrayList<MediaRouteProviderProxy>();
+ private boolean mRunning;
+
+ public MediaRouteProviderWatcher(Context context, Callback callback, Handler handler,
+ int userId) {
+ mContext = context;
+ mCallback = callback;
+ mHandler = handler;
+ mUserId = userId;
+ mPackageManager = context.getPackageManager();
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + " mUserId=" + mUserId);
+ pw.println(prefix + " mRunning=" + mRunning);
+ pw.println(prefix + " mProviders.size()=" + mProviders.size());
+ }
+
+ public void start() {
+ if (!mRunning) {
+ mRunning = true;
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ filter.addDataScheme("package");
+ mContext.registerReceiverAsUser(mScanPackagesReceiver,
+ new UserHandle(mUserId), filter, null, mHandler);
+
+ // Scan packages.
+ // Also has the side-effect of restarting providers if needed.
+ mHandler.post(mScanPackagesRunnable);
+ }
+ }
+
+ public void stop() {
+ if (mRunning) {
+ mRunning = false;
+
+ mContext.unregisterReceiver(mScanPackagesReceiver);
+ mHandler.removeCallbacks(mScanPackagesRunnable);
+
+ // Stop all providers.
+ for (int i = mProviders.size() - 1; i >= 0; i--) {
+ mProviders.get(i).stop();
+ }
+ }
+ }
+
+ public ArrayList<MediaRouteProviderProxy> getProviders() {
+ return mProviders;
+ }
+
+ public MediaRouteProviderProxy getProvider(String id) {
+ int providerIndex = findProvider(id);
+ if (providerIndex != -1) {
+ return mProviders.get(providerIndex);
+ }
+ return null;
+ }
+
+ private void scanPackages() {
+ if (!mRunning) {
+ return;
+ }
+
+ // Add providers for all new services.
+ // Reorder the list so that providers left at the end will be the ones
+ // to remove.
+ int targetIndex = 0;
+ Intent intent = new Intent(RouteProviderService.SERVICE_INTERFACE);
+ for (ResolveInfo resolveInfo : mPackageManager.queryIntentServicesAsUser(
+ intent, 0, mUserId)) {
+ ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ if (DEBUG) {
+ Slog.d(TAG, "Checking service " + (serviceInfo == null ? null : serviceInfo.name));
+ }
+ if (serviceInfo != null && verifyServiceTrusted(serviceInfo)) {
+ int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
+ if (sourceIndex < 0) {
+ // TODO get declared interfaces from manifest
+ if (DEBUG) {
+ Slog.d(TAG, "Creating new provider proxy for service");
+ }
+ MediaRouteProviderProxy provider =
+ new MediaRouteProviderProxy(mContext, UUID.randomUUID().toString(),
+ new ComponentName(serviceInfo.packageName, serviceInfo.name),
+ mUserId, null);
+ provider.start();
+ mProviders.add(targetIndex++, provider);
+ mCallback.addProvider(provider);
+ } else if (sourceIndex >= targetIndex) {
+ MediaRouteProviderProxy provider = mProviders.get(sourceIndex);
+ provider.start(); // restart the provider if needed
+ provider.rebindIfDisconnected();
+ Collections.swap(mProviders, sourceIndex, targetIndex++);
+ }
+ }
+ }
+
+ // Remove providers for missing services.
+ if (targetIndex < mProviders.size()) {
+ for (int i = mProviders.size() - 1; i >= targetIndex; i--) {
+ MediaRouteProviderProxy provider = mProviders.get(i);
+ mCallback.removeProvider(provider);
+ mProviders.remove(provider);
+ provider.stop();
+ }
+ }
+ }
+
+ private boolean verifyServiceTrusted(ServiceInfo serviceInfo) {
+ if (serviceInfo.permission == null || !serviceInfo.permission.equals(
+ Manifest.permission.BIND_ROUTE_PROVIDER)) {
+ // If the service does not require this permission then any app
+ // could potentially bind to it and mess with their routes. So we
+ // only want to trust providers that require the
+ // correct permissions.
+ Slog.w(TAG, "Ignoring route provider service because it did not "
+ + "require the BIND_ROUTE_PROVIDER permission in its manifest: "
+ + serviceInfo.packageName + "/" + serviceInfo.name);
+ return false;
+ }
+ // Looks good.
+ return true;
+ }
+
+ private int findProvider(String id) {
+ int count = mProviders.size();
+ for (int i = 0; i < count; i++) {
+ MediaRouteProviderProxy provider = mProviders.get(i);
+ if (TextUtils.equals(id, provider.getId())) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private int findProvider(String packageName, String className) {
+ int count = mProviders.size();
+ for (int i = 0; i < count; i++) {
+ MediaRouteProviderProxy provider = mProviders.get(i);
+ if (provider.hasComponentName(packageName, className)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private final BroadcastReceiver mScanPackagesReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received package manager broadcast: " + intent);
+ }
+ scanPackages();
+ }
+ };
+
+ private final Runnable mScanPackagesRunnable = new Runnable() {
+ @Override
+ public void run() {
+ scanPackages();
+ }
+ };
+
+ public interface Callback {
+ void addProvider(MediaRouteProviderProxy provider);
+
+ void removeProvider(MediaRouteProviderProxy provider);
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 1ff925c..ac7f4f3 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -17,11 +17,20 @@
package com.android.server.media;
import android.content.Intent;
-import android.media.session.IMediaController;
-import android.media.session.IMediaControllerCallback;
-import android.media.session.IMediaSession;
-import android.media.session.IMediaSessionCallback;
+import android.media.routeprovider.RouteRequest;
+import android.media.session.ISessionController;
+import android.media.session.ISessionControllerCallback;
+import android.media.session.ISession;
+import android.media.session.ISessionCallback;
+import android.media.session.SessionController;
import android.media.session.MediaMetadata;
+import android.media.session.RouteCommand;
+import android.media.session.RouteInfo;
+import android.media.session.RouteOptions;
+import android.media.session.RouteEvent;
+import android.media.session.Session;
+import android.media.session.SessionInfo;
+import android.media.session.RouteInterface;
import android.media.session.PlaybackState;
import android.media.Rating;
import android.os.Bundle;
@@ -31,37 +40,44 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.view.KeyEvent;
import java.util.ArrayList;
import java.util.List;
+import java.util.UUID;
/**
* This is the system implementation of a Session. Apps will interact with the
* MediaSession wrapper class instead.
*/
public class MediaSessionRecord implements IBinder.DeathRecipient {
- private static final String TAG = "MediaSessionImpl";
+ private static final String TAG = "MediaSessionRecord";
private final MessageHandler mHandler;
private final int mPid;
- private final String mPackageName;
+ private final SessionInfo mSessionInfo;
private final String mTag;
private final ControllerStub mController;
private final SessionStub mSession;
private final SessionCb mSessionCb;
private final MediaSessionService mService;
- private final Object mControllerLock = new Object();
- private final ArrayList<IMediaControllerCallback> mControllerCallbacks =
- new ArrayList<IMediaControllerCallback>();
- private final ArrayList<String> mInterfaces = new ArrayList<String>();
+ private final Object mLock = new Object();
+ private final ArrayList<ISessionControllerCallback> mControllerCallbacks =
+ new ArrayList<ISessionControllerCallback>();
+ private final ArrayList<RouteRequest> mRequests = new ArrayList<RouteRequest>();
private boolean mTransportPerformerEnabled = false;
- private Bundle mRoute;
+ private RouteInfo mRoute;
+ private RouteOptions mRequest;
+ private RouteConnectionRecord mConnection;
+ // TODO define a RouteState class with relevant info
+ private int mRouteState;
// TransportPerformer fields
@@ -72,10 +88,10 @@
private boolean mIsPublished = false;
- public MediaSessionRecord(int pid, String packageName, IMediaSessionCallback cb, String tag,
+ public MediaSessionRecord(int pid, String packageName, ISessionCallback cb, String tag,
MediaSessionService service, Handler handler) {
mPid = pid;
- mPackageName = packageName;
+ mSessionInfo = new SessionInfo(UUID.randomUUID().toString(), packageName);
mTag = tag;
mController = new ControllerStub();
mSession = new SessionStub();
@@ -84,31 +100,140 @@
mHandler = new MessageHandler(handler.getLooper());
}
- public IMediaSession getSessionBinder() {
+ /**
+ * Get the binder for the {@link Session}.
+ *
+ * @return The session binder apps talk to.
+ */
+ public ISession getSessionBinder() {
return mSession;
}
- public IMediaController getControllerBinder() {
+ /**
+ * Get the binder for the {@link SessionController}.
+ *
+ * @return The controller binder apps talk to.
+ */
+ public ISessionController getControllerBinder() {
return mController;
}
+ /**
+ * Get the set of route requests this session is interested in.
+ *
+ * @return The list of RouteRequests
+ */
+ public List<RouteRequest> getRouteRequests() {
+ return mRequests;
+ }
+
+ /**
+ * Get the route this session is currently on.
+ *
+ * @return The route the session is on.
+ */
+ public RouteInfo getRoute() {
+ return mRoute;
+ }
+
+ /**
+ * Get the info for this session.
+ *
+ * @return Info that identifies this session.
+ */
+ public SessionInfo getSessionInfo() {
+ return mSessionInfo;
+ }
+
+ /**
+ * Set the selected route. This does not connect to the route, just notifies
+ * the app that a new route has been selected.
+ *
+ * @param route The route that was selected.
+ */
+ public void selectRoute(RouteInfo route) {
+ synchronized (mLock) {
+ if (route != mRoute) {
+ if (mConnection != null) {
+ mConnection.disconnect();
+ mConnection = null;
+ }
+ }
+ mRoute = route;
+ }
+ mSessionCb.sendRouteChange(route);
+ }
+
+ /**
+ * Update the state of the route this session is using and notify the
+ * session.
+ *
+ * @param state The new state of the route.
+ */
+ public void setRouteState(int state) {
+ mSessionCb.sendRouteStateChange(state);
+ }
+
+ /**
+ * Send an event to this session from the route it is using.
+ *
+ * @param event The event to send.
+ */
+ public void sendRouteEvent(RouteEvent event) {
+ mSessionCb.sendRouteEvent(event);
+ }
+
+ /**
+ * Set the connection to use for the selected route and notify the app it is
+ * now connected.
+ *
+ * @param route The route the connection is to.
+ * @param request The request that was used to connect.
+ * @param connection The connection to the route.
+ * @return True if this connection is still valid, false if it is stale.
+ */
+ public boolean setRouteConnected(RouteInfo route, RouteOptions request,
+ RouteConnectionRecord connection) {
+ synchronized (mLock) {
+ if (mRoute == null || !TextUtils.equals(route.getId(), mRoute.getId())) {
+ Log.w(TAG, "setRouteConnected: connected route is stale");
+ // TODO figure out disconnection path
+ return false;
+ }
+ if (request != mRequest) {
+ Log.w(TAG, "setRouteConnected: connection request is stale");
+ // TODO figure out disconnection path
+ return false;
+ }
+ mConnection = connection;
+ mConnection.setListener(mConnectionListener);
+ mSessionCb.sendRouteConnected();
+ }
+ return true;
+ }
+
+ /**
+ * Check if this session has been published by the app yet.
+ *
+ * @return True if it has been published, false otherwise.
+ */
+ public boolean isPublished() {
+ return mIsPublished;
+ }
+
@Override
public void binderDied() {
mService.sessionDied(this);
}
- public boolean isPublished() {
- return mIsPublished;
- }
-
private void onDestroy() {
mService.destroySession(this);
}
private void pushPlaybackStateUpdate() {
- synchronized (mControllerLock) {
+ synchronized (mLock) {
for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
- IMediaControllerCallback cb = mControllerCallbacks.get(i);
+ ISessionControllerCallback cb = mControllerCallbacks.get(i);
try {
cb.onPlaybackStateChanged(mPlaybackState);
} catch (RemoteException e) {
@@ -120,9 +245,9 @@
}
private void pushMetadataUpdate() {
- synchronized (mControllerLock) {
+ synchronized (mLock) {
for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
- IMediaControllerCallback cb = mControllerCallbacks.get(i);
+ ISessionControllerCallback cb = mControllerCallbacks.get(i);
try {
cb.onMetadataChanged(mMetadata);
} catch (RemoteException e) {
@@ -134,9 +259,9 @@
}
private void pushRouteUpdate() {
- synchronized (mControllerLock) {
+ synchronized (mLock) {
for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
- IMediaControllerCallback cb = mControllerCallbacks.get(i);
+ ISessionControllerCallback cb = mControllerCallbacks.get(i);
try {
cb.onRouteChanged(mRoute);
} catch (RemoteException e) {
@@ -148,21 +273,50 @@
}
private void pushEvent(String event, Bundle data) {
- synchronized (mControllerLock) {
+ synchronized (mLock) {
for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
- IMediaControllerCallback cb = mControllerCallbacks.get(i);
+ ISessionControllerCallback cb = mControllerCallbacks.get(i);
try {
cb.onEvent(event, data);
} catch (RemoteException e) {
- Log.w(TAG, "Removing dead callback in pushRouteUpdate.", e);
- mControllerCallbacks.remove(i);
+ Log.w(TAG, "Error with callback in pushEvent.", e);
}
}
}
}
- private final class SessionStub extends IMediaSession.Stub {
+ private void pushRouteCommand(RouteCommand command, ResultReceiver cb) {
+ synchronized (mLock) {
+ if (mRoute == null || !TextUtils.equals(command.getRouteInfo(), mRoute.getId())) {
+ if (cb != null) {
+ cb.send(RouteInterface.RESULT_ROUTE_IS_STALE, null);
+ return;
+ }
+ }
+ if (mConnection != null) {
+ mConnection.sendCommand(command, cb);
+ } else if (cb != null) {
+ cb.send(RouteInterface.RESULT_NOT_CONNECTED, null);
+ }
+ }
+ }
+ private final RouteConnectionRecord.Listener mConnectionListener
+ = new RouteConnectionRecord.Listener() {
+ @Override
+ public void onEvent(RouteEvent event) {
+ RouteEvent eventForSession = new RouteEvent(null, event.getIface(),
+ event.getEvent(), event.getExtras());
+ mSessionCb.sendRouteEvent(eventForSession);
+ }
+
+ @Override
+ public void disconnect() {
+ // TODO
+ }
+ };
+
+ private final class SessionStub extends ISession.Stub {
@Override
public void destroy() {
onDestroy();
@@ -174,21 +328,11 @@
}
@Override
- public IMediaController getMediaController() {
+ public ISessionController getController() {
return mController;
}
@Override
- public void setRouteState(Bundle routeState) {
- }
-
- @Override
- public void setRoute(Bundle mediaRouteDescriptor) {
- mRoute = mediaRouteDescriptor;
- mHandler.post(MessageHandler.MSG_UPDATE_ROUTE);
- }
-
- @Override
public void publish() {
mIsPublished = true; // TODO push update to service
}
@@ -198,11 +342,6 @@
}
@Override
- public List<String> getSupportedInterfaces() {
- return mInterfaces;
- }
-
- @Override
public void setMetadata(MediaMetadata metadata) {
mMetadata = metadata;
mHandler.post(MessageHandler.MSG_UPDATE_METADATA);
@@ -218,12 +357,44 @@
public void setRatingType(int type) {
mRatingType = type;
}
+
+ @Override
+ public void sendRouteCommand(RouteCommand command, ResultReceiver cb) {
+ mHandler.post(MessageHandler.MSG_SEND_COMMAND,
+ new Pair<RouteCommand, ResultReceiver>(command, cb));
+ }
+
+ @Override
+ public boolean setRoute(RouteInfo route) throws RemoteException {
+ // TODO decide if allowed to set route and if the route exists
+ return false;
+ }
+
+ @Override
+ public void connectToRoute(RouteInfo route, RouteOptions request)
+ throws RemoteException {
+ if (mRoute == null || !TextUtils.equals(route.getId(), mRoute.getId())) {
+ throw new RemoteException("RouteInfo does not match current route");
+ }
+ mService.connectToRoute(MediaSessionRecord.this, route, request);
+ mRequest = request;
+ }
+
+ @Override
+ public void setRouteOptions(List<RouteOptions> options) throws RemoteException {
+ mRequests.clear();
+ for (int i = options.size() - 1; i >= 0; i--) {
+ RouteRequest request = new RouteRequest(mSessionInfo, options.get(i),
+ false);
+ mRequests.add(request);
+ }
+ }
}
class SessionCb {
- private final IMediaSessionCallback mCb;
+ private final ISessionCallback mCb;
- public SessionCb(IMediaSessionCallback cb) {
+ public SessionCb(ISessionCallback cb) {
mCb = cb;
}
@@ -245,6 +416,38 @@
}
}
+ public void sendRouteChange(RouteInfo route) {
+ try {
+ mCb.onRequestRouteChange(route);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in sendRouteChange.", e);
+ }
+ }
+
+ public void sendRouteStateChange(int state) {
+ try {
+ mCb.onRouteStateChange(state);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in sendRouteStateChange.", e);
+ }
+ }
+
+ public void sendRouteEvent(RouteEvent event) {
+ try {
+ mCb.onRouteEvent(event);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in sendRouteEvent.", e);
+ }
+ }
+
+ public void sendRouteConnected() {
+ try {
+ mCb.onRouteConnected(mRoute, mRequest);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in sendRouteStateChange.", e);
+ }
+ }
+
public void play() {
try {
mCb.onPlay();
@@ -318,7 +521,7 @@
}
}
- class ControllerStub extends IMediaController.Stub {
+ class ControllerStub extends ISessionController.Stub {
@Override
public void sendCommand(String command, Bundle extras, ResultReceiver cb)
throws RemoteException {
@@ -331,8 +534,8 @@
}
@Override
- public void registerCallbackListener(IMediaControllerCallback cb) {
- synchronized (mControllerLock) {
+ public void registerCallbackListener(ISessionControllerCallback cb) {
+ synchronized (mLock) {
if (!mControllerCallbacks.contains(cb)) {
mControllerCallbacks.add(cb);
}
@@ -340,9 +543,9 @@
}
@Override
- public void unregisterCallbackListener(IMediaControllerCallback cb)
+ public void unregisterCallbackListener(ISessionControllerCallback cb)
throws RemoteException {
- synchronized (mControllerLock) {
+ synchronized (mLock) {
mControllerCallbacks.remove(cb);
}
}
@@ -409,9 +612,14 @@
}
@Override
- public boolean isTransportControlEnabled() throws RemoteException {
+ public boolean isTransportControlEnabled() {
return mTransportPerformerEnabled;
}
+
+ @Override
+ public void showRoutePicker() {
+ mService.showRoutePickerForSession(MediaSessionRecord.this);
+ }
}
private class MessageHandler extends Handler {
@@ -419,6 +627,8 @@
private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
private static final int MSG_UPDATE_ROUTE = 3;
private static final int MSG_SEND_EVENT = 4;
+ private static final int MSG_UPDATE_ROUTE_FILTERS = 5;
+ private static final int MSG_SEND_COMMAND = 6;
public MessageHandler(Looper looper) {
super(looper);
@@ -438,6 +648,11 @@
case MSG_SEND_EVENT:
pushEvent((String) msg.obj, msg.getData());
break;
+ case MSG_SEND_COMMAND:
+ Pair<RouteCommand, ResultReceiver> cmd =
+ (Pair<RouteCommand, ResultReceiver>) msg.obj;
+ pushRouteCommand(cmd.first, cmd.second);
+ break;
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 8fe6055..bc91370 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -17,9 +17,12 @@
package com.android.server.media;
import android.content.Context;
-import android.media.session.IMediaSession;
-import android.media.session.IMediaSessionCallback;
-import android.media.session.IMediaSessionManager;
+import android.media.routeprovider.RouteRequest;
+import android.media.session.ISession;
+import android.media.session.ISessionCallback;
+import android.media.session.ISessionManager;
+import android.media.session.RouteInfo;
+import android.media.session.RouteOptions;
import android.os.Binder;
import android.os.Handler;
import android.os.RemoteException;
@@ -38,21 +41,77 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final SessionManagerImpl mSessionManagerImpl;
+ private final MediaRouteProviderWatcher mRouteProviderWatcher;
private final ArrayList<MediaSessionRecord> mSessions
= new ArrayList<MediaSessionRecord>();
+ private final ArrayList<MediaRouteProviderProxy> mProviders
+ = new ArrayList<MediaRouteProviderProxy>();
private final Object mLock = new Object();
// TODO do we want a separate thread for handling mediasession messages?
private final Handler mHandler = new Handler();
+ // Used to keep track of the current request to show routes for a specific
+ // session so we drop late callbacks properly.
+ private int mShowRoutesRequestId = 0;
+
+ // TODO refactor to have per user state. See MediaRouterService for an
+ // example
+
public MediaSessionService(Context context) {
super(context);
mSessionManagerImpl = new SessionManagerImpl();
+ mRouteProviderWatcher = new MediaRouteProviderWatcher(context, mProviderWatcherCallback,
+ mHandler, context.getUserId());
}
@Override
public void onStart() {
publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
+ mRouteProviderWatcher.start();
+ }
+
+ /**
+ * Should trigger showing the Media route picker dialog. Right now it just
+ * kicks off a query to all the providers to get routes.
+ *
+ * @param record The session to show the picker for.
+ */
+ public void showRoutePickerForSession(MediaSessionRecord record) {
+ // TODO for now just toggle the route to test (we will only have one
+ // match for now)
+ if (record.getRoute() != null) {
+ // For now send null to mean the local route
+ record.selectRoute(null);
+ return;
+ }
+ mShowRoutesRequestId++;
+ ArrayList<MediaRouteProviderProxy> providers = mRouteProviderWatcher.getProviders();
+ for (int i = providers.size() - 1; i >= 0; i--) {
+ MediaRouteProviderProxy provider = providers.get(i);
+ provider.getRoutes(record, mShowRoutesRequestId);
+ }
+ }
+
+ /**
+ * Connect a session to the given route.
+ *
+ * @param session The session to connect.
+ * @param route The route to connect to.
+ * @param options The options to use for the connection.
+ */
+ public void connectToRoute(MediaSessionRecord session, RouteInfo route,
+ RouteOptions options) {
+ synchronized (mLock) {
+ MediaRouteProviderProxy proxy = getProviderLocked(route.getProvider());
+ if (proxy == null) {
+ Log.w(TAG, "Provider for route " + route.getName() + " does not exist.");
+ return;
+ }
+ RouteRequest request = new RouteRequest(session.getSessionInfo(), options, true);
+ // TODO make connect an async call to a ThreadPoolExecutor
+ proxy.connectToRoute(session, route, request);
+ }
}
void sessionDied(MediaSessionRecord session) {
@@ -86,14 +145,14 @@
}
private MediaSessionRecord createSessionInternal(int pid, String packageName,
- IMediaSessionCallback cb, String tag) {
+ ISessionCallback cb, String tag) {
synchronized (mLock) {
return createSessionLocked(pid, packageName, cb, tag);
}
}
private MediaSessionRecord createSessionLocked(int pid, String packageName,
- IMediaSessionCallback cb, String tag) {
+ ISessionCallback cb, String tag) {
final MediaSessionRecord session = new MediaSessionRecord(pid, packageName, cb, tag, this,
mHandler);
try {
@@ -110,9 +169,82 @@
return session;
}
- class SessionManagerImpl extends IMediaSessionManager.Stub {
+ private MediaRouteProviderProxy getProviderLocked(String providerId) {
+ for (int i = mProviders.size() - 1; i >= 0; i--) {
+ MediaRouteProviderProxy provider = mProviders.get(i);
+ if (TextUtils.equals(providerId, provider.getId())) {
+ return provider;
+ }
+ }
+ return null;
+ }
+
+ private int findIndexOfSessionForIdLocked(String sessionId) {
+ for (int i = mSessions.size() - 1; i >= 0; i--) {
+ MediaSessionRecord session = mSessions.get(i);
+ if (TextUtils.equals(session.getSessionInfo().getId(), sessionId)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private MediaRouteProviderWatcher.Callback mProviderWatcherCallback
+ = new MediaRouteProviderWatcher.Callback() {
@Override
- public IMediaSession createSession(String packageName, IMediaSessionCallback cb, String tag)
+ public void removeProvider(MediaRouteProviderProxy provider) {
+ synchronized (mLock) {
+ mProviders.remove(provider);
+ provider.setRoutesListener(null);
+ provider.setInterested(false);
+ }
+ }
+
+ @Override
+ public void addProvider(MediaRouteProviderProxy provider) {
+ synchronized (mLock) {
+ mProviders.add(provider);
+ provider.setRoutesListener(mRoutesCallback);
+ provider.setInterested(true);
+ }
+ }
+ };
+
+ private MediaRouteProviderProxy.RoutesListener mRoutesCallback
+ = new MediaRouteProviderProxy.RoutesListener() {
+ @Override
+ public void onRoutesUpdated(String sessionId, ArrayList<RouteInfo> routes,
+ int reqId) {
+ // TODO for now select the first route to test, eventually add the
+ // new routes to the dialog if it is still open
+ synchronized (mLock) {
+ int index = findIndexOfSessionForIdLocked(sessionId);
+ if (index != -1 && routes != null && routes.size() > 0) {
+ MediaSessionRecord record = mSessions.get(index);
+ record.selectRoute(routes.get(0));
+ }
+ }
+ }
+
+ @Override
+ public void onRouteConnected(String sessionId, RouteInfo route,
+ RouteRequest options, RouteConnectionRecord connection) {
+ synchronized (mLock) {
+ int index = findIndexOfSessionForIdLocked(sessionId);
+ if (index != -1) {
+ MediaSessionRecord session = mSessions.get(index);
+ session.setRouteConnected(route, options.getConnectionOptions(), connection);
+ }
+ }
+ }
+ };
+
+ class SessionManagerImpl extends ISessionManager.Stub {
+ // TODO add createSessionAsUser, pass user-id to
+ // ActivityManagerNative.handleIncomingUser and stash result for use
+ // when starting services on that session's behalf.
+ @Override
+ public ISession createSession(String packageName, ISessionCallback cb, String tag)
throws RemoteException {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/media/RouteConnectionRecord.java b/services/core/java/com/android/server/media/RouteConnectionRecord.java
new file mode 100644
index 0000000..8da0f95
--- /dev/null
+++ b/services/core/java/com/android/server/media/RouteConnectionRecord.java
@@ -0,0 +1,108 @@
+/*
+ * 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.media;
+
+import android.media.routeprovider.IRouteConnection;
+import android.media.session.RouteCommand;
+import android.media.session.RouteEvent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+/**
+ * A connection between a Session and a Route.
+ */
+public class RouteConnectionRecord {
+ private static final String TAG = "RouteConnRecord";
+ private final IRouteConnection mBinder;
+ private Listener mListener;
+
+ public RouteConnectionRecord(IRouteConnection binder) {
+ mBinder = binder;
+ }
+
+ /**
+ * Add a listener to get route events on.
+ *
+ * @param listener The listener to get events on.
+ */
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Check if this connection matches the token given.
+ *
+ * @param binder The token to check
+ * @return True if this is the connection you're looking for, false
+ * otherwise.
+ */
+ public boolean isConnection(IBinder binder) {
+ return binder != null && binder.equals(mBinder.asBinder());
+ }
+
+ /**
+ * Send an event from this connection.
+ *
+ * @param event The event to send.
+ */
+ public void sendEvent(RouteEvent event) {
+ if (mListener != null) {
+ mListener.onEvent(event);
+ }
+ }
+
+ /**
+ * Send a command to this connection.
+ *
+ * @param command The command to send.
+ * @param cb The receiver to get a result on.
+ */
+ public void sendCommand(RouteCommand command, ResultReceiver cb) {
+ try {
+ mBinder.onCommand(command, cb);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in sendCommand", e);
+ }
+ }
+
+ /**
+ * Tell the session that the provider has disconnected it.
+ */
+ public void disconnect() {
+ if (mListener != null) {
+ mListener.disconnect();
+ }
+ }
+
+ /**
+ * Listener to receive updates from the provider for this connection.
+ */
+ public static interface Listener {
+ /**
+ * Called when an event is sent on this connection.
+ *
+ * @param event The event that was sent.
+ */
+ public void onEvent(RouteEvent event);
+
+ /**
+ * Called when the provider has disconnected the route.
+ */
+ public void disconnect();
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5b597a3..c8bdb4c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -52,6 +52,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
+import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -64,6 +65,7 @@
import android.service.notification.INotificationListener;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
+import android.service.notification.ZenModeConfig;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -78,6 +80,7 @@
import com.android.internal.R;
import com.android.internal.notification.NotificationScorer;
+import com.android.internal.util.FastXmlSerializer;
import com.android.server.EventLogTags;
import com.android.server.notification.NotificationUsageStats.SingleNotificationStats;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -87,11 +90,13 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Array;
@@ -115,6 +120,7 @@
// message codes
static final int MESSAGE_TIMEOUT = 2;
+ static final int MESSAGE_SAVE_POLICY_FILE = 3;
static final int LONG_DELAY = 3500; // 3.5 seconds
static final int SHORT_DELAY = 2000; // 2 seconds
@@ -209,15 +215,6 @@
private final NotificationUsageStats mUsageStats = new NotificationUsageStats();
- private int mZenMode;
- // temporary, until we update apps to provide metadata
- private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
- "com.google.android.dialer",
- "com.android.phone"
- ));
- private static final Set<String> ALARM_PACKAGES = new HashSet<String>(Arrays.asList(
- "com.google.android.deskclock"
- ));
private static final String EXTRA_INTERCEPT = "android.intercept";
// Profiles of the current user.
@@ -421,53 +418,82 @@
Archive mArchive = new Archive();
- private void loadBlockDb() {
- synchronized(mBlockedPackages) {
- if (mPolicyFile == null) {
- File dir = new File("/data/system");
- mPolicyFile = new AtomicFile(new File(dir, "notification_policy.xml"));
+ private void loadPolicyFile() {
+ synchronized(mPolicyFile) {
+ mBlockedPackages.clear();
- mBlockedPackages.clear();
+ FileInputStream infile = null;
+ try {
+ infile = mPolicyFile.openRead();
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(infile, null);
- FileInputStream infile = null;
- try {
- infile = mPolicyFile.openRead();
- final XmlPullParser parser = Xml.newPullParser();
- parser.setInput(infile, null);
-
- int type;
- String tag;
- int version = DB_VERSION;
- while ((type = parser.next()) != END_DOCUMENT) {
- tag = parser.getName();
- if (type == START_TAG) {
- if (TAG_BODY.equals(tag)) {
- version = Integer.parseInt(
- parser.getAttributeValue(null, ATTR_VERSION));
- } else if (TAG_BLOCKED_PKGS.equals(tag)) {
- while ((type = parser.next()) != END_DOCUMENT) {
- tag = parser.getName();
- if (TAG_PACKAGE.equals(tag)) {
- mBlockedPackages.add(
- parser.getAttributeValue(null, ATTR_NAME));
- } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
- break;
- }
+ int type;
+ String tag;
+ int version = DB_VERSION;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ tag = parser.getName();
+ if (type == START_TAG) {
+ if (TAG_BODY.equals(tag)) {
+ version = Integer.parseInt(
+ parser.getAttributeValue(null, ATTR_VERSION));
+ } else if (TAG_BLOCKED_PKGS.equals(tag)) {
+ while ((type = parser.next()) != END_DOCUMENT) {
+ tag = parser.getName();
+ if (TAG_PACKAGE.equals(tag)) {
+ mBlockedPackages.add(
+ parser.getAttributeValue(null, ATTR_NAME));
+ } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
+ break;
}
}
}
}
- } catch (FileNotFoundException e) {
- // No data yet
- } catch (IOException e) {
- Log.wtf(TAG, "Unable to read blocked notifications database", e);
- } catch (NumberFormatException e) {
- Log.wtf(TAG, "Unable to parse blocked notifications database", e);
- } catch (XmlPullParserException e) {
- Log.wtf(TAG, "Unable to parse blocked notifications database", e);
- } finally {
- IoUtils.closeQuietly(infile);
+ mZenModeHelper.readXml(parser);
}
+ } catch (FileNotFoundException e) {
+ // No data yet
+ } catch (IOException e) {
+ Log.wtf(TAG, "Unable to read notification policy", e);
+ } catch (NumberFormatException e) {
+ Log.wtf(TAG, "Unable to parse notification policy", e);
+ } catch (XmlPullParserException e) {
+ Log.wtf(TAG, "Unable to parse notification policy", e);
+ } finally {
+ IoUtils.closeQuietly(infile);
+ }
+ }
+ }
+
+ public void savePolicyFile() {
+ mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
+ mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
+ }
+
+ private void handleSavePolicyFile() {
+ Slog.d(TAG, "handleSavePolicyFile");
+ synchronized (mPolicyFile) {
+ final FileOutputStream stream;
+ try {
+ stream = mPolicyFile.startWrite();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to save policy file", e);
+ return;
+ }
+
+ try {
+ final XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, TAG_BODY);
+ out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
+ mZenModeHelper.writeXml(out);
+ out.endTag(null, TAG_BODY);
+ out.endDocument();
+ mPolicyFile.finishWrite(stream);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to save policy file, restoring backup", e);
+ mPolicyFile.failWrite(stream);
}
}
}
@@ -1066,10 +1092,7 @@
@Override
public boolean allowDisable(int what, IBinder token, String pkg) {
- if (isCall(pkg, null)) {
- return mZenMode == Settings.Global.ZEN_MODE_OFF;
- }
- return true;
+ return mZenModeHelper.allowDisable(what, token, pkg);
}
@Override
@@ -1194,9 +1217,6 @@
private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
= Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
- private final Uri ZEN_MODE
- = Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
-
SettingsObserver(Handler handler) {
super(handler);
}
@@ -1207,8 +1227,6 @@
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
false, this, UserHandle.USER_ALL);
- resolver.registerContentObserver(ZEN_MODE,
- false, this);
update(null);
}
@@ -1229,13 +1247,11 @@
if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
rebindListenerServices();
}
- if (ZEN_MODE.equals(uri)) {
- updateZenMode();
- }
}
}
private SettingsObserver mSettingsObserver;
+ private ZenModeHelper mZenModeHelper;
static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
int[] ar = r.getIntArray(resid);
@@ -1261,6 +1277,15 @@
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
mHandler = new WorkerHandler();
+ mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
+ mZenModeHelper.setCallback(new ZenModeHelper.Callback() {
+ @Override
+ public void onConfigChanged() {
+ savePolicyFile();
+ }
+ });
+ final File systemDir = new File(Environment.getDataDirectory(), "system");
+ mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
importOldBlockDb();
@@ -1297,7 +1322,7 @@
Settings.Global.DEVICE_PROVISIONED, 0)) {
mDisableNotificationAlerts = true;
}
- updateZenMode();
+ mZenModeHelper.updateZenMode();
updateCurrentProfilesCache(getContext());
@@ -1350,7 +1375,7 @@
* Read the old XML-based app block database and import those blockages into the AppOps system.
*/
private void importOldBlockDb() {
- loadBlockDb();
+ loadPolicyFile();
PackageManager pm = getContext().getPackageManager();
for (String pkg : mBlockedPackages) {
@@ -1363,9 +1388,6 @@
}
}
mBlockedPackages.clear();
- if (mPolicyFile != null) {
- mPolicyFile.delete();
- }
}
@Override
@@ -1745,6 +1767,18 @@
}
@Override
+ public ZenModeConfig getZenModeConfig() {
+ checkCallerIsSystem();
+ return mZenModeHelper.getConfig();
+ }
+
+ @Override
+ public boolean setZenModeConfig(ZenModeConfig config) {
+ checkCallerIsSystem();
+ return mZenModeHelper.setConfig(config);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -1825,7 +1859,6 @@
pw.println(" mSoundNotification=" + mSoundNotification);
pw.println(" mVibrateNotification=" + mVibrateNotification);
pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts);
- pw.println(" mZenMode=" + Settings.Global.zenModeToString(mZenMode));
pw.println(" mSystemReady=" + mSystemReady);
pw.println(" mArchive=" + mArchive.toString());
Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
@@ -1841,6 +1874,8 @@
pw.println("\n Usage Stats:");
mUsageStats.dump(pw, " ");
+ pw.println("\n Zen Mode:");
+ mZenModeHelper.dump(pw, " ");
}
}
@@ -1973,7 +2008,7 @@
}
// Is this notification intercepted by zen mode?
- final boolean intercept = shouldIntercept(pkg, notification);
+ final boolean intercept = mZenModeHelper.shouldIntercept(pkg, notification);
notification.extras.putBoolean(EXTRA_INTERCEPT, intercept);
// Should this notification make noise, vibe, or use the LED?
@@ -2358,6 +2393,9 @@
case MESSAGE_TIMEOUT:
handleTimeout((ToastRecord)msg.obj);
break;
+ case MESSAGE_SAVE_POLICY_FILE:
+ handleSavePolicyFile();
+ break;
}
}
}
@@ -2722,42 +2760,6 @@
}
}
- private void updateZenMode() {
- final int mode = Settings.Global.getInt(getContext().getContentResolver(),
- Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
- if (mode != mZenMode) {
- Slog.d(TAG, String.format("updateZenMode: %s -> %s",
- Settings.Global.zenModeToString(mZenMode),
- Settings.Global.zenModeToString(mode)));
- }
- mZenMode = mode;
-
- final String[] exceptionPackages = null; // none (for now)
-
- // call restrictions
- final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF;
- mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING,
- muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
- exceptionPackages);
- mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING,
- muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
- exceptionPackages);
-
- // alarm restrictions
- final boolean muteAlarms = false; // TODO until we save user config
- mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_ALARM,
- muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
- exceptionPackages);
- mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_ALARM,
- muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
- exceptionPackages);
-
- // restrict vibrations with no hints
- mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
- (muteAlarms || muteCalls) ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
- exceptionPackages);
- }
-
private void updateCurrentProfilesCache(Context context) {
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
if (userManager != null) {
@@ -2788,19 +2790,4 @@
return mCurrentProfiles.get(userId) != null;
}
}
-
- private boolean isCall(String pkg, Notification n) {
- return CALL_PACKAGES.contains(pkg);
- }
-
- private boolean isAlarm(String pkg, Notification n) {
- return ALARM_PACKAGES.contains(pkg);
- }
-
- private boolean shouldIntercept(String pkg, Notification n) {
- if (mZenMode != Settings.Global.ZEN_MODE_OFF) {
- return !isAlarm(pkg, n);
- }
- return false;
- }
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
new file mode 100644
index 0000000..80f5b5c
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -0,0 +1,312 @@
+/**
+ * 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.AlarmManager;
+import android.app.AppOpsManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.database.ContentObserver;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.provider.Settings.Global;
+import android.service.notification.ZenModeConfig;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * NotificationManagerService helper for functionality related to zen mode.
+ */
+public class ZenModeHelper {
+ private static final String TAG = "ZenModeHelper";
+
+ private static final String ACTION_ENTER_ZEN = "enter_zen";
+ private static final int REQUEST_CODE_ENTER = 100;
+ private static final String ACTION_EXIT_ZEN = "exit_zen";
+ private static final int REQUEST_CODE_EXIT = 101;
+ private static final String EXTRA_TIME = "time";
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final SettingsObserver mSettingsObserver;
+ private final AppOpsManager mAppOps;
+ private final ZenModeConfig mDefaultConfig;
+
+ private Callback mCallback;
+ private int mZenMode;
+ private ZenModeConfig mConfig;
+
+ // temporary, until we update apps to provide metadata
+ private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
+ "com.google.android.dialer",
+ "com.android.phone"
+ ));
+ private static final Set<String> MESSAGE_PACKAGES = new HashSet<String>(Arrays.asList(
+ "com.google.android.talk",
+ "com.android.mms"
+ ));
+
+ public ZenModeHelper(Context context, Handler handler) {
+ mContext = context;
+ mHandler = handler;
+ mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ mDefaultConfig = readDefaultConfig(context.getResources());
+ mConfig = mDefaultConfig;
+ mSettingsObserver = new SettingsObserver(mHandler);
+ mSettingsObserver.observe();
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_ENTER_ZEN);
+ filter.addAction(ACTION_EXIT_ZEN);
+ mContext.registerReceiver(new ZenBroadcastReceiver(), filter);
+ }
+
+ public static ZenModeConfig readDefaultConfig(Resources resources) {
+ XmlResourceParser parser = null;
+ try {
+ parser = resources.getXml(R.xml.default_zen_mode_config);
+ while (parser.next() != XmlPullParser.END_DOCUMENT) {
+ final ZenModeConfig config = ZenModeConfig.readXml(parser);
+ if (config != null) return config;
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Error reading default zen mode config from resource", e);
+ } finally {
+ IoUtils.closeQuietly(parser);
+ }
+ return new ZenModeConfig();
+ }
+
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ public boolean shouldIntercept(String pkg, Notification n) {
+ if (mZenMode != Global.ZEN_MODE_OFF) {
+ if (isCall(pkg, n)) {
+ return !mConfig.allowCalls;
+ }
+ if (isMessage(pkg, n)) {
+ return !mConfig.allowMessages;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public void updateZenMode() {
+ final int mode = Global.getInt(mContext.getContentResolver(),
+ Global.ZEN_MODE, Global.ZEN_MODE_OFF);
+ if (mode != mZenMode) {
+ Slog.d(TAG, String.format("updateZenMode: %s -> %s",
+ Global.zenModeToString(mZenMode),
+ Global.zenModeToString(mode)));
+ }
+ mZenMode = mode;
+ final boolean zen = mZenMode != Global.ZEN_MODE_OFF;
+ final String[] exceptionPackages = null; // none (for now)
+
+ // call restrictions
+ final boolean muteCalls = zen && !mConfig.allowCalls;
+ mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING,
+ muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
+ mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING,
+ muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
+
+ // restrict vibrations with no hints
+ mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
+ zen ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
+ }
+
+ public boolean allowDisable(int what, IBinder token, String pkg) {
+ if (isCall(pkg, null)) {
+ return mZenMode == Global.ZEN_MODE_OFF || mConfig.allowCalls;
+ }
+ return true;
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("mZenMode=");
+ pw.println(Global.zenModeToString(mZenMode));
+ pw.print(prefix); pw.print("mConfig="); pw.println(mConfig);
+ pw.print(prefix); pw.print("mDefaultConfig="); pw.println(mDefaultConfig);
+ }
+
+ public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException {
+ final ZenModeConfig config = ZenModeConfig.readXml(parser);
+ if (config != null) {
+ setConfig(config);
+ }
+ }
+
+ public void writeXml(XmlSerializer out) throws IOException {
+ mConfig.writeXml(out);
+ }
+
+ public ZenModeConfig getConfig() {
+ return mConfig;
+ }
+
+ public boolean setConfig(ZenModeConfig config) {
+ if (config == null || !config.isValid()) return false;
+ if (config.equals(mConfig)) return true;
+ mConfig = config;
+ Slog.d(TAG, "mConfig=" + mConfig);
+ if (mCallback != null) mCallback.onConfigChanged();
+ final String val = Integer.toString(mConfig.hashCode());
+ Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
+ updateAlarms();
+ updateZenMode();
+ return true;
+ }
+
+ private boolean isCall(String pkg, Notification n) {
+ return CALL_PACKAGES.contains(pkg);
+ }
+
+ private boolean isMessage(String pkg, Notification n) {
+ return MESSAGE_PACKAGES.contains(pkg);
+ }
+
+ private void updateAlarms() {
+ updateAlarm(ACTION_ENTER_ZEN, REQUEST_CODE_ENTER,
+ mConfig.sleepStartHour, mConfig.sleepStartMinute);
+ updateAlarm(ACTION_EXIT_ZEN, REQUEST_CODE_EXIT,
+ mConfig.sleepEndHour, mConfig.sleepEndMinute);
+ }
+
+ private void updateAlarm(String action, int requestCode, int hr, int min) {
+ final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ final long now = System.currentTimeMillis();
+ final Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(now);
+ c.set(Calendar.HOUR_OF_DAY, hr);
+ c.set(Calendar.MINUTE, min);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+ if (c.getTimeInMillis() <= now) {
+ c.add(Calendar.DATE, 1);
+ }
+ final long time = c.getTimeInMillis();
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, requestCode,
+ new Intent(action).putExtra(EXTRA_TIME, time), PendingIntent.FLAG_UPDATE_CURRENT);
+ alarms.cancel(pendingIntent);
+ if (mConfig.sleepMode != null) {
+ Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future, now=%s",
+ action, ts(time), time - now, ts(now)));
+ alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
+ }
+ }
+
+ private static String ts(long time) {
+ return new Date(time) + " (" + time + ")";
+ }
+
+ public static boolean isWeekend(long time, int offsetDays) {
+ final Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(time);
+ if (offsetDays != 0) {
+ c.add(Calendar.DATE, offsetDays);
+ }
+ final int day = c.get(Calendar.DAY_OF_WEEK);
+ return day == Calendar.SATURDAY || day == Calendar.SUNDAY;
+ }
+
+ private class SettingsObserver extends ContentObserver {
+ private final Uri ZEN_MODE = Global.getUriFor(Global.ZEN_MODE);
+
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void observe() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(ZEN_MODE, false /*notifyForDescendents*/, this);
+ update(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ update(uri);
+ }
+
+ public void update(Uri uri) {
+ if (ZEN_MODE.equals(uri)) {
+ updateZenMode();
+ }
+ }
+ }
+
+ private class ZenBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_ENTER_ZEN.equals(intent.getAction())) {
+ setZenMode(intent, 1, Global.ZEN_MODE_ON);
+ } else if (ACTION_EXIT_ZEN.equals(intent.getAction())) {
+ setZenMode(intent, 0, Global.ZEN_MODE_OFF);
+ }
+ }
+
+ private void setZenMode(Intent intent, int wkendOffsetDays, int zenModeValue) {
+ final long schTime = intent.getLongExtra(EXTRA_TIME, 0);
+ final long now = System.currentTimeMillis();
+ Slog.d(TAG, String.format("%s scheduled for %s, fired at %s, delta=%s",
+ intent.getAction(), ts(schTime), ts(now), now - schTime));
+
+ final boolean skip = ZenModeConfig.SLEEP_MODE_WEEKNIGHTS.equals(mConfig.sleepMode) &&
+ isWeekend(schTime, wkendOffsetDays);
+
+ if (skip) {
+ Slog.d(TAG, "Skipping zen mode update for the weekend");
+ } else {
+ Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zenModeValue);
+ }
+ updateAlarms();
+ }
+ }
+
+ public interface Callback {
+ void onConfigChanged();
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 747d0a7..702d9d2 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -264,6 +264,7 @@
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
private static final String LIB_DIR_NAME = "lib";
+ private static final String LIB64_DIR_NAME = "lib64";
private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
@@ -4318,6 +4319,14 @@
private boolean updateSharedLibrariesLPw(PackageParser.Package pkg,
PackageParser.Package changingLib) {
+ // We might be upgrading from a version of the platform that did not
+ // provide per-package native library directories for system apps.
+ // Fix that up here.
+ if (isSystemApp(pkg)) {
+ PackageSetting ps = mSettings.mPackages.get(pkg.applicationInfo.packageName);
+ setInternalAppNativeLibraryPath(pkg, ps);
+ }
+
if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) {
if (mTmpSharedLibraries == null ||
mTmpSharedLibraries.length < mSharedLibraries.size()) {
@@ -4483,17 +4492,6 @@
// writer
synchronized (mPackages) {
- if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
- // Check all shared libraries and map to their actual file path.
- // We only do this here for apps not on a system dir, because those
- // are the only ones that can fail an install due to this. We
- // will take care of the system apps by updating all of their
- // library paths after the scan is done.
- if (!updateSharedLibrariesLPw(pkg, null)) {
- return null;
- }
- }
-
if (pkg.mSharedUserId != null) {
suid = mSettings.getSharedUserLPw(pkg.mSharedUserId, 0, true);
if (suid == null) {
@@ -4604,6 +4602,17 @@
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
+ if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+ // Check all shared libraries and map to their actual file path.
+ // We only do this here for apps not on a system dir, because those
+ // are the only ones that can fail an install due to this. We
+ // will take care of the system apps by updating all of their
+ // library paths after the scan is done.
+ if (!updateSharedLibrariesLPw(pkg, null)) {
+ return null;
+ }
+ }
+
if (mFoundPolicyFile) {
SELinuxMMAC.assignSeinfoValue(pkg);
}
@@ -5411,10 +5420,56 @@
}
}
+ private String calculateApkRoot(final File codePath) {
+ final File codeRoot;
+ if (FileUtils.contains(Environment.getRootDirectory(), codePath)) {
+ codeRoot = Environment.getRootDirectory();
+ } else if (FileUtils.contains(Environment.getOemDirectory(), codePath)) {
+ codeRoot = Environment.getRootDirectory();
+ } else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) {
+ codeRoot = Environment.getVendorDirectory();
+ } else {
+ // Unrecognized code path; take its top real segment as the apk root:
+ // e.g. /something/app/blah.apk => /something
+ try {
+ File f = codePath.getCanonicalFile();
+ File parent = f.getParentFile(); // non-null because codePath is a file
+ File tmp;
+ while ((tmp = parent.getParentFile()) != null) {
+ f = parent;
+ parent = tmp;
+ }
+ codeRoot = f;
+ Slog.w(TAG, "Unrecognized code path "
+ + codePath + " - using " + codeRoot);
+ } catch (IOException e) {
+ // Can't canonicalize the lib path -- shenanigans?
+ Slog.w(TAG, "Can't canonicalize code path " + codePath);
+ return Environment.getRootDirectory().getPath();
+ }
+ }
+ return codeRoot.getPath();
+ }
+
+ // This is the initial scan-time determination of how to handle a given
+ // package for purposes of native library location.
private void setInternalAppNativeLibraryPath(PackageParser.Package pkg,
PackageSetting pkgSetting) {
- final String apkLibPath = getApkName(pkgSetting.codePathString);
- final String nativeLibraryPath = new File(mAppLibInstallDir, apkLibPath).getPath();
+ // "bundled" here means system-installed with no overriding update
+ final boolean bundledApk = isSystemApp(pkg) && !isUpdatedSystemApp(pkg);
+ final String apkName = getApkName(pkgSetting.codePathString);
+ final File libDir;
+ if (bundledApk) {
+ // If "/system/lib64/apkname" exists, assume that is the per-package
+ // native library directory to use; otherwise use "/system/lib/apkname".
+ String apkRoot = calculateApkRoot(pkgSetting.codePath);
+ File lib64 = new File(apkRoot, LIB64_DIR_NAME);
+ File packLib64 = new File(lib64, apkName);
+ libDir = (packLib64.exists()) ? lib64 : new File(apkRoot, LIB_DIR_NAME);
+ } else {
+ libDir = mAppLibInstallDir;
+ }
+ final String nativeLibraryPath = (new File(libDir, apkName)).getPath();
pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath;
pkgSetting.nativeLibraryPathString = nativeLibraryPath;
}
@@ -9926,13 +9981,14 @@
}
// writer
synchronized (mPackages) {
+ PackageSetting ps = mSettings.mPackages.get(newPkg.packageName);
+ setInternalAppNativeLibraryPath(newPkg, ps);
updatePermissionsLPw(newPkg.packageName, newPkg,
UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);
if (applyUserRestrictions) {
if (DEBUG_REMOVE) {
Slog.d(TAG, "Propagating install state across reinstall");
}
- PackageSetting ps = mSettings.mPackages.get(newPkg.packageName);
for (int i = 0; i < allUserHandles.length; i++) {
if (DEBUG_REMOVE) {
Slog.d(TAG, " user " + allUserHandles[i]
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 75e5857..6700895 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
@@ -42,6 +43,7 @@
import android.tv.TvInputService;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.Surface;
@@ -116,17 +118,19 @@
UserState userState = getUserStateLocked(userId);
userState.inputList.clear();
+ if (DEBUG) Slog.d(TAG, "buildTvInputList");
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> services = pm.queryIntentServices(
new Intent(TvInputService.SERVICE_INTERFACE), PackageManager.GET_SERVICES);
for (ResolveInfo ri : services) {
ServiceInfo si = ri.serviceInfo;
if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
- Log.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
+ Slog.w(TAG, "Skipping TV input " + si.name + ": it does not require the permission "
+ android.Manifest.permission.BIND_TV_INPUT);
continue;
}
TvInputInfo info = new TvInputInfo(ri);
+ if (DEBUG) Slog.d(TAG, "add " + info.getId());
userState.inputList.add(info);
}
}
@@ -161,7 +165,7 @@
try {
state.session.release();
} catch (RemoteException e) {
- Log.e(TAG, "error in release", e);
+ Slog.e(TAG, "error in release", e);
}
}
}
@@ -173,7 +177,7 @@
try {
serviceState.service.unregisterCallback(serviceState.callback);
} catch (RemoteException e) {
- Log.e(TAG, "error in unregisterCallback", e);
+ Slog.e(TAG, "error in unregisterCallback", e);
}
}
serviceState.clients.clear();
@@ -244,7 +248,7 @@
return;
}
if (DEBUG) {
- Log.i(TAG, "bindServiceAsUser(name=" + name.getClassName() + ", userId=" + userId
+ Slog.d(TAG, "bindServiceAsUser(name=" + name.getClassName() + ", userId=" + userId
+ ")");
}
Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(name);
@@ -255,7 +259,7 @@
// This means that the service is already connected but its state indicates that we have
// nothing to do with it. Then, disconnect the service.
if (DEBUG) {
- Log.i(TAG, "unbindService(name=" + name.getClassName() + ")");
+ Slog.d(TAG, "unbindService(name=" + name.getClassName() + ")");
}
mContext.unbindService(serviceState.connection);
userState.serviceStateMap.remove(name);
@@ -267,7 +271,7 @@
final SessionState sessionState =
getUserStateLocked(userId).sessionStateMap.get(sessionToken);
if (DEBUG) {
- Log.d(TAG, "createSessionInternalLocked(name=" + sessionState.name.getClassName()
+ Slog.d(TAG, "createSessionInternalLocked(name=" + sessionState.name.getClassName()
+ ")");
}
// Set up a callback to send the session token.
@@ -275,7 +279,7 @@
@Override
public void onSessionCreated(ITvInputSession session) {
if (DEBUG) {
- Log.d(TAG, "onSessionCreated(name=" + sessionState.name.getClassName() + ")");
+ Slog.d(TAG, "onSessionCreated(name=" + sessionState.name.getClassName() + ")");
}
synchronized (mLock) {
sessionState.session = session;
@@ -295,7 +299,7 @@
try {
service.createSession(callback);
} catch (RemoteException e) {
- Log.e(TAG, "error in createSession", e);
+ Slog.e(TAG, "error in createSession", e);
removeSessionStateLocked(sessionToken, userId);
sendSessionTokenToClientLocked(sessionState.client, sessionState.name, null,
sessionState.seq, userId);
@@ -307,7 +311,7 @@
try {
client.onSessionCreated(name, sessionToken, seq);
} catch (RemoteException exception) {
- Log.e(TAG, "error in onSessionCreated", exception);
+ Slog.e(TAG, "error in onSessionCreated", exception);
}
if (sessionToken == null) {
@@ -396,7 +400,7 @@
try {
serviceState.service.registerCallback(serviceState.callback);
} catch (RemoteException e) {
- Log.e(TAG, "error in registerCallback", e);
+ Slog.e(TAG, "error in registerCallback", e);
}
} else {
updateServiceConnectionLocked(name, resolvedUserId);
@@ -432,7 +436,7 @@
try {
serviceState.service.unregisterCallback(serviceState.callback);
} catch (RemoteException e) {
- Log.e(TAG, "error in unregisterCallback", e);
+ Slog.e(TAG, "error in unregisterCallback", e);
} finally {
serviceState.callback = null;
updateServiceConnectionLocked(name, resolvedUserId);
@@ -493,7 +497,7 @@
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId).release();
} catch (RemoteException e) {
- Log.e(TAG, "error in release", e);
+ Slog.e(TAG, "error in release", e);
}
removeSessionStateLocked(sessionToken, resolvedUserId);
@@ -515,7 +519,7 @@
getSessionLocked(sessionToken, callingUid, resolvedUserId).setSurface(
surface);
} catch (RemoteException e) {
- Log.e(TAG, "error in setSurface", e);
+ Slog.e(TAG, "error in setSurface", e);
}
}
} finally {
@@ -535,7 +539,7 @@
getSessionLocked(sessionToken, callingUid, resolvedUserId).setVolume(
volume);
} catch (RemoteException e) {
- Log.e(TAG, "error in setVolume", e);
+ Slog.e(TAG, "error in setVolume", e);
}
}
} finally {
@@ -551,13 +555,10 @@
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- SessionState sessionState = getUserStateLocked(resolvedUserId)
- .sessionStateMap.get(sessionToken);
- final String serviceName = sessionState.name.getClassName();
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(channelUri);
} catch (RemoteException e) {
- Log.e(TAG, "error in tune", e);
+ Slog.e(TAG, "error in tune", e);
return;
}
}
@@ -565,6 +566,67 @@
Binder.restoreCallingIdentity(identity);
}
}
+
+ @Override
+ public void createOverlayView(IBinder sessionToken, IBinder windowToken, Rect frame,
+ int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "createOverlayView");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId)
+ .createOverlayView(windowToken, frame);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in createOverlayView", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void relayoutOverlayView(IBinder sessionToken, Rect frame, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "relayoutOverlayView");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId)
+ .relayoutOverlayView(frame);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in relayoutOverlayView", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void removeOverlayView(IBinder sessionToken, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "removeOverlayView");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId)
+ .removeOverlayView();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in removeOverlayView", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
private static final class UserState {
@@ -621,7 +683,7 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) {
- Log.d(TAG, "onServiceConnected(name=" + name.getClassName() + ")");
+ Slog.d(TAG, "onServiceConnected(name=" + name.getClassName() + ")");
}
synchronized (mLock) {
ServiceState serviceState = getServiceStateLocked(name, mUserId);
@@ -633,7 +695,7 @@
try {
serviceState.service.registerCallback(serviceState.callback);
} catch (RemoteException e) {
- Log.e(TAG, "error in registerCallback", e);
+ Slog.e(TAG, "error in registerCallback", e);
}
}
@@ -647,7 +709,7 @@
@Override
public void onServiceDisconnected(ComponentName name) {
if (DEBUG) {
- Log.d(TAG, "onServiceDisconnected(name=" + name.getClassName() + ")");
+ Slog.d(TAG, "onServiceDisconnected(name=" + name.getClassName() + ")");
}
}
}
@@ -663,7 +725,7 @@
public void onAvailabilityChanged(ComponentName name, boolean isAvailable)
throws RemoteException {
if (DEBUG) {
- Log.d(TAG, "onAvailabilityChanged(name=" + name.getClassName() + ", isAvailable="
+ Slog.d(TAG, "onAvailabilityChanged(name=" + name.getClassName() + ", isAvailable="
+ isAvailable + ")");
}
synchronized (mLock) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 912ac4d..f08d69f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -311,6 +311,7 @@
NetworkStatsService networkStats = null;
NetworkPolicyManagerService networkPolicy = null;
ConnectivityService connectivity = null;
+ NetworkScoreService networkScore = null;
NsdService serviceDiscovery= null;
IPackageManager pm = null;
WindowManagerService wm = null;
@@ -643,6 +644,14 @@
}
try {
+ Slog.i(TAG, "Network Score Service");
+ networkScore = new NetworkScoreService(context);
+ ServiceManager.addService(Context.NETWORK_SCORE_SERVICE, networkScore);
+ } catch (Throwable e) {
+ reportWtf("starting Network Score Service", e);
+ }
+
+ try {
Slog.i(TAG, "Network Service Discovery Service");
serviceDiscovery = NsdService.create(context);
ServiceManager.addService(
@@ -1021,6 +1030,7 @@
final NetworkStatsService networkStatsF = networkStats;
final NetworkPolicyManagerService networkPolicyF = networkPolicy;
final ConnectivityService connectivityF = connectivity;
+ final NetworkScoreService networkScoreF = networkScore;
final DockObserver dockF = dock;
final WallpaperManagerService wallpaperF = wallpaper;
final InputMethodManagerService immF = imm;
@@ -1069,6 +1079,11 @@
reportWtf("making Battery Service ready", e);
}
try {
+ if (networkScoreF != null) networkScoreF.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making Network Score Service ready", e);
+ }
+ try {
if (networkManagementF != null) networkManagementF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Managment Service ready", e);
diff --git a/tests/OneMedia/AndroidManifest.xml b/tests/OneMedia/AndroidManifest.xml
index 7d6ba1d..504d471 100644
--- a/tests/OneMedia/AndroidManifest.xml
+++ b/tests/OneMedia/AndroidManifest.xml
@@ -25,6 +25,15 @@
android:name="com.android.onemedia.OnePlayerService"
android:exported="false"
android:process="com.android.onemedia.service" />
+ <service
+ android:name=".provider.OneMediaRouteProvider"
+ android:permission="android.permission.BIND_ROUTE_PROVIDER"
+ android:exported="true"
+ android:process="com.android.onemedia.provider">
+ <intent-filter>
+ <action android:name="com.android.media.session.MediaRouteProvider" />
+ </intent-filter>
+ </service>
</application>
</manifest>
diff --git a/tests/OneMedia/res/layout/activity_one_player.xml b/tests/OneMedia/res/layout/activity_one_player.xml
index 4208355..516562f 100644
--- a/tests/OneMedia/res/layout/activity_one_player.xml
+++ b/tests/OneMedia/res/layout/activity_one_player.xml
@@ -53,6 +53,12 @@
android:layout_weight="1"
android:text="@string/play_button" />
</LinearLayout>
+ <Button
+ android:id="@+id/route_button"
+ style="@style/BottomBarButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/route_button" />
<TextView
android:id="@+id/status"
android:layout_width="match_parent"
diff --git a/tests/OneMedia/res/values/strings.xml b/tests/OneMedia/res/values/strings.xml
index 1b0cebb..3735c8d 100644
--- a/tests/OneMedia/res/values/strings.xml
+++ b/tests/OneMedia/res/values/strings.xml
@@ -7,6 +7,7 @@
<string name="start_button">Start</string>
<string name="play_button">Play</string>
+ <string name="route_button">Change route</string>
<string name="media_content_hint">Content</string>
<string name="media_next_hint">Next content</string>
<string name="has_video">Is video</string>
diff --git a/tests/OneMedia/src/com/android/onemedia/IPlayerCallback.aidl b/tests/OneMedia/src/com/android/onemedia/IPlayerCallback.aidl
index 2b14384..189fa6a 100644
--- a/tests/OneMedia/src/com/android/onemedia/IPlayerCallback.aidl
+++ b/tests/OneMedia/src/com/android/onemedia/IPlayerCallback.aidl
@@ -15,8 +15,8 @@
package com.android.onemedia;
-import android.media.session.MediaSessionToken;
+import android.media.session.SessionToken;
interface IPlayerCallback {
- void onSessionChanged(in MediaSessionToken session);
+ void onSessionChanged(in SessionToken session);
}
\ No newline at end of file
diff --git a/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl b/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl
index efdbe9a..15ea25f 100644
--- a/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl
+++ b/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl
@@ -15,14 +15,14 @@
package com.android.onemedia;
-import android.media.session.MediaSessionToken;
+import android.media.session.SessionToken;
import android.os.Bundle;
import com.android.onemedia.IPlayerCallback;
import com.android.onemedia.playback.IRequestCallback;
interface IPlayerService {
- MediaSessionToken getSessionToken();
+ SessionToken getSessionToken();
void registerCallback(in IPlayerCallback cb);
void unregisterCallback(in IPlayerCallback cb);
void sendRequest(String action, in Bundle params, in IRequestCallback cb);
diff --git a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
index 3114ca9..b9a6470 100644
--- a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
+++ b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
@@ -37,6 +37,7 @@
private Button mStartButton;
private Button mPlayButton;
+ private Button mRouteButton;
private TextView mStatusView;
private EditText mContentText;
@@ -54,6 +55,7 @@
mStartButton = (Button) findViewById(R.id.start_button);
mPlayButton = (Button) findViewById(R.id.play_button);
+ mRouteButton = (Button) findViewById(R.id.route_button);
mStatusView = (TextView) findViewById(R.id.status);
mContentText = (EditText) findViewById(R.id.content);
mNextContentText = (EditText) findViewById(R.id.next_content);
@@ -61,6 +63,7 @@
mStartButton.setOnClickListener(mButtonListener);
mPlayButton.setOnClickListener(mButtonListener);
+ mRouteButton.setOnClickListener(mButtonListener);
}
@@ -107,6 +110,9 @@
Log.d(TAG, "Start button pressed, in state " + mPlaybackState);
mPlayer.setContent(mContentText.getText().toString());
break;
+ case R.id.route_button:
+ mPlayer.showRoutePicker();
+ break;
}
}
@@ -117,6 +123,7 @@
public void onPlaybackStateChange(PlaybackState state) {
mPlaybackState = state.getState();
boolean enablePlay = false;
+ boolean enableControls = true;
StringBuilder statusBuilder = new StringBuilder();
switch (mPlaybackState) {
case PlaybackState.PLAYSTATE_PLAYING:
@@ -143,12 +150,17 @@
case PlaybackState.PLAYSTATE_NONE:
statusBuilder.append("none");
break;
+ case PlaybackState.PLAYSTATE_CONNECTING:
+ statusBuilder.append("connecting");
+ enableControls = false;
+ break;
default:
statusBuilder.append(mPlaybackState);
}
statusBuilder.append(" -- At position: ").append(state.getPosition());
mStatusView.setText(statusBuilder.toString());
mPlayButton.setEnabled(enablePlay);
+ setControlsEnabled(enableControls);
}
@Override
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerController.java b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
index e831ec6..e3f5c0c 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerController.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
@@ -16,9 +16,10 @@
*/
package com.android.onemedia;
-import android.media.session.MediaController;
+import android.media.session.SessionController;
import android.media.session.MediaMetadata;
-import android.media.session.MediaSessionManager;
+import android.media.session.RouteInfo;
+import android.media.session.SessionManager;
import android.media.session.PlaybackState;
import android.media.session.TransportController;
import android.os.Bundle;
@@ -39,7 +40,7 @@
public static final int STATE_DISCONNECTED = 0;
public static final int STATE_CONNECTED = 1;
- protected MediaController mController;
+ protected SessionController mController;
protected IPlayerService mBinder;
protected TransportController mTransportControls;
@@ -48,7 +49,7 @@
private Listener mListener;
private TransportListener mTransportListener = new TransportListener();
private SessionCallback mControllerCb;
- private MediaSessionManager mManager;
+ private SessionManager mManager;
private Handler mHandler = new Handler();
private boolean mResumed;
@@ -61,7 +62,7 @@
mServiceIntent = serviceIntent;
}
mControllerCb = new SessionCallback();
- mManager = (MediaSessionManager) context
+ mManager = (SessionManager) context
.getSystemService(Context.MEDIA_SESSION_SERVICE);
mResumed = false;
@@ -121,6 +122,10 @@
}
}
+ public void showRoutePicker() {
+ mController.showRoutePicker();
+ }
+
private void unbindFromService() {
mContext.unbindService(mServiceConnection);
}
@@ -150,7 +155,7 @@
mBinder = IPlayerService.Stub.asInterface(service);
Log.d(TAG, "service is " + service + " binder is " + mBinder);
try {
- mController = MediaController.fromToken(mBinder.getSessionToken());
+ mController = SessionController.fromToken(mBinder.getSessionToken());
} catch (RemoteException e) {
Log.e(TAG, "Error getting session", e);
return;
@@ -171,9 +176,9 @@
}
};
- private class SessionCallback extends MediaController.Callback {
+ private class SessionCallback extends SessionController.Callback {
@Override
- public void onRouteChanged(Bundle route) {
+ public void onRouteChanged(RouteInfo route) {
// TODO
}
}
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerService.java b/tests/OneMedia/src/com/android/onemedia/PlayerService.java
index 0ad6dd1..8b53ddf 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerService.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerService.java
@@ -17,7 +17,7 @@
import android.app.Service;
import android.content.Intent;
-import android.media.session.MediaSessionToken;
+import android.media.session.SessionToken;
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.IBinder;
@@ -149,7 +149,7 @@
}
@Override
- public MediaSessionToken getSessionToken() throws RemoteException {
+ public SessionToken getSessionToken() throws RemoteException {
return mSession.getSessionToken();
}
}
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
index a2d7897e..5dc3904 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
@@ -17,9 +17,13 @@
import android.content.Context;
import android.content.Intent;
-import android.media.session.MediaSession;
-import android.media.session.MediaSessionManager;
-import android.media.session.MediaSessionToken;
+import android.media.session.Route;
+import android.media.session.RouteInfo;
+import android.media.session.RouteOptions;
+import android.media.session.RoutePlaybackControls;
+import android.media.session.Session;
+import android.media.session.SessionManager;
+import android.media.session.SessionToken;
import android.media.session.PlaybackState;
import android.media.session.TransportPerformer;
import android.os.Bundle;
@@ -27,41 +31,55 @@
import android.view.KeyEvent;
import com.android.onemedia.playback.LocalRenderer;
+import com.android.onemedia.playback.OneMRPRenderer;
import com.android.onemedia.playback.Renderer;
-import com.android.onemedia.playback.RendererFactory;
+import com.android.onemedia.playback.RequestUtils;
+
+import java.util.ArrayList;
public class PlayerSession {
private static final String TAG = "PlayerSession";
- protected MediaSession mSession;
+ protected Session mSession;
protected Context mContext;
- protected RendererFactory mRendererFactory;
- protected LocalRenderer mRenderer;
- protected MediaSession.Callback mCallback;
+ protected Renderer mRenderer;
+ protected Session.Callback mCallback;
protected Renderer.Listener mRenderListener;
protected TransportPerformer mPerformer;
protected PlaybackState mPlaybackState;
protected Listener mListener;
+ protected ArrayList<RouteOptions> mRouteOptions;
+ protected Route mRoute;
+ protected RoutePlaybackControls mRouteControls;
+ protected RouteListener mRouteListener;
+
+ private String mContent;
public PlayerSession(Context context) {
mContext = context;
- mRendererFactory = new RendererFactory();
mRenderer = new LocalRenderer(context, null);
- mCallback = new ControllerCb();
+ mCallback = new SessionCb();
mRenderListener = new RenderListener();
mPlaybackState = new PlaybackState();
mPlaybackState.setActions(PlaybackState.ACTION_PAUSE
| PlaybackState.ACTION_PLAY);
mRenderer.registerListener(mRenderListener);
+
+ // TODO need an easier way to build route options
+ mRouteOptions = new ArrayList<RouteOptions>();
+ RouteOptions.Builder bob = new RouteOptions.Builder();
+ bob.addInterface(RoutePlaybackControls.NAME);
+ mRouteOptions.add(bob.build());
+ mRouteListener = new RouteListener();
}
public void createSession() {
if (mSession != null) {
mSession.release();
}
- MediaSessionManager man = (MediaSessionManager) mContext
+ SessionManager man = (SessionManager) mContext
.getSystemService(Context.MEDIA_SESSION_SERVICE);
Log.d(TAG, "Creating session for package " + mContext.getBasePackageName());
mSession = man.createSession("OneMedia");
@@ -69,6 +87,7 @@
mPerformer = mSession.setTransportPerformerEnabled();
mPerformer.addListener(new TransportListener());
mPerformer.setPlaybackState(mPlaybackState);
+ mSession.setRouteOptions(mRouteOptions);
mSession.publish();
}
@@ -86,18 +105,24 @@
mListener = listener;
}
- public MediaSessionToken getSessionToken() {
+ public SessionToken getSessionToken() {
return mSession.getSessionToken();
}
public void setContent(Bundle request) {
mRenderer.setContent(request);
+ mContent = request.getString(RequestUtils.EXTRA_KEY_SOURCE);
}
public void setNextContent(Bundle request) {
mRenderer.setNextContent(request);
}
+ private void updateState(int newState) {
+ mPlaybackState.setState(newState);
+ mPerformer.setPlaybackState(mPlaybackState);
+ }
+
public interface Listener {
public void onPlayStateChanged(PlaybackState state);
}
@@ -145,7 +170,11 @@
mPlaybackState.setErrorMessage("unkown state");
break;
}
- mPlaybackState.setPosition(mRenderer.getSeekPosition());
+ if (mRenderer != null) {
+ mPlaybackState.setPosition(mRenderer.getSeekPosition());
+ } else {
+ mPlaybackState.setPosition(-1);
+ }
mPerformer.setPlaybackState(mPlaybackState);
if (mListener != null) {
mListener.onPlayStateChanged(mPlaybackState);
@@ -173,8 +202,7 @@
}
- private class ControllerCb extends MediaSession.Callback {
-
+ private class SessionCb extends Session.Callback {
@Override
public void onMediaButton(Intent mediaRequestIntent) {
if (Intent.ACTION_MEDIA_BUTTON.equals(mediaRequestIntent.getAction())) {
@@ -192,6 +220,40 @@
}
}
}
+
+ @Override
+ public void onRequestRouteChange(RouteInfo route) {
+ if (mRenderer != null) {
+ mRenderer.onStop();
+ }
+ if (route == null) {
+ // Use local route
+ mRoute = null;
+ mRenderer = new LocalRenderer(mContext, null);
+ mRenderer.registerListener(mRenderListener);
+ updateState(PlaybackState.PLAYSTATE_NONE);
+ } else {
+ // Use remote route
+ mSession.connect(route, mRouteOptions.get(0));
+ mRenderer = null;
+ updateState(PlaybackState.PLAYSTATE_CONNECTING);
+ }
+ }
+
+ @Override
+ public void onRouteConnected(Route route) {
+ mRoute = route;
+ mRouteControls = RoutePlaybackControls.from(route);
+ mRouteControls.addListener(mRouteListener);
+ Log.d(TAG, "Connected to route, registering listener");
+ mRenderer = new OneMRPRenderer(mRouteControls);
+ updateState(PlaybackState.PLAYSTATE_NONE);
+ }
+
+ @Override
+ public void onRouteDisconnected(Route route, int reason) {
+
+ }
}
private class TransportListener extends TransportPerformer.Listener {
@@ -206,4 +268,12 @@
}
}
+ private class RouteListener extends RoutePlaybackControls.Listener {
+ @Override
+ public void onPlaybackStateChange(int state) {
+ Log.d(TAG, "Updating state to " + state);
+ updateState(state);
+ }
+ }
+
}
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java b/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java
index 7f62f66..c8a8d6c 100644
--- a/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java
+++ b/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java
@@ -1,3 +1,18 @@
+/*
+ * 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.onemedia.playback;
import org.apache.http.Header;
@@ -370,6 +385,8 @@
* Prepares the player for the given playback request. If the holder is null
* it is assumed this is an audio only source. If playOnReady is set to true
* the media will begin playing as soon as it can.
+ *
+ * @see RequestUtils for the set of valid keys.
*/
public void setContent(Bundle request, SurfaceHolder holder) {
String source = request.getString(RequestUtils.EXTRA_KEY_SOURCE);
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/MediaItem.java b/tests/OneMedia/src/com/android/onemedia/playback/MediaItem.java
index f9e6794..05516d2 100644
--- a/tests/OneMedia/src/com/android/onemedia/playback/MediaItem.java
+++ b/tests/OneMedia/src/com/android/onemedia/playback/MediaItem.java
@@ -1,3 +1,18 @@
+/*
+ * 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.onemedia.playback;
import android.os.Bundle;
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java b/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java
new file mode 100644
index 0000000..9b0a2b2
--- /dev/null
+++ b/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java
@@ -0,0 +1,44 @@
+package com.android.onemedia.playback;
+
+import android.media.session.RoutePlaybackControls;
+import android.os.Bundle;
+
+/**
+ * Renderer for communicating with the OneMRP route
+ */
+public class OneMRPRenderer extends Renderer {
+ private final RoutePlaybackControls mControls;
+
+ public OneMRPRenderer(RoutePlaybackControls controls) {
+ super(null, null);
+ mControls = controls;
+ }
+
+ @Override
+ public void setContent(Bundle request) {
+ mControls.playNow(request.getString(RequestUtils.EXTRA_KEY_SOURCE));
+ }
+
+ @Override
+ public boolean onStop() {
+ mControls.pause();
+ return true;
+ }
+
+ @Override
+ public boolean onPlay() {
+ mControls.resume();
+ return true;
+ }
+
+ @Override
+ public boolean onPause() {
+ mControls.pause();
+ return true;
+ }
+
+ @Override
+ public long getSeekPosition() {
+ return -1;
+ }
+}
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/PlaybackError.java b/tests/OneMedia/src/com/android/onemedia/playback/PlaybackError.java
index 72d936c..ac9da23 100644
--- a/tests/OneMedia/src/com/android/onemedia/playback/PlaybackError.java
+++ b/tests/OneMedia/src/com/android/onemedia/playback/PlaybackError.java
@@ -1,3 +1,18 @@
+/*
+ * 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.onemedia.playback;
import android.os.Bundle;
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/Renderer.java b/tests/OneMedia/src/com/android/onemedia/playback/Renderer.java
index 2451bdf..09debcf 100644
--- a/tests/OneMedia/src/com/android/onemedia/playback/Renderer.java
+++ b/tests/OneMedia/src/com/android/onemedia/playback/Renderer.java
@@ -1,3 +1,18 @@
+/*
+ * 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.onemedia.playback;
import android.content.Context;
@@ -77,39 +92,54 @@
}
public boolean onPlay() {
- throw new UnsupportedOperationException("play is not supported.");
+ // TODO consider making these log warnings instead of crashes (or
+ // Log.wtf)
+ // throw new UnsupportedOperationException("play is not supported.");
+ return false;
}
public boolean onPause() {
- throw new UnsupportedOperationException("pause is not supported.");
+ // throw new UnsupportedOperationException("pause is not supported.");
+ return false;
}
public boolean onNext() {
- throw new UnsupportedOperationException("next is not supported.");
+ // throw new UnsupportedOperationException("next is not supported.");
+ return false;
}
public boolean onPrevious() {
- throw new UnsupportedOperationException("previous is not supported.");
+ // throw new
+ // UnsupportedOperationException("previous is not supported.");
+ return false;
}
public boolean onStop() {
- throw new UnsupportedOperationException("stop is not supported.");
+ // throw new UnsupportedOperationException("stop is not supported.");
+ return false;
}
public boolean onSeekTo(int time) {
- throw new UnsupportedOperationException("seekTo is not supported.");
+ // throw new UnsupportedOperationException("seekTo is not supported.");
+ return false;
}
public long getSeekPosition() {
- throw new UnsupportedOperationException("getSeekPosition is not supported.");
+ // throw new
+ // UnsupportedOperationException("getSeekPosition is not supported.");
+ return -1;
}
public long getDuration() {
- throw new UnsupportedOperationException("getDuration is not supported.");
+ // throw new
+ // UnsupportedOperationException("getDuration is not supported.");
+ return -1;
}
public int getPlayState() {
- throw new UnsupportedOperationException("getPlayState is not supported.");
+ // throw new
+ // UnsupportedOperationException("getPlayState is not supported.");
+ return 0;
}
public void onDestroy() {
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/RendererFactory.java b/tests/OneMedia/src/com/android/onemedia/playback/RendererFactory.java
deleted file mode 100644
index f333fce..0000000
--- a/tests/OneMedia/src/com/android/onemedia/playback/RendererFactory.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.android.onemedia.playback;
-
-import android.content.Context;
-import android.media.MediaRouter;
-import android.os.Bundle;
-import android.util.Log;
-
-/**
- * TODO: Insert description here.
- */
-public class RendererFactory {
- private static final String TAG = "RendererFactory";
-
- public Renderer createRenderer(MediaRouter.RouteInfo route, Context context, Bundle params) {
- if (route.getPlaybackType() == MediaRouter.RouteInfo.PLAYBACK_TYPE_LOCAL) {
- return new LocalRenderer(context, params);
- }
- Log.e(TAG, "Unable to create renderer for route of playback type "
- + route.getPlaybackType());
- return null;
- }
-}
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java b/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
index 9b50dad..dd0d982 100644
--- a/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
+++ b/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
@@ -1,3 +1,18 @@
+/*
+ * 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.onemedia.playback;
import android.os.Bundle;
diff --git a/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java b/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java
new file mode 100644
index 0000000..6edcd7d
--- /dev/null
+++ b/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java
@@ -0,0 +1,204 @@
+/*
+ * 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.onemedia.provider;
+
+import android.media.routeprovider.RouteConnection;
+import android.media.routeprovider.RouteInterfaceHandler;
+import android.media.routeprovider.RoutePlaybackControlsHandler;
+import android.media.routeprovider.RouteProviderService;
+import android.media.routeprovider.RouteRequest;
+import android.media.session.RouteInfo;
+import android.media.session.RoutePlaybackControls;
+import android.media.session.RouteInterface;
+import android.media.session.PlaybackState;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+import com.android.onemedia.playback.LocalRenderer;
+import com.android.onemedia.playback.Renderer;
+import com.android.onemedia.playback.RequestUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Test of MediaRouteProvider. Show a dummy provider with a simple interface for
+ * playing music.
+ */
+public class OneMediaRouteProvider extends RouteProviderService {
+ private static final String TAG = "OneMRP";
+ private static final boolean DEBUG = true;
+
+ private Renderer mRenderer;
+ private RenderListener mRenderListener;
+ private PlaybackState mPlaybackState;
+ private RouteConnection mConnection;
+ private RoutePlaybackControlsHandler mControls;
+ private String mRouteId;
+ private Handler mHandler;
+
+ @Override
+ public void onCreate() {
+ mHandler = new Handler();
+ mRouteId = UUID.randomUUID().toString();
+ mRenderer = new LocalRenderer(this, null);
+ mRenderListener = new RenderListener();
+ mPlaybackState = new PlaybackState();
+ mPlaybackState.setActions(PlaybackState.ACTION_PAUSE
+ | PlaybackState.ACTION_PLAY);
+
+ mRenderer.registerListener(mRenderListener);
+
+ if (DEBUG) {
+ Log.d(TAG, "onCreate, routeId is " + mRouteId);
+ }
+ }
+
+ @Override
+ public List<RouteInfo> getMatchingRoutes(List<RouteRequest> requests) {
+ RouteInfo.Builder bob = new RouteInfo.Builder();
+ bob.setName("OneMedia").setId(mRouteId);
+ // TODO add a helper library for generating route info with the correct
+ // options
+ Log.d(TAG, "Requests:");
+ for (RouteRequest request : requests) {
+ List<String> ifaces = request.getConnectionOptions().getInterfaceNames();
+ Log.d(TAG, " request ifaces:" + ifaces.toString());
+ if (ifaces != null && ifaces.size() == 1
+ && RoutePlaybackControls.NAME.equals(ifaces.get(0))) {
+ bob.addRouteOptions(request.getConnectionOptions());
+ }
+ }
+ ArrayList<RouteInfo> result = new ArrayList<RouteInfo>();
+ if (bob.getOptionsSize() > 0) {
+ RouteInfo info = bob.build();
+ result.add(info);
+ }
+ if (DEBUG) {
+ Log.d(TAG, "getRoutes returning " + result.toString());
+ }
+ return result;
+ }
+
+ @Override
+ public RouteConnection connect(RouteInfo route, RouteRequest request) {
+ if (mConnection != null) {
+ disconnect(mConnection);
+ }
+ RouteConnection connection = new RouteConnection(this, route);
+ mControls = RoutePlaybackControlsHandler.addTo(connection);
+ mControls.addListener(new PlayHandler(mRouteId), mHandler);
+ if (DEBUG) {
+ Log.d(TAG, "Connected to route");
+ }
+ return connection;
+ }
+
+ private class PlayHandler extends RoutePlaybackControlsHandler.Listener {
+ private final String mRouteId;
+
+ public PlayHandler(String routeId) {
+ mRouteId = routeId;
+ }
+
+ @Override
+ public void playNow(String content, ResultReceiver cb) {
+ if (DEBUG) {
+ Log.d(TAG, "Attempting to play " + content);
+ }
+ // look up the route and send a play command to it
+ Bundle bundle = new Bundle();
+ bundle.putString(RequestUtils.EXTRA_KEY_SOURCE, content);
+ mRenderer.setContent(bundle);
+ RouteInterfaceHandler.sendResult(cb, RouteInterface.RESULT_SUCCESS, null);
+ }
+
+ @Override
+ public boolean resume() {
+ mRenderer.onPlay();
+ return true;
+ }
+
+ @Override
+ public boolean pause() {
+ mRenderer.onPause();
+ return true;
+ }
+ }
+
+ private class RenderListener implements Renderer.Listener {
+
+ @Override
+ public void onError(int type, int extra, Bundle extras, Throwable error) {
+ Log.d(TAG, "Sending onError with type " + type + " and extra " + extra);
+ if (mControls != null) {
+ mControls.sendPlaybackChangeEvent(PlaybackState.PLAYSTATE_ERROR);
+ }
+ }
+
+ @Override
+ public void onStateChanged(int newState) {
+ if (newState != Renderer.STATE_ERROR) {
+ mPlaybackState.setErrorMessage(null);
+ }
+ switch (newState) {
+ case Renderer.STATE_ENDED:
+ case Renderer.STATE_STOPPED:
+ mPlaybackState.setState(PlaybackState.PLAYSTATE_STOPPED);
+ break;
+ case Renderer.STATE_INIT:
+ case Renderer.STATE_PREPARING:
+ mPlaybackState.setState(PlaybackState.PLAYSTATE_BUFFERING);
+ break;
+ case Renderer.STATE_ERROR:
+ mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR);
+ break;
+ case Renderer.STATE_PAUSED:
+ mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED);
+ break;
+ case Renderer.STATE_PLAYING:
+ mPlaybackState.setState(PlaybackState.PLAYSTATE_PLAYING);
+ break;
+ default:
+ mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR);
+ mPlaybackState.setErrorMessage("unkown state");
+ break;
+ }
+ mPlaybackState.setPosition(mRenderer.getSeekPosition());
+
+ mControls.sendPlaybackChangeEvent(mPlaybackState.getState());
+ }
+
+ @Override
+ public void onBufferingUpdate(int percent) {
+ }
+
+ @Override
+ public void onFocusLost() {
+ Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED);
+ mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED);
+ mPlaybackState.setPosition(mRenderer.getSeekPosition());
+ }
+
+ @Override
+ public void onNextStarted() {
+ }
+ }
+}
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index d2eccbe..ebe1bed 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -42,6 +42,15 @@
} Command;
/*
+ * Pseudolocalization methods
+ */
+typedef enum PseudolocalizationMethod {
+ NO_PSEUDOLOCALIZATION = 0,
+ PSEUDO_ACCENTED,
+ PSEUDO_BIDI,
+} PseudolocalizationMethod;
+
+/*
* Bundle of goodies, including everything specified on the command line.
*/
class Bundle {
@@ -50,7 +59,7 @@
: mCmd(kCommandUnknown), mVerbose(false), mAndroidList(false),
mForce(false), mGrayscaleTolerance(0), mMakePackageDirs(false),
mUpdate(false), mExtending(false),
- mRequireLocalization(false), mPseudolocalize(false),
+ mRequireLocalization(false), mPseudolocalize(NO_PSEUDOLOCALIZATION),
mWantUTF16(false), mValues(false), mIncludeMetaData(false),
mCompressionMethod(0), mJunkPath(false), mOutputAPKFile(NULL),
mManifestPackageNameOverride(NULL), mInstrumentationPackageNameOverride(NULL),
@@ -95,8 +104,8 @@
void setExtending(bool val) { mExtending = val; }
bool getRequireLocalization(void) const { return mRequireLocalization; }
void setRequireLocalization(bool val) { mRequireLocalization = val; }
- bool getPseudolocalize(void) const { return mPseudolocalize; }
- void setPseudolocalize(bool val) { mPseudolocalize = val; }
+ short getPseudolocalize(void) const { return mPseudolocalize; }
+ void setPseudolocalize(short val) { mPseudolocalize = val; }
void setWantUTF16(bool val) { mWantUTF16 = val; }
bool getValues(void) const { return mValues; }
void setValues(bool val) { mValues = val; }
@@ -259,7 +268,7 @@
bool mUpdate;
bool mExtending;
bool mRequireLocalization;
- bool mPseudolocalize;
+ short mPseudolocalize;
bool mWantUTF16;
bool mValues;
bool mIncludeMetaData;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 44b8340..0af1ce1 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -2048,14 +2048,17 @@
FILE* fp;
String8 dependencyFile;
- // -c zz_ZZ means do pseudolocalization
+ // -c en_XA or/and ar_XB means do pseudolocalization
ResourceFilter filter;
err = filter.parse(bundle->getConfigurations());
if (err != NO_ERROR) {
goto bail;
}
if (filter.containsPseudo()) {
- bundle->setPseudolocalize(true);
+ bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
+ }
+ if (filter.containsPseudoBidi()) {
+ bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
}
N = bundle->getFileSpecCount();
diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp
index e8a2be4..8ca852e 100644
--- a/tools/aapt/ResourceFilter.cpp
+++ b/tools/aapt/ResourceFilter.cpp
@@ -24,8 +24,10 @@
String8 part(p, q-p);
- if (part == "zz_ZZ") {
- mContainsPseudo = true;
+ if (part == "en_XA") {
+ mContainsPseudoAccented = true;
+ } else if (part == "ar_XB") {
+ mContainsPseudoBidi = true;
}
int axis;
AxisValue value;
diff --git a/tools/aapt/ResourceFilter.h b/tools/aapt/ResourceFilter.h
index 0d127ba..c57770e 100644
--- a/tools/aapt/ResourceFilter.h
+++ b/tools/aapt/ResourceFilter.h
@@ -16,19 +16,22 @@
class ResourceFilter
{
public:
- ResourceFilter() : mData(), mContainsPseudo(false) {}
+ ResourceFilter() : mData(), mContainsPseudoAccented(false),
+ mContainsPseudoBidi(false) {}
status_t parse(const char* arg);
bool isEmpty() const;
bool match(int axis, const ResTable_config& config) const;
bool match(const ResTable_config& config) const;
const SortedVector<AxisValue>* configsForAxis(int axis) const;
- inline bool containsPseudo() const { return mContainsPseudo; }
+ inline bool containsPseudo() const { return mContainsPseudoAccented; }
+ inline bool containsPseudoBidi() const { return mContainsPseudoBidi; }
private:
bool match(int axis, const AxisValue& value) const;
KeyedVector<int,SortedVector<AxisValue> > mData;
- bool mContainsPseudo;
+ bool mContainsPseudoAccented;
+ bool mContainsPseudoBidi;
};
#endif
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 0fb2606..25bb26e 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -25,7 +25,7 @@
if (root == NULL) {
return UNKNOWN_ERROR;
}
-
+
return compileXmlFile(assets, root, target, table, options);
}
@@ -577,13 +577,13 @@
int32_t curFormat,
bool isFormatted,
const String16& product,
- bool pseudolocalize,
+ PseudolocalizationMethod pseudolocalize,
const bool overwrite,
ResourceTable* outTable)
{
status_t err;
const String16 item16("item");
-
+
String16 str;
Vector<StringPool::entry_style_span> spans;
err = parseStyledString(bundle, in->getPrintableSource().string(),
@@ -672,7 +672,7 @@
int32_t curFormat,
bool isFormatted,
const String16& product,
- bool pseudolocalize,
+ PseudolocalizationMethod pseudolocalize,
const bool overwrite,
KeyedVector<type_ident_pair_t, bool>* skippedResourceNames,
ResourceTable* outTable)
@@ -854,10 +854,16 @@
ResTable_config curParams(defParams);
ResTable_config pseudoParams(curParams);
- pseudoParams.language[0] = 'z';
- pseudoParams.language[1] = 'z';
- pseudoParams.country[0] = 'Z';
- pseudoParams.country[1] = 'Z';
+ pseudoParams.language[0] = 'e';
+ pseudoParams.language[1] = 'n';
+ pseudoParams.country[0] = 'X';
+ pseudoParams.country[1] = 'A';
+
+ ResTable_config pseudoBidiParams(curParams);
+ pseudoBidiParams.language[0] = 'a';
+ pseudoBidiParams.language[1] = 'r';
+ pseudoBidiParams.country[0] = 'X';
+ pseudoBidiParams.country[1] = 'B';
while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::START_TAG) {
@@ -1334,6 +1340,7 @@
name,
locale,
SourcePos(in->getPrintableSource(), block.getLineNumber()));
+ curIsPseudolocalizable = fileIsTranslatable;
}
if (formatted == false16) {
@@ -1389,6 +1396,7 @@
curTag = &plurals16;
curType = plurals16;
curIsBag = true;
+ curIsPseudolocalizable = fileIsTranslatable;
} else if (strcmp16(block.getElementName(&len), array16.string()) == 0) {
curTag = &array16;
curType = array16;
@@ -1410,26 +1418,24 @@
} else if (strcmp16(block.getElementName(&len), string_array16.string()) == 0) {
// Check whether these strings need valid formats.
// (simplified form of what string16 does above)
+ bool isTranslatable = false;
size_t n = block.getAttributeCount();
// Pseudolocalizable by default, unless this string array isn't
// translatable.
- curIsPseudolocalizable = true;
for (size_t i = 0; i < n; i++) {
size_t length;
const uint16_t* attr = block.getAttributeName(i, &length);
- if (strcmp16(attr, translatable16.string()) == 0) {
- const uint16_t* value = block.getAttributeStringValue(i, &length);
- if (strcmp16(value, false16.string()) == 0) {
- curIsPseudolocalizable = false;
- }
- }
-
if (strcmp16(attr, formatted16.string()) == 0) {
const uint16_t* value = block.getAttributeStringValue(i, &length);
if (strcmp16(value, false16.string()) == 0) {
curIsFormatted = false;
}
+ } else if (strcmp16(attr, translatable16.string()) == 0) {
+ const uint16_t* value = block.getAttributeStringValue(i, &length);
+ if (strcmp16(value, false16.string()) == 0) {
+ isTranslatable = false;
+ }
}
}
@@ -1438,6 +1444,7 @@
curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
curIsBag = true;
curIsBagReplaceOnOverwrite = true;
+ curIsPseudolocalizable = isTranslatable && fileIsTranslatable;
} else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) {
curTag = &integer_array16;
curType = array16;
@@ -1559,19 +1566,29 @@
err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType,
ident, parentIdent, itemIdent, curFormat, curIsFormatted,
- product, false, overwrite, outTable);
+ product, NO_PSEUDOLOCALIZATION, overwrite, outTable);
if (err == NO_ERROR) {
if (curIsPseudolocalizable && localeIsDefined(curParams)
- && bundle->getPseudolocalize()) {
+ && bundle->getPseudolocalize() > 0) {
// pseudolocalize here
-#if 1
- block.setPosition(parserPosition);
- err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage,
- curType, ident, parentIdent, itemIdent, curFormat,
- curIsFormatted, product, true, overwrite, outTable);
-#endif
+ if ((PSEUDO_ACCENTED & bundle->getPseudolocalize()) ==
+ PSEUDO_ACCENTED) {
+ block.setPosition(parserPosition);
+ err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage,
+ curType, ident, parentIdent, itemIdent, curFormat,
+ curIsFormatted, product, PSEUDO_ACCENTED,
+ overwrite, outTable);
+ }
+ if ((PSEUDO_BIDI & bundle->getPseudolocalize()) ==
+ PSEUDO_BIDI) {
+ block.setPosition(parserPosition);
+ err = parseAndAddBag(bundle, in, &block, pseudoBidiParams, myPackage,
+ curType, ident, parentIdent, itemIdent, curFormat,
+ curIsFormatted, product, PSEUDO_BIDI,
+ overwrite, outTable);
+ }
}
- }
+ }
if (err != NO_ERROR) {
hasErrors = localHasErrors = true;
}
@@ -1592,20 +1609,31 @@
err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident,
*curTag, curIsStyled, curFormat, curIsFormatted,
- product, false, overwrite, &skippedResourceNames, outTable);
+ product, NO_PSEUDOLOCALIZATION, overwrite, &skippedResourceNames, outTable);
if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR?
hasErrors = localHasErrors = true;
}
else if (err == NO_ERROR) {
if (curIsPseudolocalizable && localeIsDefined(curParams)
- && bundle->getPseudolocalize()) {
+ && bundle->getPseudolocalize() > 0) {
// pseudolocalize here
- block.setPosition(parserPosition);
- err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
- ident, *curTag, curIsStyled, curFormat,
- curIsFormatted, product,
- true, overwrite, &skippedResourceNames, outTable);
+ if ((PSEUDO_ACCENTED & bundle->getPseudolocalize()) ==
+ PSEUDO_ACCENTED) {
+ block.setPosition(parserPosition);
+ err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
+ ident, *curTag, curIsStyled, curFormat,
+ curIsFormatted, product,
+ PSEUDO_ACCENTED, overwrite, &skippedResourceNames, outTable);
+ }
+ if ((PSEUDO_BIDI & bundle->getPseudolocalize()) ==
+ PSEUDO_BIDI) {
+ block.setPosition(parserPosition);
+ err = parseAndAddEntry(bundle, in, &block, pseudoBidiParams,
+ myPackage, curType, ident, *curTag, curIsStyled, curFormat,
+ curIsFormatted, product,
+ PSEUDO_BIDI, overwrite, &skippedResourceNames, outTable);
+ }
if (err != NO_ERROR) {
hasErrors = localHasErrors = true;
}
@@ -2637,8 +2665,8 @@
continue;
}
- // don't bother with the pseudolocale "zz_ZZ"
- if (config != "zz_ZZ") {
+ // don't bother with the pseudolocale "en_XA" or "ar_XB"
+ if (config != "en_XA" && config != "ar_XB") {
if (configSrcMap.find(config) == configSrcMap.end()) {
// okay, no specific localization found. it's possible that we are
// requiring a specific regional localization [e.g. de_DE] but there is an
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index a663ad5..607d419 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -187,7 +187,7 @@
String16* outString,
Vector<StringPool::entry_style_span>* outSpans,
bool isFormatted,
- bool pseudolocalize)
+ PseudolocalizationMethod pseudolocalize)
{
Vector<StringPool::entry_style_span> spanStack;
String16 curString;
@@ -198,21 +198,30 @@
size_t len;
ResXMLTree::event_code_t code;
+ // Bracketing if pseudolocalization accented method specified.
+ if (pseudolocalize == PSEUDO_ACCENTED) {
+ curString.append(String16(String8("[")));
+ }
while ((code=inXml->next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
-
if (code == ResXMLTree::TEXT) {
String16 text(inXml->getText(&len));
if (firstTime && text.size() > 0) {
firstTime = false;
if (text.string()[0] == '@') {
// If this is a resource reference, don't do the pseudoloc.
- pseudolocalize = false;
+ pseudolocalize = NO_PSEUDOLOCALIZATION;
}
}
- if (xliffDepth == 0 && pseudolocalize) {
- std::string orig(String8(text).string());
- std::string pseudo = pseudolocalize_string(orig);
- curString.append(String16(String8(pseudo.c_str())));
+ if (xliffDepth == 0 && pseudolocalize > 0) {
+ String16 pseudo;
+ if (pseudolocalize == PSEUDO_ACCENTED) {
+ pseudo = pseudolocalize_string(text);
+ } else if (pseudolocalize == PSEUDO_BIDI) {
+ pseudo = pseudobidi_string(text);
+ } else {
+ pseudo = text;
+ }
+ curString.append(pseudo);
} else {
if (isFormatted && hasSubstitutionErrors(fileName, inXml, text) != NO_ERROR) {
return UNKNOWN_ERROR;
@@ -352,6 +361,25 @@
}
}
+ // Bracketing if pseudolocalization accented method specified.
+ if (pseudolocalize == PSEUDO_ACCENTED) {
+ const char16_t* str = outString->string();
+ const char16_t* p = str;
+ const char16_t* e = p + outString->size();
+ int words_cnt = 0;
+ while (p < e) {
+ if (isspace(*p)) {
+ words_cnt++;
+ }
+ p++;
+ }
+ unsigned int length = words_cnt > 3 ? outString->size() :
+ outString->size() / 2;
+ curString.append(String16(String8(" ")));
+ curString.append(pseudo_generate_expansion(length));
+ curString.append(String16(String8("]")));
+ }
+
if (code == ResXMLTree::BAD_DOCUMENT) {
SourcePos(String8(fileName), inXml->getLineNumber()).error(
"Error parsing XML\n");
diff --git a/tools/aapt/XMLNode.h b/tools/aapt/XMLNode.h
index 05624b7..ccbf9f4 100644
--- a/tools/aapt/XMLNode.h
+++ b/tools/aapt/XMLNode.h
@@ -26,7 +26,7 @@
String16* outString,
Vector<StringPool::entry_style_span>* outSpans,
bool isFormatted,
- bool isPseudolocalizable);
+ PseudolocalizationMethod isPseudolocalizable);
void printXMLBlock(ResXMLTree* block);
diff --git a/tools/aapt/pseudolocalize.cpp b/tools/aapt/pseudolocalize.cpp
index 9e50c5a..60aa2b2 100644
--- a/tools/aapt/pseudolocalize.cpp
+++ b/tools/aapt/pseudolocalize.cpp
@@ -2,89 +2,155 @@
using namespace std;
+// String basis to generate expansion
+static const String16 k_expansion_string = String16("one two three "
+ "four five six seven eight nine ten eleven twelve thirteen "
+ "fourteen fiveteen sixteen seventeen nineteen twenty");
+
+// Special unicode characters to override directionality of the words
+static const String16 k_rlm = String16("\xe2\x80\x8f");
+static const String16 k_rlo = String16("\xE2\x80\xae");
+static const String16 k_pdf = String16("\xE2\x80\xac");
+
+// Placeholder marks
+static const String16 k_placeholder_open = String16("\xc2\xbb");
+static const String16 k_placeholder_close = String16("\xc2\xab");
+
static const char*
-pseudolocalize_char(char c)
+pseudolocalize_char(const char16_t c)
{
switch (c) {
- case 'a': return "\xc4\x83";
- case 'b': return "\xcf\x84";
- case 'c': return "\xc4\x8b";
- case 'd': return "\xc4\x8f";
- case 'e': return "\xc4\x99";
+ case 'a': return "\xc3\xa5";
+ case 'b': return "\xc9\x93";
+ case 'c': return "\xc3\xa7";
+ case 'd': return "\xc3\xb0";
+ case 'e': return "\xc3\xa9";
case 'f': return "\xc6\x92";
case 'g': return "\xc4\x9d";
- case 'h': return "\xd1\x9b";
- case 'i': return "\xcf\x8a";
+ case 'h': return "\xc4\xa5";
+ case 'i': return "\xc3\xae";
case 'j': return "\xc4\xb5";
- case 'k': return "\xc4\xb8";
- case 'l': return "\xc4\xba";
+ case 'k': return "\xc4\xb7";
+ case 'l': return "\xc4\xbc";
case 'm': return "\xe1\xb8\xbf";
- case 'n': return "\xd0\xb8";
- case 'o': return "\xcf\x8c";
- case 'p': return "\xcf\x81";
+ case 'n': return "\xc3\xb1";
+ case 'o': return "\xc3\xb6";
+ case 'p': return "\xc3\xbe";
case 'q': return "\x51";
- case 'r': return "\xd2\x91";
+ case 'r': return "\xc5\x95";
case 's': return "\xc5\xa1";
- case 't': return "\xd1\x82";
- case 'u': return "\xce\xb0";
+ case 't': return "\xc5\xa3";
+ case 'u': return "\xc3\xbb";
case 'v': return "\x56";
- case 'w': return "\xe1\xba\x85";
+ case 'w': return "\xc5\xb5";
case 'x': return "\xd1\x85";
- case 'y': return "\xe1\xbb\xb3";
- case 'z': return "\xc5\xba";
+ case 'y': return "\xc3\xbd";
+ case 'z': return "\xc5\xbe";
case 'A': return "\xc3\x85";
case 'B': return "\xce\xb2";
- case 'C': return "\xc4\x88";
- case 'D': return "\xc4\x90";
- case 'E': return "\xd0\x84";
- case 'F': return "\xce\x93";
- case 'G': return "\xc4\x9e";
- case 'H': return "\xc4\xa6";
- case 'I': return "\xd0\x87";
- case 'J': return "\xc4\xb5";
+ case 'C': return "\xc3\x87";
+ case 'D': return "\xc3\x90";
+ case 'E': return "\xc3\x89";
+ case 'G': return "\xc4\x9c";
+ case 'H': return "\xc4\xa4";
+ case 'I': return "\xc3\x8e";
+ case 'J': return "\xc4\xb4";
case 'K': return "\xc4\xb6";
- case 'L': return "\xc5\x81";
+ case 'L': return "\xc4\xbb";
case 'M': return "\xe1\xb8\xbe";
- case 'N': return "\xc5\x83";
- case 'O': return "\xce\x98";
- case 'P': return "\xcf\x81";
+ case 'N': return "\xc3\x91";
+ case 'O': return "\xc3\x96";
+ case 'P': return "\xc3\x9e";
case 'Q': return "\x71";
- case 'R': return "\xd0\xaf";
- case 'S': return "\xc8\x98";
- case 'T': return "\xc5\xa6";
- case 'U': return "\xc5\xa8";
+ case 'R': return "\xc5\x94";
+ case 'S': return "\xc5\xa0";
+ case 'T': return "\xc5\xa2";
+ case 'U': return "\xc3\x9b";
case 'V': return "\xce\xbd";
- case 'W': return "\xe1\xba\x84";
+ case 'W': return "\xc5\xb4";
case 'X': return "\xc3\x97";
- case 'Y': return "\xc2\xa5";
+ case 'Y': return "\xc3\x9d";
case 'Z': return "\xc5\xbd";
+ case '!': return "\xc2\xa1";
+ case '?': return "\xc2\xbf";
+ case '$': return "\xe2\x82\xac";
default: return NULL;
}
}
+static bool
+is_possible_normal_placeholder_end(const char16_t c) {
+ switch (c) {
+ case 's': return true;
+ case 'S': return true;
+ case 'c': return true;
+ case 'C': return true;
+ case 'd': return true;
+ case 'o': return true;
+ case 'x': return true;
+ case 'X': return true;
+ case 'f': return true;
+ case 'e': return true;
+ case 'E': return true;
+ case 'g': return true;
+ case 'G': return true;
+ case 'a': return true;
+ case 'A': return true;
+ case 'b': return true;
+ case 'B': return true;
+ case 'h': return true;
+ case 'H': return true;
+ case '%': return true;
+ case 'n': return true;
+ default: return false;
+ }
+}
+
+String16
+pseudo_generate_expansion(const unsigned int length) {
+ String16 result = k_expansion_string;
+ const char16_t* s = result.string();
+ if (result.size() < length) {
+ result += String16(" ");
+ result += pseudo_generate_expansion(length - result.size());
+ } else {
+ int ext = 0;
+ // Should contain only whole words, so looking for a space
+ for (unsigned int i = length + 1; i < result.size(); ++i) {
+ ++ext;
+ if (s[i] == ' ') {
+ break;
+ }
+ }
+ result.remove(length + ext, 0);
+ }
+ return result;
+}
+
/**
* Converts characters so they look like they've been localized.
*
* Note: This leaves escape sequences untouched so they can later be
* processed by ResTable::collectString in the normal way.
*/
-string
-pseudolocalize_string(const string& source)
+String16
+pseudolocalize_string(const String16& source)
{
- const char* s = source.c_str();
- string result;
- const size_t I = source.length();
+ const char16_t* s = source.string();
+ String16 result;
+ const size_t I = source.size();
for (size_t i=0; i<I; i++) {
- char c = s[i];
+ char16_t c = s[i];
if (c == '\\') {
+ // Escape syntax, no need to pseudolocalize
if (i<I-1) {
- result += '\\';
+ result += String16("\\");
i++;
c = s[i];
switch (c) {
case 'u':
// this one takes up 5 chars
- result += string(s+i, 5);
+ result += String16(s+i, 5);
i += 4;
break;
case 't':
@@ -96,24 +162,107 @@
case '\'':
case '\\':
default:
- result += c;
+ result.append(&c, 1);
break;
}
} else {
- result += c;
+ result.append(&c, 1);
+ }
+ } else if (c == '%') {
+ // Placeholder syntax, no need to pseudolocalize
+ result += k_placeholder_open;
+ bool end = false;
+ result.append(&c, 1);
+ while (!end && i < I) {
+ ++i;
+ c = s[i];
+ result.append(&c, 1);
+ if (is_possible_normal_placeholder_end(c)) {
+ end = true;
+ } else if (c == 't') {
+ ++i;
+ c = s[i];
+ result.append(&c, 1);
+ end = true;
+ }
+ }
+ result += k_placeholder_close;
+ } else if (c == '<' || c == '&') {
+ // html syntax, no need to pseudolocalize
+ bool tag_closed = false;
+ while (!tag_closed && i < I) {
+ if (c == '&') {
+ String16 escape_text;
+ escape_text.append(&c, 1);
+ bool end = false;
+ size_t htmlCodePos = i;
+ while (!end && htmlCodePos < I) {
+ ++htmlCodePos;
+ c = s[htmlCodePos];
+ escape_text.append(&c, 1);
+ // Valid html code
+ if (c == ';') {
+ end = true;
+ i = htmlCodePos;
+ }
+ // Wrong html code
+ else if (!((c == '#' ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9')))) {
+ end = true;
+ }
+ }
+ result += escape_text;
+ if (escape_text != String16("<")) {
+ tag_closed = true;
+ }
+ continue;
+ }
+ if (c == '>') {
+ tag_closed = true;
+ result.append(&c, 1);
+ continue;
+ }
+ result.append(&c, 1);
+ i++;
+ c = s[i];
}
} else {
+ // This is a pure text that should be pseudolocalized
const char* p = pseudolocalize_char(c);
if (p != NULL) {
- result += p;
+ result += String16(p);
} else {
- result += c;
+ result.append(&c, 1);
}
}
}
-
- //printf("result=\'%s\'\n", result.c_str());
return result;
}
+String16
+pseudobidi_string(const String16& source)
+{
+ const char16_t* s = source.string();
+ String16 result;
+ result += k_rlm;
+ result += k_rlo;
+ for (size_t i=0; i<source.size(); i++) {
+ char16_t c = s[i];
+ switch(c) {
+ case ' ': result += k_pdf;
+ result += k_rlm;
+ result.append(&c, 1);
+ result += k_rlm;
+ result += k_rlo;
+ break;
+ default: result.append(&c, 1);
+ break;
+ }
+ }
+ result += k_pdf;
+ result += k_rlm;
+ return result;
+}
diff --git a/tools/aapt/pseudolocalize.h b/tools/aapt/pseudolocalize.h
index 94cb034..e6ab18e 100644
--- a/tools/aapt/pseudolocalize.h
+++ b/tools/aapt/pseudolocalize.h
@@ -1,9 +1,18 @@
#ifndef HOST_PSEUDOLOCALIZE_H
#define HOST_PSEUDOLOCALIZE_H
+#include "StringPool.h"
+
#include <string>
-std::string pseudolocalize_string(const std::string& source);
+String16 pseudolocalize_string(const String16& source);
+// Surrounds every word in the sentance with specific characters that makes
+// the word directionality RTL.
+String16 pseudobidi_string(const String16& source);
+// Generates expansion string based on the specified lenght.
+// Generated string could not be shorter that length, but it could be slightly
+// longer.
+String16 pseudo_generate_expansion(const unsigned int length);
#endif // HOST_PSEUDOLOCALIZE_H
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index de2e592..25eaaf5 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -428,6 +428,16 @@
}
@LayoutlibDelegate
+ /*package*/ static boolean isElegantTextHeight(Paint thisPaint) {
+ return false;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void setElegantTextHeight(Paint thisPaint, boolean elegant) {
+ // TODO
+ }
+
+ @LayoutlibDelegate
/*package*/ static float getTextSize(Paint thisPaint) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 6595ce1..08e9d99 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -58,6 +58,7 @@
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -101,6 +102,7 @@
private final ApplicationInfo mApplicationInfo;
private final IProjectCallback mProjectCallback;
private final WindowManager mWindowManager;
+ private final DisplayManager mDisplayManager;
private Resources.Theme mTheme;
@@ -149,6 +151,7 @@
}
mWindowManager = new WindowManagerImpl(mMetrics);
+ mDisplayManager = new DisplayManager(this);
}
/**
@@ -455,6 +458,10 @@
return new PowerManager(this, new BridgePowerManager(), new Handler());
}
+ if (DISPLAY_SERVICE.equals(service)) {
+ return mDisplayManager;
+ }
+
throw new UnsupportedOperationException("Unsupported Service: " + service);
}