Merge "More native interpolators"
diff --git a/api/current.txt b/api/current.txt
index 380d641..17f5e53 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1047,6 +1047,7 @@
field public static final int spinnerStyle = 16842881; // 0x1010081
field public static final int spinnersShown = 16843595; // 0x101034b
field public static final int splitMotionEvents = 16843503; // 0x10102ef
+ field public static final int splitTrack = 16843858; // 0x1010452
field public static final int src = 16843033; // 0x1010119
field public static final int ssp = 16843747; // 0x10103e3
field public static final int sspPattern = 16843749; // 0x10103e5
@@ -7612,7 +7613,7 @@
method public long getMaxExecutionDelayMillis();
method public long getMinLatencyMillis();
method public int getNetworkCapabilities();
- method public java.lang.String getServiceClassName();
+ method public android.content.ComponentName getService();
method public int getTaskId();
method public boolean isPeriodic();
method public boolean isRequireCharging();
@@ -7627,7 +7628,7 @@
}
public final class Task.Builder {
- ctor public Task.Builder(int, java.lang.Class<android.app.task.TaskService>);
+ ctor public Task.Builder(int, android.content.ComponentName);
method public android.content.Task build();
method public android.content.Task.Builder setBackoffCriteria(long, int);
method public android.content.Task.Builder setExtras(android.os.Bundle);
@@ -13340,6 +13341,45 @@
method public void stop();
}
+ public final class AudioAttributes {
+ method public int getContentType();
+ method public int getFlags();
+ method public java.util.Set<java.lang.String> getTags();
+ method public int getUsage();
+ field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
+ field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
+ field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4
+ field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1
+ field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
+ field public static final int USAGE_ALARM = 4; // 0x4
+ field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
+ field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
+ field public static final int USAGE_ASSISTANCE_SONIFICATION = 13; // 0xd
+ field public static final int USAGE_GAME = 14; // 0xe
+ field public static final int USAGE_MEDIA = 1; // 0x1
+ field public static final int USAGE_NOTIFICATION = 5; // 0x5
+ field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9
+ field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8
+ field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7
+ field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa
+ field public static final int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6; // 0x6
+ field public static final int USAGE_UNKNOWN = 0; // 0x0
+ field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2
+ field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3
+ }
+
+ public static class AudioAttributes.Builder {
+ ctor public AudioAttributes.Builder();
+ ctor public AudioAttributes.Builder(android.media.AudioAttributes);
+ method public android.media.AudioAttributes.Builder addTag(java.lang.String);
+ method public android.media.AudioAttributes build();
+ method public android.media.AudioAttributes.Builder setContentType(int);
+ method public android.media.AudioAttributes.Builder setFlags(int);
+ method public android.media.AudioAttributes.Builder setLegacyStreamType(int);
+ method public android.media.AudioAttributes.Builder setUsage(int);
+ }
+
public class AudioFormat {
ctor public AudioFormat();
field public static final deprecated int CHANNEL_CONFIGURATION_DEFAULT = 1; // 0x1
@@ -23458,6 +23498,7 @@
method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
method public static final deprecated void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean);
+ field public static final java.lang.String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled";
field public static final java.lang.String ACCESSIBILITY_ENABLED = "accessibility_enabled";
field public static final java.lang.String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password";
field public static final deprecated java.lang.String ADB_ENABLED = "adb_enabled";
@@ -25182,16 +25223,24 @@
method public final deprecated void cancelNotification(java.lang.String, java.lang.String, int);
method public final void cancelNotification(java.lang.String);
method public final void cancelNotifications(java.lang.String[]);
- method public java.lang.String[] getActiveNotificationKeys();
method public android.service.notification.StatusBarNotification[] getActiveNotifications();
method public android.service.notification.StatusBarNotification[] getActiveNotifications(java.lang.String[]);
+ method public java.lang.String[] getOrderedNotificationKeys();
method public android.os.IBinder onBind(android.content.Intent);
method public void onListenerConnected(java.lang.String[]);
+ method public void onNotificationOrderUpdate();
method public abstract void onNotificationPosted(android.service.notification.StatusBarNotification);
method public abstract void onNotificationRemoved(android.service.notification.StatusBarNotification);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
}
+ public class NotificationOrderUpdate implements android.os.Parcelable {
+ ctor public NotificationOrderUpdate(android.os.Parcel);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
public class StatusBarNotification implements android.os.Parcelable {
ctor public StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long);
ctor public StatusBarNotification(android.os.Parcel);
@@ -29130,11 +29179,11 @@
}
public final class TvInputManager {
- method public void createSession(android.content.ComponentName, android.tv.TvInputManager.SessionCreateCallback, android.os.Handler);
- method public boolean getAvailability(android.content.ComponentName);
+ method public void createSession(java.lang.String, android.tv.TvInputManager.SessionCallback, android.os.Handler);
+ method public boolean getAvailability(java.lang.String);
method public java.util.List<android.tv.TvInputInfo> getTvInputList();
- method public void registerListener(android.content.ComponentName, android.tv.TvInputManager.TvInputListener, android.os.Handler);
- method public void unregisterListener(android.content.ComponentName, android.tv.TvInputManager.TvInputListener);
+ method public void registerListener(java.lang.String, android.tv.TvInputManager.TvInputListener, android.os.Handler);
+ method public void unregisterListener(java.lang.String, android.tv.TvInputManager.TvInputListener);
}
public static final class TvInputManager.Session {
@@ -29143,13 +29192,15 @@
method public void tune(android.net.Uri);
}
- public static abstract interface TvInputManager.SessionCreateCallback {
- method public abstract void onSessionCreated(android.tv.TvInputManager.Session);
+ public static abstract class TvInputManager.SessionCallback {
+ ctor public TvInputManager.SessionCallback();
+ method public void onSessionCreated(android.tv.TvInputManager.Session);
+ method public void onSessionReleased(android.tv.TvInputManager.Session);
}
public static abstract class TvInputManager.TvInputListener {
ctor public TvInputManager.TvInputListener();
- method public void onAvailabilityChanged(android.content.ComponentName, boolean);
+ method public void onAvailabilityChanged(java.lang.String, boolean);
}
public abstract class TvInputService extends android.app.Service {
@@ -29181,7 +29232,7 @@
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 bindTvInput(java.lang.String, android.tv.TvInputManager.SessionCallback);
method public boolean dispatchUnhandledInputEvent(android.view.InputEvent);
method public boolean onUnhandledInputEvent(android.view.InputEvent);
method public void setOnUnhandledInputEventListener(android.tv.TvView.OnUnhandledInputEventListener);
@@ -34347,9 +34398,11 @@
ctor public AbsSeekBar(android.content.Context, android.util.AttributeSet, int);
ctor public AbsSeekBar(android.content.Context, android.util.AttributeSet, int, int);
method public int getKeyProgressIncrement();
+ method public boolean getSplitTrack();
method public android.graphics.drawable.Drawable getThumb();
method public int getThumbOffset();
method public void setKeyProgressIncrement(int);
+ method public void setSplitTrack(boolean);
method public void setThumb(android.graphics.drawable.Drawable);
method public void setThumbOffset(int);
}
@@ -36154,6 +36207,7 @@
ctor public Switch(android.content.Context, android.util.AttributeSet);
ctor public Switch(android.content.Context, android.util.AttributeSet, int);
ctor public Switch(android.content.Context, android.util.AttributeSet, int, int);
+ method public boolean getSplitTrack();
method public int getSwitchMinWidth();
method public int getSwitchPadding();
method public java.lang.CharSequence getTextOff();
@@ -36162,6 +36216,7 @@
method public int getThumbTextPadding();
method public android.graphics.drawable.Drawable getTrackDrawable();
method public void onMeasure(int, int);
+ method public void setSplitTrack(boolean);
method public void setSwitchMinWidth(int);
method public void setSwitchPadding(int);
method public void setSwitchTextAppearance(android.content.Context, int);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 161cb76..4cf30ae 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -47,6 +47,7 @@
import android.net.IConnectivityManager;
import android.net.Proxy;
import android.net.ProxyInfo;
+import android.net.Uri;
import android.opengl.GLUtils;
import android.os.AsyncTask;
import android.os.Binder;
@@ -839,7 +840,7 @@
InetAddress.clearDnsCache();
}
- public void setHttpProxy(String host, String port, String exclList, String pacFileUrl) {
+ public void setHttpProxy(String host, String port, String exclList, Uri pacFileUrl) {
Proxy.setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
}
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index e7902a9..0029efa 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -25,6 +25,7 @@
import android.content.pm.ServiceInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
@@ -339,7 +340,7 @@
final String proxy = data.readString();
final String port = data.readString();
final String exclList = data.readString();
- final String pacFileUrl = data.readString();
+ final Uri pacFileUrl = Uri.CREATOR.createFromParcel(data);
setHttpProxy(proxy, port, exclList, pacFileUrl);
return true;
}
@@ -1008,13 +1009,13 @@
}
public void setHttpProxy(String proxy, String port, String exclList,
- String pacFileUrl) throws RemoteException {
+ Uri pacFileUrl) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeString(proxy);
data.writeString(port);
data.writeString(exclList);
- data.writeString(pacFileUrl);
+ pacFileUrl.writeToParcel(data, 0);
mRemote.transact(SET_HTTP_PROXY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
data.recycle();
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index a832034..d5fbd0b 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -25,6 +25,7 @@
import android.content.pm.ServiceInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Debug;
import android.os.ParcelFileDescriptor;
@@ -106,7 +107,7 @@
void updateTimeZone() throws RemoteException;
void clearDnsCache() throws RemoteException;
void setHttpProxy(String proxy, String port, String exclList,
- String pacFileUrl) throws RemoteException;
+ Uri pacFileUrl) throws RemoteException;
void processInBackground() throws RemoteException;
void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args)
throws RemoteException;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index bba6caf..76a6a8e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -659,8 +659,8 @@
/**
* @hide
- * Extra added by NotificationManagerService to indicate whether a NotificationScorer
- * modified the Notifications's score.
+ * Extra added by NotificationManagerService to indicate whether
+ * the Notifications's score has been modified.
*/
public static final String EXTRA_SCORE_MODIFIED = "android.scoreModified";
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 58049fd..8884446 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2068,28 +2068,6 @@
}
/**
- * Called by a profile owner to disable account management for a specific type of account.
- *
- * <p>The calling device admin must be a profile owner. If it is not, a
- * security exception will be thrown.
- *
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param accountType For which account management is disabled or enabled.
- * @param disabled The boolean indicating that account management will be disabled (true) or
- * enabled (false).
- */
- public void setAccountManagementDisabled(ComponentName admin, String accountType,
- boolean disabled) {
- if (mService != null) {
- try {
- mService.setAccountManagementDisabled(admin, accountType, disabled);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed talking with device policy service", e);
- }
- }
- }
-
- /**
* Called by profile or device owner to re-enable system apps by intent that were disabled
* by default when the managed profile was created. This should only be called from a profile
* or device owner running within a managed profile.
@@ -2111,6 +2089,31 @@
}
/**
+ * Called by a profile owner to disable account management for a specific type of account.
+ *
+ * <p>The calling device admin must be a profile owner. If it is not, a
+ * security exception will be thrown.
+ *
+ * <p>When account management is disabled for an account type, adding or removing an account
+ * of that type will not be possible.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param accountType For which account management is disabled or enabled.
+ * @param disabled The boolean indicating that account management will be disabled (true) or
+ * enabled (false).
+ */
+ public void setAccountManagementDisabled(ComponentName admin, String accountType,
+ boolean disabled) {
+ if (mService != null) {
+ try {
+ mService.setAccountManagementDisabled(admin, accountType, disabled);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
* Gets the array of accounts for which account management is disabled by the profile owner.
*
* <p> Account management can be disabled/enabled by calling
diff --git a/core/java/android/app/task/TaskParams.java b/core/java/android/app/task/TaskParams.java
index e2eafd8..0351082 100644
--- a/core/java/android/app/task/TaskParams.java
+++ b/core/java/android/app/task/TaskParams.java
@@ -29,7 +29,14 @@
private final int taskId;
private final Bundle extras;
- private final IBinder mCallback;
+ private final IBinder callback;
+
+ /** @hide */
+ public TaskParams(int taskId, Bundle extras, IBinder callback) {
+ this.taskId = taskId;
+ this.extras = extras;
+ this.callback = callback;
+ }
/**
* @return The unique id of this task, specified at creation time.
@@ -47,17 +54,15 @@
return extras;
}
- /**
- * @hide
- */
+ /** @hide */
public ITaskCallback getCallback() {
- return ITaskCallback.Stub.asInterface(mCallback);
+ return ITaskCallback.Stub.asInterface(callback);
}
private TaskParams(Parcel in) {
taskId = in.readInt();
extras = in.readBundle();
- mCallback = in.readStrongBinder();
+ callback = in.readStrongBinder();
}
@Override
@@ -69,7 +74,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(taskId);
dest.writeBundle(extras);
- dest.writeStrongBinder(mCallback);
+ dest.writeStrongBinder(callback);
}
public static final Creator<TaskParams> CREATOR = new Creator<TaskParams>() {
diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
index a0b603e..f0c8299 100644
--- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
+++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
@@ -20,7 +20,7 @@
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.DhcpResults;
-import android.net.LinkCapabilities;
+import android.net.NetworkCapabilities;
import android.net.LinkProperties;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
@@ -75,7 +75,7 @@
private BluetoothTetheringDataTracker() {
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORKTYPE, "");
mLinkProperties = new LinkProperties();
- mLinkCapabilities = new LinkCapabilities();
+ mNetworkCapabilities = new NetworkCapabilities();
mNetworkInfo.setIsAvailable(false);
setTeardownRequested(false);
@@ -242,16 +242,6 @@
}
}
- /**
- * A capability is an Integer/String pair, the capabilities
- * are defined in the class LinkSocket#Key.
- *
- * @return a copy of this connections capabilities, may be empty but never null.
- */
- public LinkCapabilities getLinkCapabilities() {
- return new LinkCapabilities(mLinkCapabilities);
- }
-
/**
* Fetch default gateway address for the network
*/
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index d4f7f06..6b4404d 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -82,7 +82,7 @@
/**
* Set a set of String values in the preferences editor, to be written
- * back once {@link #commit} is called.
+ * back once {@link #commit} or {@link #apply} is called.
*
* @param key The name of the preference to modify.
* @param values The set of new values for the preference. Passing {@code null}
diff --git a/core/java/android/content/Task.java b/core/java/android/content/Task.java
index ed5ed884..407880f 100644
--- a/core/java/android/content/Task.java
+++ b/core/java/android/content/Task.java
@@ -42,6 +42,20 @@
public final int EXPONENTIAL = 1;
}
+ private final int taskId;
+ // TODO: Change this to use PersistableBundle when that lands in master.
+ private final Bundle extras;
+ private final ComponentName service;
+ private final boolean requireCharging;
+ private final boolean requireDeviceIdle;
+ private final int networkCapabilities;
+ private final long minLatencyMillis;
+ private final long maxExecutionDelayMillis;
+ private final boolean isPeriodic;
+ private final long intervalMillis;
+ private final long initialBackoffMillis;
+ private final int backoffPolicy;
+
/**
* Unique task id associated with this class. This is assigned to your task by the scheduler.
*/
@@ -59,8 +73,8 @@
/**
* Name of the service endpoint that will be called back into by the TaskManager.
*/
- public String getServiceClassName() {
- return serviceClassName;
+ public ComponentName getService() {
+ return service;
}
/**
@@ -132,24 +146,10 @@
return backoffPolicy;
}
- private final int taskId;
- // TODO: Change this to use PersistableBundle when that lands in master.
- private final Bundle extras;
- private final String serviceClassName;
- private final boolean requireCharging;
- private final boolean requireDeviceIdle;
- private final int networkCapabilities;
- private final long minLatencyMillis;
- private final long maxExecutionDelayMillis;
- private final boolean isPeriodic;
- private final long intervalMillis;
- private final long initialBackoffMillis;
- private final int backoffPolicy;
-
private Task(Parcel in) {
taskId = in.readInt();
extras = in.readBundle();
- serviceClassName = in.readString();
+ service = ComponentName.readFromParcel(in);
requireCharging = in.readInt() == 1;
requireDeviceIdle = in.readInt() == 1;
networkCapabilities = in.readInt();
@@ -164,7 +164,7 @@
private Task(Task.Builder b) {
taskId = b.mTaskId;
extras = new Bundle(b.mExtras);
- serviceClassName = b.mTaskServiceClassName;
+ service = b.mTaskService;
requireCharging = b.mRequiresCharging;
requireDeviceIdle = b.mRequiresDeviceIdle;
networkCapabilities = b.mNetworkCapabilities;
@@ -185,7 +185,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeInt(taskId);
out.writeBundle(extras);
- out.writeString(serviceClassName);
+ ComponentName.writeToParcel(service, out);
out.writeInt(requireCharging ? 1 : 0);
out.writeInt(requireDeviceIdle ? 1 : 0);
out.writeInt(networkCapabilities);
@@ -215,7 +215,7 @@
public final class Builder {
private int mTaskId;
private Bundle mExtras;
- private String mTaskServiceClassName;
+ private ComponentName mTaskService;
// Requirements.
private boolean mRequiresCharging;
private boolean mRequiresDeviceIdle;
@@ -236,11 +236,11 @@
* @param taskId Application-provided id for this task. Subsequent calls to cancel, or
* tasks created with the same taskId, will update the pre-existing task with
* the same id.
- * @param cls The endpoint that you implement that will receive the callback from the
+ * @param taskService The endpoint that you implement that will receive the callback from the
* TaskManager.
*/
- public Builder(int taskId, Class<TaskService> cls) {
- mTaskServiceClassName = cls.getClass().getName();
+ public Builder(int taskId, ComponentName taskService) {
+ mTaskService = taskService;
mTaskId = taskId;
}
@@ -296,7 +296,7 @@
* period. You have no control over when within this interval this task will be executed,
* only the guarantee that it will be executed at most once within this interval.
* A periodic task will be repeated until the phone is turned off, however it will only be
- * persisted if the client app has declared the
+ * persisted beyond boot if the client app has declared the
* {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission. You can schedule
* periodic tasks without this permission, they simply will cease to exist after the phone
* restarts.
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 4bea9ee..86208fc 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -397,6 +397,32 @@
public static final String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate";
/**
+ * A constant describing a wake gesture sensor.
+ * <p>
+ * Wake gesture sensors enable waking up the device based on a device specific motion.
+ * <p>
+ * When this sensor triggers, the device behaves as if the power button was pressed, turning the
+ * screen on. This behavior (turning on the screen when this sensor triggers) might be
+ * deactivated by the user in the device settings. Changes in settings do not impact the
+ * behavior of the sensor: only whether the framework turns the screen on when it triggers.
+ * <p>
+ * The actual gesture to be detected is not specified, and can be chosen by the manufacturer of
+ * the device. This sensor must be low power, as it is likely to be activated 24/7.
+ * Values of events created by this sensors should not be used.
+ *
+ * @hide This sensor is expected to only be used by the power manager
+ */
+ public static final int TYPE_WAKE_GESTURE = 42;
+
+ /**
+ * A constant string describing a wake gesture sensor.
+ *
+ * @hide This sensor is expected to only be used by the power manager
+ * @see #TYPE_WAKE_GESTURE
+ */
+ public static final String STRING_TYPE_WAKE_GESTURE = "android.sensor.wake_gesture";
+
+ /**
* A constant describing all sensor types.
*/
public static final int TYPE_ALL = -1;
diff --git a/core/java/android/net/BaseNetworkStateTracker.java b/core/java/android/net/BaseNetworkStateTracker.java
index 804f8ee..79db389 100644
--- a/core/java/android/net/BaseNetworkStateTracker.java
+++ b/core/java/android/net/BaseNetworkStateTracker.java
@@ -44,7 +44,8 @@
protected NetworkInfo mNetworkInfo;
protected LinkProperties mLinkProperties;
- protected LinkCapabilities mLinkCapabilities;
+ protected NetworkCapabilities mNetworkCapabilities;
+ protected Network mNetwork = new Network(ConnectivityManager.INVALID_NET_ID);
private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
@@ -54,7 +55,7 @@
mNetworkInfo = new NetworkInfo(
networkType, -1, ConnectivityManager.getNetworkTypeName(networkType), null);
mLinkProperties = new LinkProperties();
- mLinkCapabilities = new LinkCapabilities();
+ mNetworkCapabilities = new NetworkCapabilities();
}
protected BaseNetworkStateTracker() {
@@ -98,8 +99,8 @@
}
@Override
- public LinkCapabilities getLinkCapabilities() {
- return new LinkCapabilities(mLinkCapabilities);
+ public NetworkCapabilities getNetworkCapabilities() {
+ return new NetworkCapabilities(mNetworkCapabilities);
}
@Override
@@ -201,4 +202,14 @@
public void stopSampling(SamplingDataTracker.SamplingSnapshot s) {
// nothing to do
}
+
+ @Override
+ public void setNetId(int netId) {
+ mNetwork = new Network(netId);
+ }
+
+ @Override
+ public Network getNetwork() {
+ return mNetwork;
+ }
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 30d7043..3e00250 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -408,6 +408,11 @@
*/
public static final int CONNECTIVITY_CHANGE_DELAY_DEFAULT = 3000;
+ /**
+ * @hide
+ */
+ public final static int INVALID_NET_ID = 0;
+
private final IConnectivityManager mService;
private final String mPackageName;
diff --git a/core/java/android/net/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java
index a5d059e..eff9f9f 100644
--- a/core/java/android/net/DummyDataStateTracker.java
+++ b/core/java/android/net/DummyDataStateTracker.java
@@ -190,13 +190,6 @@
return new LinkProperties(mLinkProperties);
}
- /**
- * @see android.net.NetworkStateTracker#getLinkCapabilities()
- */
- public LinkCapabilities getLinkCapabilities() {
- return new LinkCapabilities(mLinkCapabilities);
- }
-
public void setDependencyMet(boolean met) {
// not supported on this network
}
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index 10b5d0b..c1afc9b 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -103,7 +103,7 @@
private EthernetDataTracker() {
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORKTYPE, "");
mLinkProperties = new LinkProperties();
- mLinkCapabilities = new LinkCapabilities();
+ mNetworkCapabilities = new NetworkCapabilities();
}
private void interfaceUpdated() {
@@ -372,16 +372,6 @@
return new LinkProperties(mLinkProperties);
}
- /**
- * A capability is an Integer/String pair, the capabilities
- * are defined in the class LinkSocket#Key.
- *
- * @return a copy of this connections capabilities, may be empty but never null.
- */
- public LinkCapabilities getLinkCapabilities() {
- return new LinkCapabilities(mLinkCapabilities);
- }
-
/**
* Fetch default gateway address for the network
*/
diff --git a/core/java/android/net/LinkCapabilities.java b/core/java/android/net/LinkCapabilities.java
deleted file mode 100644
index fb444ea..0000000
--- a/core/java/android/net/LinkCapabilities.java
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- * Copyright (C) 2010 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.os.Parcelable;
-import android.os.Parcel;
-import android.util.Log;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-/**
- * A class representing the capabilities of a link
- *
- * @hide
- */
-public class LinkCapabilities implements Parcelable {
- private static final String TAG = "LinkCapabilities";
- private static final boolean DBG = false;
-
- /** The Map of Keys to Values */
- private HashMap<Integer, String> mCapabilities;
-
-
- /**
- * The set of keys defined for a links capabilities.
- *
- * Keys starting with RW are read + write, i.e. the application
- * can request for a certain requirement corresponding to that key.
- * Keys starting with RO are read only, i.e. the the application
- * can read the value of that key from the socket but cannot request
- * a corresponding requirement.
- *
- * TODO: Provide a documentation technique for concisely and precisely
- * define the syntax for each value string associated with a key.
- */
- public static final class Key {
- /** No constructor */
- private Key() {}
-
- /**
- * An integer representing the network type.
- * @see ConnectivityManager
- */
- public final static int RO_NETWORK_TYPE = 1;
-
- /**
- * Desired minimum forward link (download) bandwidth for the
- * in kilobits per second (kbps). Values should be strings such
- * "50", "100", "1500", etc.
- */
- public final static int RW_DESIRED_FWD_BW = 2;
-
- /**
- * Required minimum forward link (download) bandwidth, in
- * per second (kbps), below which the socket cannot function.
- * Values should be strings such as "50", "100", "1500", etc.
- */
- public final static int RW_REQUIRED_FWD_BW = 3;
-
- /**
- * Available forward link (download) bandwidth for the socket.
- * This value is in kilobits per second (kbps).
- * Values will be strings such as "50", "100", "1500", etc.
- */
- public final static int RO_AVAILABLE_FWD_BW = 4;
-
- /**
- * Desired minimum reverse link (upload) bandwidth for the socket
- * in kilobits per second (kbps).
- * Values should be strings such as "50", "100", "1500", etc.
- * <p>
- * This key is set via the needs map.
- */
- public final static int RW_DESIRED_REV_BW = 5;
-
- /**
- * Required minimum reverse link (upload) bandwidth, in kilobits
- * per second (kbps), below which the socket cannot function.
- * If a rate is not specified, the default rate of kbps will be
- * Values should be strings such as "50", "100", "1500", etc.
- */
- public final static int RW_REQUIRED_REV_BW = 6;
-
- /**
- * Available reverse link (upload) bandwidth for the socket.
- * This value is in kilobits per second (kbps).
- * Values will be strings such as "50", "100", "1500", etc.
- */
- public final static int RO_AVAILABLE_REV_BW = 7;
-
- /**
- * Maximum latency for the socket, in milliseconds, above which
- * socket cannot function.
- * Values should be strings such as "50", "300", "500", etc.
- */
- public final static int RW_MAX_ALLOWED_LATENCY = 8;
-
- /**
- * Interface that the socket is bound to. This can be a virtual
- * interface (e.g. VPN or Mobile IP) or a physical interface
- * (e.g. wlan0 or rmnet0).
- * Values will be strings such as "wlan0", "rmnet0"
- */
- public final static int RO_BOUND_INTERFACE = 9;
-
- /**
- * Physical interface that the socket is routed on.
- * This can be different from BOUND_INTERFACE in cases such as
- * VPN or Mobile IP. The physical interface may change over time
- * if seamless mobility is supported.
- * Values will be strings such as "wlan0", "rmnet0"
- */
- public final static int RO_PHYSICAL_INTERFACE = 10;
- }
-
- /**
- * Role informs the LinkSocket about the data usage patterns of your
- * application.
- * <P>
- * {@code Role.DEFAULT} is the default role, and is used whenever
- * a role isn't set.
- */
- public static final class Role {
- /** No constructor */
- private Role() {}
-
- // examples only, discuss which roles should be defined, and then
- // code these to match
-
- /** Default Role */
- public static final String DEFAULT = "default";
- /** Bulk down load */
- public static final String BULK_DOWNLOAD = "bulk.download";
- /** Bulk upload */
- public static final String BULK_UPLOAD = "bulk.upload";
-
- /** VoIP Application at 24kbps */
- public static final String VOIP_24KBPS = "voip.24k";
- /** VoIP Application at 32kbps */
- public static final String VOIP_32KBPS = "voip.32k";
-
- /** Video Streaming at 480p */
- public static final String VIDEO_STREAMING_480P = "video.streaming.480p";
- /** Video Streaming at 720p */
- public static final String VIDEO_STREAMING_720I = "video.streaming.720i";
-
- /** Video Chat Application at 360p */
- public static final String VIDEO_CHAT_360P = "video.chat.360p";
- /** Video Chat Application at 480p */
- public static final String VIDEO_CHAT_480P = "video.chat.480i";
- }
-
- /**
- * Constructor
- */
- public LinkCapabilities() {
- mCapabilities = new HashMap<Integer, String>();
- }
-
- /**
- * Copy constructor.
- *
- * @param source
- */
- public LinkCapabilities(LinkCapabilities source) {
- if (source != null) {
- mCapabilities = new HashMap<Integer, String>(source.mCapabilities);
- } else {
- mCapabilities = new HashMap<Integer, String>();
- }
- }
-
- /**
- * Create the {@code LinkCapabilities} with values depending on role type.
- * @param applicationRole a {@code LinkSocket.Role}
- * @return the {@code LinkCapabilities} associated with the applicationRole, empty if none
- */
- public static LinkCapabilities createNeedsMap(String applicationRole) {
- if (DBG) log("createNeededCapabilities(applicationRole) EX");
- return new LinkCapabilities();
- }
-
- /**
- * Remove all capabilities
- */
- public void clear() {
- mCapabilities.clear();
- }
-
- /**
- * Returns whether this map is empty.
- */
- public boolean isEmpty() {
- return mCapabilities.isEmpty();
- }
-
- /**
- * Returns the number of elements in this map.
- *
- * @return the number of elements in this map.
- */
- public int size() {
- return mCapabilities.size();
- }
-
- /**
- * Given the key return the capability string
- *
- * @param key
- * @return the capability string
- */
- public String get(int key) {
- return mCapabilities.get(key);
- }
-
- /**
- * Store the key/value capability pair
- *
- * @param key
- * @param value
- */
- public void put(int key, String value) {
- mCapabilities.put(key, value);
- }
-
- /**
- * Returns whether this map contains the specified key.
- *
- * @param key to search for.
- * @return {@code true} if this map contains the specified key,
- * {@code false} otherwise.
- */
- public boolean containsKey(int key) {
- return mCapabilities.containsKey(key);
- }
-
- /**
- * Returns whether this map contains the specified value.
- *
- * @param value to search for.
- * @return {@code true} if this map contains the specified value,
- * {@code false} otherwise.
- */
- public boolean containsValue(String value) {
- return mCapabilities.containsValue(value);
- }
-
- /**
- * Returns a set containing all of the mappings in this map. Each mapping is
- * an instance of {@link Map.Entry}. As the set is backed by this map,
- * changes in one will be reflected in the other.
- *
- * @return a set of the mappings.
- */
- public Set<Entry<Integer, String>> entrySet() {
- return mCapabilities.entrySet();
- }
-
- /**
- * @return the set of the keys.
- */
- public Set<Integer> keySet() {
- return mCapabilities.keySet();
- }
-
- /**
- * @return the set of values
- */
- public Collection<String> values() {
- return mCapabilities.values();
- }
-
- /**
- * Implement the Parcelable interface
- * @hide
- */
- public int describeContents() {
- return 0;
- }
-
- /**
- * Convert to string for debugging
- */
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("{");
- boolean firstTime = true;
- for (Entry<Integer, String> entry : mCapabilities.entrySet()) {
- if (firstTime) {
- firstTime = false;
- } else {
- sb.append(",");
- }
- sb.append(entry.getKey());
- sb.append(":\"");
- sb.append(entry.getValue());
- sb.append("\"");
- }
- sb.append("}");
- return sb.toString();
- }
-
- /**
- * Implement the Parcelable interface.
- * @hide
- */
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mCapabilities.size());
- for (Entry<Integer, String> entry : mCapabilities.entrySet()) {
- dest.writeInt(entry.getKey().intValue());
- dest.writeString(entry.getValue());
- }
- }
-
- /**
- * Implement the Parcelable interface.
- * @hide
- */
- public static final Creator<LinkCapabilities> CREATOR =
- new Creator<LinkCapabilities>() {
- public LinkCapabilities createFromParcel(Parcel in) {
- LinkCapabilities capabilities = new LinkCapabilities();
- int size = in.readInt();
- while (size-- != 0) {
- int key = in.readInt();
- String value = in.readString();
- capabilities.mCapabilities.put(key, value);
- }
- return capabilities;
- }
-
- public LinkCapabilities[] newArray(int size) {
- return new LinkCapabilities[size];
- }
- };
-
- /**
- * Debug logging
- */
- protected static void log(String s) {
- Log.d(TAG, s);
- }
-}
diff --git a/core/java/android/net/LinkSocket.java b/core/java/android/net/LinkSocket.java
deleted file mode 100644
index 5aa6451..0000000
--- a/core/java/android/net/LinkSocket.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (C) 2010 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.LinkCapabilities;
-import android.net.LinkProperties;
-import android.net.LinkSocketNotifier;
-
-import android.util.Log;
-
-import java.io.IOException;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.SocketTimeoutException;
-import java.net.UnknownHostException;
-import java.util.HashSet;
-import java.util.Set;
-
-/** @hide */
-public class LinkSocket extends Socket {
- private final static String TAG = "LinkSocket";
- private final static boolean DBG = true;
-
- /**
- * Default constructor
- */
- public LinkSocket() {
- if (DBG) log("LinkSocket() EX");
- }
-
- /**
- * Creates a new unconnected socket.
- * @param notifier a reference to a class that implements {@code LinkSocketNotifier}
- */
- public LinkSocket(LinkSocketNotifier notifier) {
- if (DBG) log("LinkSocket(notifier) EX");
- }
-
- /**
- * Creates a new unconnected socket usign the given proxy type.
- * @param notifier a reference to a class that implements {@code LinkSocketNotifier}
- * @param proxy the specified proxy for this socket
- * @throws IllegalArgumentException if the argument proxy is null or of an invalid type.
- * @throws SecurityException if a security manager exists and it denies the permission
- * to connect to the given proxy.
- */
- public LinkSocket(LinkSocketNotifier notifier, Proxy proxy) {
- if (DBG) log("LinkSocket(notifier, proxy) EX");
- }
-
- /**
- * @return the {@code LinkProperties} for the socket
- */
- public LinkProperties getLinkProperties() {
- if (DBG) log("LinkProperties() EX");
- return new LinkProperties();
- }
-
- /**
- * Set the {@code LinkCapabilies} needed for this socket. If the socket is already connected
- * or is a duplicate socket the request is ignored and {@code false} will
- * be returned. A needs map can be created via the {@code createNeedsMap} static
- * method.
- * @param needs the needs of the socket
- * @return {@code true} if needs are successfully set, {@code false} otherwise
- */
- public boolean setNeededCapabilities(LinkCapabilities needs) {
- if (DBG) log("setNeeds() EX");
- return false;
- }
-
- /**
- * @return the LinkCapabilites set by setNeededCapabilities, empty if none has been set
- */
- public LinkCapabilities getNeededCapabilities() {
- if (DBG) log("getNeeds() EX");
- return null;
- }
-
- /**
- * @return all of the {@code LinkCapabilities} of the link used by this socket
- */
- public LinkCapabilities getCapabilities() {
- if (DBG) log("getCapabilities() EX");
- return null;
- }
-
- /**
- * Returns this LinkSockets set of capabilities, filtered according to
- * the given {@code Set}. Capabilities in the Set but not available from
- * the link will not be reported in the results. Capabilities of the link
- * but not listed in the Set will also not be reported in the results.
- * @param capabilities {@code Set} of capabilities requested
- * @return the filtered {@code LinkCapabilities} of this LinkSocket, may be empty
- */
- public LinkCapabilities getCapabilities(Set<Integer> capabilities) {
- if (DBG) log("getCapabilities(capabilities) EX");
- return new LinkCapabilities();
- }
-
- /**
- * Provide the set of capabilities the application is interested in tracking
- * for this LinkSocket.
- * @param capabilities a {@code Set} of capabilities to track
- */
- public void setTrackedCapabilities(Set<Integer> capabilities) {
- if (DBG) log("setTrackedCapabilities(capabilities) EX");
- }
-
- /**
- * @return the {@code LinkCapabilities} that are tracked, empty if none has been set.
- */
- public Set<Integer> getTrackedCapabilities() {
- if (DBG) log("getTrackedCapabilities(capabilities) EX");
- return new HashSet<Integer>();
- }
-
- /**
- * Connects this socket to the given remote host address and port specified
- * by dstName and dstPort.
- * @param dstName the address of the remote host to connect to
- * @param dstPort the port to connect to on the remote host
- * @param timeout the timeout value in milliseconds or 0 for infinite timeout
- * @throws UnknownHostException if the given dstName is invalid
- * @throws IOException if the socket is already connected or an error occurs
- * while connecting
- * @throws SocketTimeoutException if the timeout fires
- */
- public void connect(String dstName, int dstPort, int timeout)
- throws UnknownHostException, IOException, SocketTimeoutException {
- if (DBG) log("connect(dstName, dstPort, timeout) EX");
- }
-
- /**
- * Connects this socket to the given remote host address and port specified
- * by dstName and dstPort.
- * @param dstName the address of the remote host to connect to
- * @param dstPort the port to connect to on the remote host
- * @throws UnknownHostException if the given dstName is invalid
- * @throws IOException if the socket is already connected or an error occurs
- * while connecting
- */
- public void connect(String dstName, int dstPort)
- throws UnknownHostException, IOException {
- if (DBG) log("connect(dstName, dstPort, timeout) EX");
- }
-
- /**
- * Connects this socket to the given remote host address and port specified
- * by the SocketAddress with the specified timeout.
- * @deprecated Use {@code connect(String dstName, int dstPort, int timeout)}
- * instead. Using this method may result in reduced functionality.
- * @param remoteAddr the address and port of the remote host to connect to
- * @throws IllegalArgumentException if the given SocketAddress is invalid
- * @throws IOException if the socket is already connected or an error occurs
- * while connecting
- * @throws SocketTimeoutException if the timeout expires
- */
- @Override
- @Deprecated
- public void connect(SocketAddress remoteAddr, int timeout)
- throws IOException, SocketTimeoutException {
- if (DBG) log("connect(remoteAddr, timeout) EX DEPRECATED");
- }
-
- /**
- * Connects this socket to the given remote host address and port specified
- * by the SocketAddress.
- * TODO add comment on all these that the network selection happens during connect
- * and may take 30 seconds
- * @deprecated Use {@code connect(String dstName, int dstPort)}
- * Using this method may result in reduced functionality.
- * @param remoteAddr the address and port of the remote host to connect to.
- * @throws IllegalArgumentException if the SocketAddress is invalid or not supported.
- * @throws IOException if the socket is already connected or an error occurs
- * while connecting
- */
- @Override
- @Deprecated
- public void connect(SocketAddress remoteAddr) throws IOException {
- if (DBG) log("connect(remoteAddr) EX DEPRECATED");
- }
-
- /**
- * Connect a duplicate socket socket to the same remote host address and port
- * as the original with a timeout parameter.
- * @param timeout the timeout value in milliseconds or 0 for infinite timeout
- * @throws IOException if the socket is already connected or an error occurs
- * while connecting
- */
- public void connect(int timeout) throws IOException {
- if (DBG) log("connect(timeout) EX");
- }
-
- /**
- * Connect a duplicate socket socket to the same remote host address and port
- * as the original.
- * @throws IOException if the socket is already connected or an error occurs
- * while connecting
- */
- public void connect() throws IOException {
- if (DBG) log("connect() EX");
- }
-
- /**
- * Closes the socket. It is not possible to reconnect or rebind to this
- * socket thereafter which means a new socket instance has to be created.
- * @throws IOException if an error occurs while closing the socket
- */
- @Override
- public synchronized void close() throws IOException {
- if (DBG) log("close() EX");
- }
-
- /**
- * Request that a new LinkSocket be created using a different radio
- * (such as WiFi or 3G) than the current LinkSocket. If a different
- * radio is available a call back will be made via {@code onBetterLinkAvail}.
- * If unable to find a better radio, application will be notified via
- * {@code onNewLinkUnavailable}
- * @see LinkSocketNotifier#onBetterLinkAvailable(LinkSocket, LinkSocket)
- * @param linkRequestReason reason for requesting a new link.
- */
- public void requestNewLink(LinkRequestReason linkRequestReason) {
- if (DBG) log("requestNewLink(linkRequestReason) EX");
- }
-
- /**
- * @deprecated LinkSocket will automatically pick the optimum interface
- * to bind to
- * @param localAddr the specific address and port on the local machine
- * to bind to
- * @throws IOException always as this method is deprecated for LinkSocket
- */
- @Override
- @Deprecated
- public void bind(SocketAddress localAddr) throws UnsupportedOperationException {
- if (DBG) log("bind(localAddr) EX throws IOException");
- throw new UnsupportedOperationException("bind is deprecated for LinkSocket");
- }
-
- /**
- * Reason codes an application can specify when requesting for a new link.
- * TODO: need better documentation
- */
- public static final class LinkRequestReason {
- /** No constructor */
- private LinkRequestReason() {}
-
- /** This link is working properly */
- public static final int LINK_PROBLEM_NONE = 0;
- /** This link has an unknown issue */
- public static final int LINK_PROBLEM_UNKNOWN = 1;
- }
-
- /**
- * Debug logging
- */
- protected static void log(String s) {
- Log.d(TAG, s);
- }
-}
diff --git a/core/java/android/net/LinkSocketNotifier.java b/core/java/android/net/LinkSocketNotifier.java
deleted file mode 100644
index e2429d8..0000000
--- a/core/java/android/net/LinkSocketNotifier.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-/**
- * Interface used to get feedback about a {@link android.net.LinkSocket}. Instance is optionally
- * passed when a LinkSocket is constructed. Multiple LinkSockets may use the same notifier.
- * @hide
- */
-public interface LinkSocketNotifier {
- /**
- * This callback function will be called if a better link
- * becomes available.
- * TODO - this shouldn't be checked for all cases - what's the conditional
- * flag?
- * If the duplicate socket is accepted, the original will be marked invalid
- * and additional use will throw exceptions.
- * @param original the original LinkSocket
- * @param duplicate the new LinkSocket that better meets the application
- * requirements
- * @return {@code true} if the application intends to use this link
- *
- * REM
- * TODO - how agressive should we be?
- * At a minimum CS tracks which LS have this turned on and tracks the requirements
- * When a new link becomes available, automatically check if any of the LinkSockets
- * will care.
- * If found, grab a refcount on the link so it doesn't go away and send notification
- * Optionally, periodically setup connection on available networks to check for better links
- * Maybe pass this info into the LinkFactories so condition changes can be acted on more quickly
- */
- public boolean onBetterLinkAvailable(LinkSocket original, LinkSocket duplicate);
-
- /**
- * This callback function will be called when a LinkSocket no longer has
- * an active link.
- * @param socket the LinkSocket that lost its link
- *
- * REM
- * NetworkStateTracker tells us it is disconnected
- * CS checks the table for LS on that link
- * CS calls each callback (need to work out p2p cross process callback)
- */
- public void onLinkLost(LinkSocket socket);
-
- /**
- * This callback function will be called when an application calls
- * requestNewLink on a LinkSocket but the LinkSocket is unable to find
- * a suitable new link.
- * @param socket the LinkSocket for which a new link was not found
- * TODO - why the diff between initial request (sync) and requestNewLink?
- *
- * REM
- * CS process of trying to find a new link must track the LS that started it
- * on failure, call callback
- */
- public void onNewLinkUnavailable(LinkSocket socket);
-
- /**
- * This callback function will be called when any of the notification-marked
- * capabilities of the LinkSocket (e.g. upstream bandwidth) have changed.
- * @param socket the linkSocet for which capabilities have changed
- * @param changedCapabilities the set of capabilities that the application
- * is interested in and have changed (with new values)
- *
- * REM
- * Maybe pass the interesting capabilities into the Links.
- * Get notified of every capability change
- * check for LinkSockets on that Link that are interested in that Capability - call them
- */
- public void onCapabilitiesChanged(LinkSocket socket, LinkCapabilities changedCapabilities);
-}
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 30b61c5..535bbe24 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -66,7 +66,6 @@
private Handler mTarget;
private Context mContext;
private LinkProperties mLinkProperties;
- private LinkCapabilities mLinkCapabilities;
private boolean mPrivateDnsRouteSet = false;
private boolean mDefaultRouteSet = false;
@@ -200,11 +199,11 @@
}
mLinkProperties.setMtu(mContext.getResources().getInteger(
com.android.internal.R.integer.config_mobile_mtu));
- mLinkCapabilities = intent.getParcelableExtra(
- PhoneConstants.DATA_LINK_CAPABILITIES_KEY);
- if (mLinkCapabilities == null) {
- loge("CONNECTED event did not supply link capabilities.");
- mLinkCapabilities = new LinkCapabilities();
+ mNetworkCapabilities = intent.getParcelableExtra(
+ PhoneConstants.DATA_NETWORK_CAPABILITIES_KEY);
+ if (mNetworkCapabilities == null) {
+ loge("CONNECTED event did not supply network capabilities.");
+ mNetworkCapabilities = new NetworkCapabilities();
}
}
@@ -316,10 +315,10 @@
Slog.d(TAG, "LinkProperties = " );
}
- if (mLinkCapabilities != null) {
- Slog.d(TAG, "LinkCapabilities = " + mLinkCapabilities);
+ if (mNetworkCapabilities != null) {
+ Slog.d(TAG, mNetworkCapabilities.toString());
} else {
- Slog.d(TAG, "LinkCapabilities = " );
+ Slog.d(TAG, "NetworkCapabilities = " );
}
}
@@ -750,14 +749,6 @@
return new LinkProperties(mLinkProperties);
}
- /**
- * @see android.net.NetworkStateTracker#getLinkCapabilities()
- */
- @Override
- public LinkCapabilities getLinkCapabilities() {
- return new LinkCapabilities(mLinkCapabilities);
- }
-
public void supplyMessenger(Messenger messenger) {
if (VDBG) log(mApnType + " got supplyMessenger");
AsyncChannel ac = new AsyncChannel();
diff --git a/core/java/android/net/LinkCapabilities.aidl b/core/java/android/net/Network.aidl
similarity index 87%
rename from core/java/android/net/LinkCapabilities.aidl
rename to core/java/android/net/Network.aidl
index df72599..73ba1af 100644
--- a/core/java/android/net/LinkCapabilities.aidl
+++ b/core/java/android/net/Network.aidl
@@ -1,6 +1,6 @@
/*
**
-** Copyright (C) 2010 The Android Open Source Project
+** Copyright (C) 2014 The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -17,5 +17,4 @@
package android.net;
-parcelable LinkCapabilities;
-
+parcelable Network;
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
new file mode 100644
index 0000000..f82bc22
--- /dev/null
+++ b/core/java/android/net/Network.java
@@ -0,0 +1,59 @@
+/*
+ * 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.os.Parcelable;
+import android.os.Parcel;
+
+
+/**
+ * Identifies the Network.
+ * @hide
+ */
+public class Network implements Parcelable {
+
+ public final int netId;
+
+ public Network(int netId) {
+ this.netId = netId;
+ }
+
+ public Network(Network that) {
+ this.netId = that.netId;
+ }
+
+ // implement the Parcelable interface
+ public int describeContents() {
+ return 0;
+ }
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(netId);
+ }
+
+ public static final Creator<Network> CREATOR =
+ new Creator<Network>() {
+ public Network createFromParcel(Parcel in) {
+ int netId = in.readInt();
+
+ return new Network(netId);
+ }
+
+ public Network[] newArray(int size) {
+ return new Network[size];
+ }
+ };
+}
diff --git a/core/java/android/net/LinkCapabilities.aidl b/core/java/android/net/NetworkCapabilities.aidl
similarity index 87%
copy from core/java/android/net/LinkCapabilities.aidl
copy to core/java/android/net/NetworkCapabilities.aidl
index df72599..cd7d71c 100644
--- a/core/java/android/net/LinkCapabilities.aidl
+++ b/core/java/android/net/NetworkCapabilities.aidl
@@ -1,6 +1,6 @@
/*
**
-** Copyright (C) 2010 The Android Open Source Project
+** Copyright (C) 2014 The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -17,5 +17,5 @@
package android.net;
-parcelable LinkCapabilities;
+parcelable NetworkCapabilities;
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
new file mode 100644
index 0000000..b783046
--- /dev/null
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -0,0 +1,328 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.lang.IllegalArgumentException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * A class representing the capabilities of a network
+ * @hide
+ */
+public final class NetworkCapabilities implements Parcelable {
+ private static final String TAG = "NetworkCapabilities";
+ private static final boolean DBG = false;
+
+
+ /**
+ * Represents the network's capabilities. If any are specified they will be satisfied
+ * by any Network that matches all of them.
+ */
+ private long mNetworkCapabilities = (1 << NET_CAPABILITY_NOT_RESTRICTED);
+
+ /**
+ * Values for NetworkCapabilities. Roughly matches/extends deprecated
+ * ConnectivityManager TYPE_*
+ */
+ public static final int NET_CAPABILITY_MMS = 0;
+ public static final int NET_CAPABILITY_SUPL = 1;
+ public static final int NET_CAPABILITY_DUN = 2;
+ public static final int NET_CAPABILITY_FOTA = 3;
+ public static final int NET_CAPABILITY_IMS = 4;
+ public static final int NET_CAPABILITY_CBS = 5;
+ public static final int NET_CAPABILITY_WIFI_P2P = 6;
+ public static final int NET_CAPABILITY_IA = 7;
+ public static final int NET_CAPABILITY_RCS = 8;
+ public static final int NET_CAPABILITY_XCAP = 9;
+ public static final int NET_CAPABILITY_EIMS = 10;
+ public static final int NET_CAPABILITY_NOT_METERED = 11;
+ public static final int NET_CAPABILITY_INTERNET = 12;
+ /** Set by default */
+ public static final int NET_CAPABILITY_NOT_RESTRICTED = 13;
+
+ private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_RESTRICTED;
+
+ public void addNetworkCapability(int networkCapability) {
+ if (networkCapability < MIN_NET_CAPABILITY ||
+ networkCapability > MAX_NET_CAPABILITY) {
+ throw new IllegalArgumentException("NetworkCapability out of range");
+ }
+ mNetworkCapabilities |= 1 << networkCapability;
+ }
+ public void removeNetworkCapability(int networkCapability) {
+ if (networkCapability < MIN_NET_CAPABILITY ||
+ networkCapability > MAX_NET_CAPABILITY) {
+ throw new IllegalArgumentException("NetworkCapability out of range");
+ }
+ mNetworkCapabilities &= ~(1 << networkCapability);
+ }
+ public Collection<Integer> getNetworkCapabilities() {
+ return enumerateBits(mNetworkCapabilities);
+ }
+ public boolean hasCapability(int networkCapability) {
+ if (networkCapability < MIN_NET_CAPABILITY ||
+ networkCapability > MAX_NET_CAPABILITY) {
+ return false;
+ }
+ return ((mNetworkCapabilities & (1 << networkCapability)) != 0);
+ }
+
+ private Collection<Integer> enumerateBits(long val) {
+ ArrayList<Integer> result = new ArrayList<Integer>();
+ int resource = 0;
+ while (val > 0) {
+ if ((val & 1) == 1) result.add(resource);
+ val = val >> 1;
+ resource++;
+ }
+ return result;
+ }
+
+ private void combineNetCapabilities(NetworkCapabilities nc) {
+ this.mNetworkCapabilities |= nc.mNetworkCapabilities;
+ }
+
+ private boolean satisfiedByNetCapabilities(NetworkCapabilities nc) {
+ return ((nc.mNetworkCapabilities & this.mNetworkCapabilities) == this.mNetworkCapabilities);
+ }
+
+ private boolean equalsNetCapabilities(NetworkCapabilities nc) {
+ return (nc.mNetworkCapabilities == this.mNetworkCapabilities);
+ }
+
+ /**
+ * Representing the transport type. Apps should generally not care about transport. A
+ * request for a fast internet connection could be satisfied by a number of different
+ * transports. If any are specified here it will be satisfied a Network that matches
+ * any of them. If a caller doesn't care about the transport it should not specify any.
+ */
+ private long mTransportTypes;
+
+ /**
+ * Values for TransportType
+ */
+ public static final int TRANSPORT_CELLULAR = 0;
+ public static final int TRANSPORT_WIFI = 1;
+ public static final int TRANSPORT_BLUETOOTH = 2;
+ public static final int TRANSPORT_ETHERNET = 3;
+
+ private static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
+ private static final int MAX_TRANSPORT = TRANSPORT_ETHERNET;
+
+ public void addTransportType(int transportType) {
+ if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
+ throw new IllegalArgumentException("TransportType out of range");
+ }
+ mTransportTypes |= 1 << transportType;
+ }
+ public void removeTransportType(int transportType) {
+ if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
+ throw new IllegalArgumentException("TransportType out of range");
+ }
+ mTransportTypes &= ~(1 << transportType);
+ }
+ public Collection<Integer> getTransportTypes() {
+ return enumerateBits(mTransportTypes);
+ }
+ public boolean hasTransport(int transportType) {
+ if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
+ return false;
+ }
+ return ((mTransportTypes & (1 << transportType)) != 0);
+ }
+
+ private void combineTransportTypes(NetworkCapabilities nc) {
+ this.mTransportTypes |= nc.mTransportTypes;
+ }
+ private boolean satisfiedByTransportTypes(NetworkCapabilities nc) {
+ return ((this.mTransportTypes == 0) ||
+ ((this.mTransportTypes & nc.mTransportTypes) != 0));
+ }
+ private boolean equalsTransportTypes(NetworkCapabilities nc) {
+ return (nc.mTransportTypes == this.mTransportTypes);
+ }
+
+ /**
+ * Passive link bandwidth. This is a rough guide of the expected peak bandwidth
+ * for the first hop on the given transport. It is not measured, but may take into account
+ * link parameters (Radio technology, allocated channels, etc).
+ */
+ private int mLinkUpBandwidthKbps;
+ private int mLinkDownBandwidthKbps;
+
+ public void setLinkUpstreamBandwidthKbps(int upKbps) {
+ mLinkUpBandwidthKbps = upKbps;
+ }
+ public int getLinkUpstreamBandwidthKbps() {
+ return mLinkUpBandwidthKbps;
+ }
+ public void setLinkDownstreamBandwidthKbps(int downKbps) {
+ mLinkDownBandwidthKbps = downKbps;
+ }
+ public int getLinkDownstreamBandwidthKbps() {
+ return mLinkDownBandwidthKbps;
+ }
+
+ private void combineLinkBandwidths(NetworkCapabilities nc) {
+ this.mLinkUpBandwidthKbps =
+ Math.max(this.mLinkUpBandwidthKbps, nc.mLinkUpBandwidthKbps);
+ this.mLinkDownBandwidthKbps =
+ Math.max(this.mLinkDownBandwidthKbps, nc.mLinkDownBandwidthKbps);
+ }
+ private boolean satisfiedByLinkBandwidths(NetworkCapabilities nc) {
+ return !(this.mLinkUpBandwidthKbps > nc.mLinkUpBandwidthKbps ||
+ this.mLinkDownBandwidthKbps > nc.mLinkDownBandwidthKbps);
+ }
+ private boolean equalsLinkBandwidths(NetworkCapabilities nc) {
+ return (this.mLinkUpBandwidthKbps == nc.mLinkUpBandwidthKbps &&
+ this.mLinkDownBandwidthKbps == nc.mLinkDownBandwidthKbps);
+ }
+
+ /**
+ * Combine a set of Capabilities to this one. Useful for coming up with the complete set
+ * {@hide}
+ */
+ public void combineCapabilities(NetworkCapabilities nc) {
+ combineNetCapabilities(nc);
+ combineTransportTypes(nc);
+ combineLinkBandwidths(nc);
+ }
+
+ /**
+ * Check if our requirements are satisfied by the given Capabilities.
+ * {@hide}
+ */
+ public boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc) {
+ return (nc != null &&
+ satisfiedByNetCapabilities(nc) &&
+ satisfiedByTransportTypes(nc) &&
+ satisfiedByLinkBandwidths(nc));
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || (obj instanceof NetworkCapabilities == false)) return false;
+ NetworkCapabilities that = (NetworkCapabilities)obj;
+ return (equalsNetCapabilities(that) &&
+ equalsTransportTypes(that) &&
+ equalsLinkBandwidths(that));
+ }
+
+ @Override
+ public int hashCode() {
+ return ((int)(mNetworkCapabilities & 0xFFFFFFFF) +
+ ((int)(mNetworkCapabilities >> 32) * 3) +
+ ((int)(mTransportTypes & 0xFFFFFFFF) * 5) +
+ ((int)(mTransportTypes >> 32) * 7) +
+ (mLinkUpBandwidthKbps * 11) +
+ (mLinkDownBandwidthKbps * 13));
+ }
+
+ public NetworkCapabilities() {
+ }
+
+ public NetworkCapabilities(NetworkCapabilities nc) {
+ if (nc != null) {
+ mNetworkCapabilities = nc.mNetworkCapabilities;
+ mTransportTypes = nc.mTransportTypes;
+ mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps;
+ mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
+ }
+ }
+
+ // Parcelable
+ public int describeContents() {
+ return 0;
+ }
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mNetworkCapabilities);
+ dest.writeLong(mTransportTypes);
+ dest.writeInt(mLinkUpBandwidthKbps);
+ dest.writeInt(mLinkDownBandwidthKbps);
+ }
+ public static final Creator<NetworkCapabilities> CREATOR =
+ new Creator<NetworkCapabilities>() {
+ public NetworkCapabilities createFromParcel(Parcel in) {
+ NetworkCapabilities netCap = new NetworkCapabilities();
+
+ netCap.mNetworkCapabilities = in.readLong();
+ netCap.mTransportTypes = in.readLong();
+ netCap.mLinkUpBandwidthKbps = in.readInt();
+ netCap.mLinkDownBandwidthKbps = in.readInt();
+ return netCap;
+ }
+ public NetworkCapabilities[] newArray(int size) {
+ return new NetworkCapabilities[size];
+ }
+ };
+
+ public String toString() {
+ Collection<Integer> types = getTransportTypes();
+ String transports = (types.size() > 0 ? " Transports: " : "");
+ Iterator<Integer> i = types.iterator();
+ while (i.hasNext()) {
+ switch (i.next()) {
+ case TRANSPORT_CELLULAR: transports += "CELLULAR"; break;
+ case TRANSPORT_WIFI: transports += "WIFI"; break;
+ case TRANSPORT_BLUETOOTH: transports += "BLUETOOTH"; break;
+ case TRANSPORT_ETHERNET: transports += "ETHERNET"; break;
+ }
+ if (i.hasNext()) transports += "|";
+ }
+
+ types = getNetworkCapabilities();
+ String capabilities = (types.size() > 0 ? " Capabilities: " : "");
+ i = types.iterator();
+ while (i.hasNext()) {
+ switch (i.next().intValue()) {
+ case NET_CAPABILITY_MMS: capabilities += "MMS"; break;
+ case NET_CAPABILITY_SUPL: capabilities += "SUPL"; break;
+ case NET_CAPABILITY_DUN: capabilities += "DUN"; break;
+ case NET_CAPABILITY_FOTA: capabilities += "FOTA"; break;
+ case NET_CAPABILITY_IMS: capabilities += "IMS"; break;
+ case NET_CAPABILITY_CBS: capabilities += "CBS"; break;
+ case NET_CAPABILITY_WIFI_P2P: capabilities += "WIFI_P2P"; break;
+ case NET_CAPABILITY_IA: capabilities += "IA"; break;
+ case NET_CAPABILITY_RCS: capabilities += "RCS"; break;
+ case NET_CAPABILITY_XCAP: capabilities += "XCAP"; break;
+ case NET_CAPABILITY_EIMS: capabilities += "EIMS"; break;
+ case NET_CAPABILITY_NOT_METERED: capabilities += "NOT_METERED"; break;
+ case NET_CAPABILITY_INTERNET: capabilities += "INTERNET"; break;
+ case NET_CAPABILITY_NOT_RESTRICTED: capabilities += "NOT_RESTRICTED"; break;
+ }
+ if (i.hasNext()) capabilities += "&";
+ }
+
+ String upBand = ((mLinkUpBandwidthKbps > 0) ? " LinkUpBandwidth>=" +
+ mLinkUpBandwidthKbps + "Kbps" : "");
+ String dnBand = ((mLinkDownBandwidthKbps > 0) ? " LinkDnBandwidth>=" +
+ mLinkDownBandwidthKbps + "Kbps" : "");
+
+ return "NetworkCapabilities: [" + transports + capabilities + upBand + dnBand + "]";
+ }
+}
diff --git a/core/java/android/net/NetworkRequest.aidl b/core/java/android/net/NetworkRequest.aidl
new file mode 100644
index 0000000..508defc
--- /dev/null
+++ b/core/java/android/net/NetworkRequest.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.net;
+
+parcelable NetworkRequest;
+
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
new file mode 100644
index 0000000..7e3a06d
--- /dev/null
+++ b/core/java/android/net/NetworkRequest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @hide
+ */
+public class NetworkRequest implements Parcelable {
+ public final NetworkCapabilities networkCapabilities;
+ public final int requestId;
+ public final boolean legacy;
+ private static final AtomicInteger sRequestId = new AtomicInteger();
+
+ public NetworkRequest(NetworkCapabilities nc) {
+ this(nc, false, sRequestId.incrementAndGet());
+ }
+
+ public NetworkRequest(NetworkCapabilities nc, boolean legacy) {
+ this(nc, legacy, sRequestId.incrementAndGet());
+ }
+
+ private NetworkRequest(NetworkCapabilities nc, boolean legacy, int rId) {
+ requestId = rId;
+ networkCapabilities = nc;
+ this.legacy = legacy;
+ }
+
+ // implement the Parcelable interface
+ public int describeContents() {
+ return 0;
+ }
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(networkCapabilities, flags);
+ dest.writeInt(legacy ? 1 : 0);
+ dest.writeInt(requestId);
+ }
+ public static final Creator<NetworkRequest> CREATOR =
+ new Creator<NetworkRequest>() {
+ public NetworkRequest createFromParcel(Parcel in) {
+ NetworkCapabilities nc = (NetworkCapabilities)in.readParcelable(null);
+ boolean legacy = (in.readInt() == 1);
+ int requestId = in.readInt();
+ return new NetworkRequest(nc, legacy, requestId);
+ }
+ public NetworkRequest[] newArray(int size) {
+ return new NetworkRequest[size];
+ }
+ };
+
+ public String toString() {
+ return "NetworkRequest [ id=" + requestId + ", legacy=" + legacy + ", " +
+ networkCapabilities.toString() + " ]";
+ }
+
+ public boolean equals(Object obj) {
+ if (obj instanceof NetworkRequest == false) return false;
+ NetworkRequest that = (NetworkRequest)obj;
+ return (that.legacy == this.legacy &&
+ that.requestId == this.requestId &&
+ ((that.networkCapabilities == null && this.networkCapabilities == null) ||
+ (that.networkCapabilities != null &&
+ that.networkCapabilities.equals(this.networkCapabilities))));
+ }
+
+ public int hashCode() {
+ return requestId + (legacy ? 1013 : 2026) + (networkCapabilities.hashCode() * 1051);
+ }
+}
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index fbe1f82..2e0e9e4 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -28,21 +28,21 @@
public final NetworkInfo networkInfo;
public final LinkProperties linkProperties;
- public final LinkCapabilities linkCapabilities;
+ public final NetworkCapabilities networkCapabilities;
/** Currently only used by testing. */
public final String subscriberId;
public final String networkId;
public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
- LinkCapabilities linkCapabilities) {
- this(networkInfo, linkProperties, linkCapabilities, null, null);
+ NetworkCapabilities networkCapabilities) {
+ this(networkInfo, linkProperties, networkCapabilities, null, null);
}
public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
- LinkCapabilities linkCapabilities, String subscriberId, String networkId) {
+ NetworkCapabilities networkCapabilities, String subscriberId, String networkId) {
this.networkInfo = networkInfo;
this.linkProperties = linkProperties;
- this.linkCapabilities = linkCapabilities;
+ this.networkCapabilities = networkCapabilities;
this.subscriberId = subscriberId;
this.networkId = networkId;
}
@@ -50,7 +50,7 @@
public NetworkState(Parcel in) {
networkInfo = in.readParcelable(null);
linkProperties = in.readParcelable(null);
- linkCapabilities = in.readParcelable(null);
+ networkCapabilities = in.readParcelable(null);
subscriberId = in.readString();
networkId = in.readString();
}
@@ -64,7 +64,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeParcelable(networkInfo, flags);
out.writeParcelable(linkProperties, flags);
- out.writeParcelable(linkCapabilities, flags);
+ out.writeParcelable(networkCapabilities, flags);
out.writeString(subscriberId);
out.writeString(networkId);
}
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index c49b1d1..35500cc 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -111,12 +111,9 @@
public LinkProperties getLinkProperties();
/**
- * A capability is an Integer/String pair, the capabilities
- * are defined in the class LinkSocket#Key.
- *
* @return a copy of this connections capabilities, may be empty but never null.
*/
- public LinkCapabilities getLinkCapabilities();
+ public NetworkCapabilities getNetworkCapabilities();
/**
* Get interesting information about this network link
@@ -250,4 +247,14 @@
*/
public void stopSampling(SamplingDataTracker.SamplingSnapshot s);
+ /*
+ * Record the current netId
+ */
+ public void setNetId(int netId);
+
+ /*
+ * ?
+ */
+ public Network getNetwork();
+
}
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index daf0065..6a78c29 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -273,21 +273,19 @@
String host = null;
String port = null;
String exclList = null;
- String pacFileUrl = null;
+ Uri pacFileUrl = Uri.EMPTY;
if (p != null) {
host = p.getHost();
port = Integer.toString(p.getPort());
exclList = p.getExclusionListAsString();
- if (p.getPacFileUrl() != null) {
- pacFileUrl = p.getPacFileUrl().toString();
- }
+ pacFileUrl = p.getPacFileUrl();
}
setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
}
/** @hide */
public static final void setHttpProxySystemProperty(String host, String port, String exclList,
- String pacFileUrl) {
+ Uri pacFileUrl) {
if (exclList != null) exclList = exclList.replace(",", "|");
if (false) Log.d(TAG, "setHttpProxySystemProperty :"+host+":"+port+" - "+exclList);
if (host != null) {
@@ -311,7 +309,7 @@
System.clearProperty("http.nonProxyHosts");
System.clearProperty("https.nonProxyHosts");
}
- if (!TextUtils.isEmpty(pacFileUrl)) {
+ if (!Uri.EMPTY.equals(pacFileUrl)) {
ProxySelector.setDefault(new PacProxySelector());
} else {
ProxySelector.setDefault(sDefaultProxySelector);
diff --git a/core/java/android/net/ProxyDataTracker.java b/core/java/android/net/ProxyDataTracker.java
index 461e8b8..4973b3d 100644
--- a/core/java/android/net/ProxyDataTracker.java
+++ b/core/java/android/net/ProxyDataTracker.java
@@ -104,7 +104,7 @@
public ProxyDataTracker() {
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_PROXY, 0, NETWORK_TYPE, "");
mLinkProperties = new LinkProperties();
- mLinkCapabilities = new LinkCapabilities();
+ mNetworkCapabilities = new NetworkCapabilities();
mNetworkInfo.setIsAvailable(true);
try {
mLinkProperties.addDns(InetAddress.getByName(DNS1));
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index b40941f..991d9da 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -44,7 +44,7 @@
private String mExclusionList;
private String[] mParsedExclusionList;
- private String mPacFileUrl;
+ private Uri mPacFileUrl;
/**
*@hide
*/
@@ -85,7 +85,7 @@
* at the specified URL.
*/
public static ProxyInfo buildPacProxy(Uri pacUri) {
- return new ProxyInfo(pacUri.toString());
+ return new ProxyInfo(pacUri);
}
/**
@@ -96,6 +96,21 @@
mHost = host;
mPort = port;
setExclusionList(exclList);
+ mPacFileUrl = Uri.EMPTY;
+ }
+
+ /**
+ * Create a ProxyProperties that points at a PAC URL.
+ * @hide
+ */
+ public ProxyInfo(Uri pacFileUrl) {
+ mHost = LOCAL_HOST;
+ mPort = LOCAL_PORT;
+ setExclusionList(LOCAL_EXCL_LIST);
+ if (pacFileUrl == null) {
+ throw new NullPointerException();
+ }
+ mPacFileUrl = pacFileUrl;
}
/**
@@ -106,17 +121,20 @@
mHost = LOCAL_HOST;
mPort = LOCAL_PORT;
setExclusionList(LOCAL_EXCL_LIST);
- mPacFileUrl = pacFileUrl;
+ mPacFileUrl = Uri.parse(pacFileUrl);
}
/**
* Only used in PacManager after Local Proxy is bound.
* @hide
*/
- public ProxyInfo(String pacFileUrl, int localProxyPort) {
+ public ProxyInfo(Uri pacFileUrl, int localProxyPort) {
mHost = LOCAL_HOST;
mPort = localProxyPort;
setExclusionList(LOCAL_EXCL_LIST);
+ if (pacFileUrl == null) {
+ throw new NullPointerException();
+ }
mPacFileUrl = pacFileUrl;
}
@@ -125,7 +143,7 @@
mPort = port;
mExclusionList = exclList;
mParsedExclusionList = parsedExclList;
- mPacFileUrl = null;
+ mPacFileUrl = Uri.EMPTY;
}
// copy constructor instead of clone
@@ -137,6 +155,9 @@
mHost = source.getHost();
mPort = source.getPort();
mPacFileUrl = source.mPacFileUrl;
+ if (mPacFileUrl == null) {
+ mPacFileUrl = Uri.EMPTY;
+ }
mExclusionList = source.getExclusionListAsString();
mParsedExclusionList = source.mParsedExclusionList;
}
@@ -158,10 +179,7 @@
* no PAC script.
*/
public Uri getPacFileUrl() {
- if (TextUtils.isEmpty(mPacFileUrl)) {
- return null;
- }
- return Uri.parse(mPacFileUrl);
+ return mPacFileUrl;
}
/**
@@ -210,7 +228,7 @@
* @hide
*/
public boolean isValid() {
- if (!TextUtils.isEmpty(mPacFileUrl)) return true;
+ if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost,
mPort == 0 ? "" : Integer.toString(mPort),
mExclusionList == null ? "" : mExclusionList);
@@ -234,7 +252,7 @@
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- if (mPacFileUrl != null) {
+ if (!Uri.EMPTY.equals(mPacFileUrl)) {
sb.append("PAC Script: ");
sb.append(mPacFileUrl);
} else if (mHost != null) {
@@ -257,13 +275,15 @@
ProxyInfo p = (ProxyInfo)o;
// If PAC URL is present in either then they must be equal.
// Other parameters will only be for fall back.
- if (!TextUtils.isEmpty(mPacFileUrl)) {
+ if (!Uri.EMPTY.equals(mPacFileUrl)) {
return mPacFileUrl.equals(p.getPacFileUrl()) && mPort == p.mPort;
}
- if (!TextUtils.isEmpty(p.mPacFileUrl)) {
+ if (!Uri.EMPTY.equals(p.mPacFileUrl)) {
return false;
}
- if (mExclusionList != null && !mExclusionList.equals(p.getExclusionListAsString())) return false;
+ if (mExclusionList != null && !mExclusionList.equals(p.getExclusionListAsString())) {
+ return false;
+ }
if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) {
return false;
}
@@ -296,9 +316,9 @@
* @hide
*/
public void writeToParcel(Parcel dest, int flags) {
- if (mPacFileUrl != null) {
+ if (!Uri.EMPTY.equals(mPacFileUrl)) {
dest.writeByte((byte)1);
- dest.writeString(mPacFileUrl);
+ mPacFileUrl.writeToParcel(dest, 0);
dest.writeInt(mPort);
return;
} else {
@@ -325,7 +345,7 @@
String host = null;
int port = 0;
if (in.readByte() != 0) {
- String url = in.readString();
+ Uri url = Uri.CREATOR.createFromParcel(in);
int localPort = in.readInt();
return new ProxyInfo(url, localPort);
}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index f5ff185..9e03f95 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -455,4 +455,14 @@
* Check whether the mobile radio is currently active.
*/
boolean isNetworkActive();
+
+ /**
+ * setup a new network
+ */
+ void createNetwork(int netId, String iface);
+
+ /**
+ * remove a network
+ */
+ void removeNetwork(int netId);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1847b55..e1fd46e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3838,22 +3838,11 @@
/**
* Setting that specifies whether display color inversion is enabled.
- *
- * @hide
*/
public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED =
"accessibility_display_inversion_enabled";
/**
- * Integer property that specifies the type of color inversion to
- * perform. Valid values are defined in AccessibilityManager.
- *
- * @hide
- */
- public static final String ACCESSIBILITY_DISPLAY_INVERSION =
- "accessibility_display_inversion";
-
- /**
* Setting that specifies whether the quick setting tile for display
* color space adjustment is enabled.
*
@@ -3881,44 +3870,6 @@
"accessibility_display_daltonizer";
/**
- * Setting that specifies whether the quick setting tile for display
- * contrast enhancement is enabled.
- *
- * @hide
- */
- public static final String ACCESSIBILITY_DISPLAY_CONTRAST_QUICK_SETTING_ENABLED =
- "accessibility_display_contrast_quick_setting_enabled";
-
- /**
- * Setting that specifies whether display contrast enhancement is
- * enabled.
- *
- * @hide
- */
- public static final String ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED =
- "accessibility_display_contrast_enabled";
-
- /**
- * Floating point property that specifies display contrast adjustment.
- * Valid range is [0, ...] where 0 is gray, 1 is normal, and higher
- * values indicate enhanced contrast.
- *
- * @hide
- */
- public static final String ACCESSIBILITY_DISPLAY_CONTRAST =
- "accessibility_display_contrast";
-
- /**
- * Floating point property that specifies display brightness adjustment.
- * Valid range is [-1, 1] where -1 is black, 0 is default, and 1 is
- * white.
- *
- * @hide
- */
- public static final String ACCESSIBILITY_DISPLAY_BRIGHTNESS =
- "accessibility_display_brightness";
-
- /**
* The timout for considering a press to be a long press in milliseconds.
* @hide
*/
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index d4b29d8..d4919eb 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -17,11 +17,15 @@
package android.service.notification;
import android.service.notification.StatusBarNotification;
+import android.service.notification.NotificationOrderUpdate;
/** @hide */
oneway interface INotificationListener
{
- void onListenerConnected(in String[] notificationKeys);
- void onNotificationPosted(in StatusBarNotification notification);
- void onNotificationRemoved(in StatusBarNotification notification);
+ void onListenerConnected(in NotificationOrderUpdate update);
+ void onNotificationPosted(in StatusBarNotification notification,
+ in NotificationOrderUpdate update);
+ void onNotificationRemoved(in StatusBarNotification notification,
+ in NotificationOrderUpdate update);
+ void onNotificationOrderUpdate(in NotificationOrderUpdate update);
}
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 3673f03..a94f45a 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -22,10 +22,13 @@
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
+import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserHandle;
import android.util.Log;
+import java.util.Comparator;
+import java.util.HashMap;
+
/**
* A service that receives calls from the system when new notifications are posted or removed.
* <p>To extend this class, you must declare the service in your manifest file with
@@ -46,6 +49,7 @@
+ "[" + getClass().getSimpleName() + "]";
private INotificationListenerWrapper mWrapper = null;
+ private String[] mNotificationKeys;
private INotificationManager mNoMan;
@@ -95,6 +99,15 @@
// optional
}
+ /**
+ * Implement this method to be notified when the notification order cahnges.
+ *
+ * Call {@link #getOrderedNotificationKeys()} to retrieve the new order.
+ */
+ public void onNotificationOrderUpdate() {
+ // optional
+ }
+
private final INotificationManager getNotificationInterface() {
if (mNoMan == null) {
mNoMan = INotificationManager.Stub.asInterface(
@@ -202,7 +215,7 @@
* Request the list of outstanding notifications (that is, those that are visible to the
* current user). Useful when you don't know what's already been posted.
*
- * @return An array of active notifications.
+ * @return An array of active notifications, sorted in natural order.
*/
public StatusBarNotification[] getActiveNotifications() {
return getActiveNotifications(null /*all*/);
@@ -213,7 +226,8 @@
* current user). Useful when you don't know what's already been posted.
*
* @param keys A specific list of notification keys, or {@code null} for all.
- * @return An array of active notifications.
+ * @return An array of active notifications, sorted in natural order
+ * if {@code keys} is {@code null}.
*/
public StatusBarNotification[] getActiveNotifications(String[] keys) {
if (!isBound()) return null;
@@ -226,21 +240,15 @@
}
/**
- * Request the list of outstanding notification keys(that is, those that are visible to the
- * current user). You can use the notification keys for subsequent retrieval via
+ * Request the list of notification keys in their current natural order.
+ * You can use the notification keys for subsequent retrieval via
* {@link #getActiveNotifications(String[]) or dismissal via
* {@link #cancelNotifications(String[]).
*
- * @return An array of active notification keys.
+ * @return An array of active notification keys, in their natural order.
*/
- public String[] getActiveNotificationKeys() {
- if (!isBound()) return null;
- try {
- return getNotificationInterface().getActiveNotificationKeysFromListener(mWrapper);
- } catch (android.os.RemoteException ex) {
- Log.v(TAG, "Unable to contact notification manager", ex);
- }
- return null;
+ public String[] getOrderedNotificationKeys() {
+ return mNotificationKeys;
}
@Override
@@ -261,28 +269,60 @@
private class INotificationListenerWrapper extends INotificationListener.Stub {
@Override
- public void onNotificationPosted(StatusBarNotification sbn) {
+ public void onNotificationPosted(StatusBarNotification sbn,
+ NotificationOrderUpdate update) {
try {
- NotificationListenerService.this.onNotificationPosted(sbn);
+ // protect subclass from concurrent modifications of (@link mNotificationKeys}.
+ synchronized (mWrapper) {
+ updateNotificationKeys(update);
+ NotificationListenerService.this.onNotificationPosted(sbn);
+ }
} catch (Throwable t) {
- Log.w(TAG, "Error running onNotificationPosted", t);
+ Log.w(TAG, "Error running onOrderedNotificationPosted", t);
}
}
@Override
- public void onNotificationRemoved(StatusBarNotification sbn) {
+ public void onNotificationRemoved(StatusBarNotification sbn,
+ NotificationOrderUpdate update) {
try {
- NotificationListenerService.this.onNotificationRemoved(sbn);
+ // protect subclass from concurrent modifications of (@link mNotificationKeys}.
+ synchronized (mWrapper) {
+ updateNotificationKeys(update);
+ NotificationListenerService.this.onNotificationRemoved(sbn);
+ }
} catch (Throwable t) {
Log.w(TAG, "Error running onNotificationRemoved", t);
}
}
@Override
- public void onListenerConnected(String[] notificationKeys) {
+ public void onListenerConnected(NotificationOrderUpdate update) {
try {
- NotificationListenerService.this.onListenerConnected(notificationKeys);
+ // protect subclass from concurrent modifications of (@link mNotificationKeys}.
+ synchronized (mWrapper) {
+ updateNotificationKeys(update);
+ NotificationListenerService.this.onListenerConnected(mNotificationKeys);
+ }
} catch (Throwable t) {
Log.w(TAG, "Error running onListenerConnected", t);
}
}
+ @Override
+ public void onNotificationOrderUpdate(NotificationOrderUpdate update)
+ throws RemoteException {
+ try {
+ // protect subclass from concurrent modifications of (@link mNotificationKeys}.
+ synchronized (mWrapper) {
+ updateNotificationKeys(update);
+ NotificationListenerService.this.onNotificationOrderUpdate();
+ }
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onNotificationOrderUpdate", t);
+ }
+ }
+ }
+
+ private void updateNotificationKeys(NotificationOrderUpdate update) {
+ // TODO: avoid garbage by comparing the lists
+ mNotificationKeys = update.getOrderedKeys();
}
}
diff --git a/core/java/android/service/notification/NotificationOrderUpdate.aidl b/core/java/android/service/notification/NotificationOrderUpdate.aidl
new file mode 100644
index 0000000..5d50641
--- /dev/null
+++ b/core/java/android/service/notification/NotificationOrderUpdate.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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 NotificationOrderUpdate;
diff --git a/core/java/android/service/notification/NotificationOrderUpdate.java b/core/java/android/service/notification/NotificationOrderUpdate.java
new file mode 100644
index 0000000..20e19a3
--- /dev/null
+++ b/core/java/android/service/notification/NotificationOrderUpdate.java
@@ -0,0 +1,62 @@
+/*
+ * 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;
+
+public class NotificationOrderUpdate implements Parcelable {
+ // TODO replace this with an update instead of the whole array
+ private final String[] mKeys;
+
+ /** @hide */
+ public NotificationOrderUpdate(String[] keys) {
+ this.mKeys = keys;
+ }
+
+ public NotificationOrderUpdate(Parcel in) {
+ this.mKeys = in.readStringArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeStringArray(this.mKeys);
+ }
+
+ public static final Parcelable.Creator<NotificationOrderUpdate> CREATOR
+ = new Parcelable.Creator<NotificationOrderUpdate>() {
+ public NotificationOrderUpdate createFromParcel(Parcel parcel) {
+ return new NotificationOrderUpdate(parcel);
+ }
+
+ public NotificationOrderUpdate[] newArray(int size) {
+ return new NotificationOrderUpdate[size];
+ }
+ };
+
+ /**
+ * @hide
+ * @return ordered list of keys
+ */
+ String[] getOrderedKeys() {
+ return mKeys;
+ }
+}
diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java
index 4f996cd..9b929a3 100644
--- a/core/java/android/speech/tts/TtsEngines.java
+++ b/core/java/android/speech/tts/TtsEngines.java
@@ -307,6 +307,24 @@
}
/**
+ * True if a given TTS engine uses the default phone locale as a default locale. Attempts to
+ * read the value from {@link Settings.Secure#TTS_DEFAULT_LOCALE}, failing which the
+ * old style value from {@link Settings.Secure#TTS_DEFAULT_LANG} is read. If
+ * both these values are empty, this methods returns true.
+ *
+ * @param engineName the engine to return the locale for.
+ */
+ public boolean isLocaleSetToDefaultForEngine(String engineName) {
+ return (TextUtils.isEmpty(parseEnginePrefFromList(
+ getString(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_LOCALE),
+ engineName)) &&
+ TextUtils.isEmpty(
+ Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.TTS_DEFAULT_LANG)));
+ }
+
+
+ /**
* Parses a locale preference value delimited by {@link #LOCALE_DELIMITER}.
* Varies from {@link String#split} in that it will always return an array
* of length 3 with non null values.
diff --git a/core/java/android/tv/ITvInputClient.aidl b/core/java/android/tv/ITvInputClient.aidl
index 538f8a1..ac83356 100644
--- a/core/java/android/tv/ITvInputClient.aidl
+++ b/core/java/android/tv/ITvInputClient.aidl
@@ -26,6 +26,7 @@
* @hide
*/
oneway interface ITvInputClient {
- void onSessionCreated(in ComponentName name, IBinder token, in InputChannel channel, int seq);
- void onAvailabilityChanged(in ComponentName name, boolean isAvailable);
+ void onSessionCreated(in String inputId, IBinder token, in InputChannel channel, int seq);
+ void onAvailabilityChanged(in String inputId, boolean isAvailable);
+ void onSessionReleased(int seq);
}
diff --git a/core/java/android/tv/ITvInputManager.aidl b/core/java/android/tv/ITvInputManager.aidl
index a4c99e4..b756aba 100644
--- a/core/java/android/tv/ITvInputManager.aidl
+++ b/core/java/android/tv/ITvInputManager.aidl
@@ -30,12 +30,12 @@
interface ITvInputManager {
List<TvInputInfo> getTvInputList(int userId);
- boolean getAvailability(in ITvInputClient client, in ComponentName name, int userId);
+ boolean getAvailability(in ITvInputClient client, in String inputId, int userId);
- void registerCallback(in ITvInputClient client, in ComponentName name, int userId);
- void unregisterCallback(in ITvInputClient client, in ComponentName name, int userId);
+ void registerCallback(in ITvInputClient client, in String inputId, int userId);
+ void unregisterCallback(in ITvInputClient client, in String inputId, int userId);
- void createSession(in ITvInputClient client, in ComponentName name, int seq, int userId);
+ void createSession(in ITvInputClient client, in String inputId, int seq, int userId);
void releaseSession(in IBinder sessionToken, int userId);
void setSurface(in IBinder sessionToken, in Surface surface, int userId);
diff --git a/core/java/android/tv/ITvInputServiceCallback.aidl b/core/java/android/tv/ITvInputServiceCallback.aidl
index e535c81..71fc780 100644
--- a/core/java/android/tv/ITvInputServiceCallback.aidl
+++ b/core/java/android/tv/ITvInputServiceCallback.aidl
@@ -24,5 +24,5 @@
* @hide
*/
oneway interface ITvInputServiceCallback {
- void onAvailabilityChanged(in ComponentName name, boolean isAvailable);
+ void onAvailabilityChanged(in String inputId, boolean isAvailable);
}
diff --git a/core/java/android/tv/TvInputInfo.java b/core/java/android/tv/TvInputInfo.java
index 90e4177..50462cc 100644
--- a/core/java/android/tv/TvInputInfo.java
+++ b/core/java/android/tv/TvInputInfo.java
@@ -39,7 +39,7 @@
public TvInputInfo(ResolveInfo service) {
mService = service;
ServiceInfo si = service.serviceInfo;
- mId = new ComponentName(si.packageName, si.name).flattenToShortString();
+ mId = generateInputIdForComponenetName(new ComponentName(si.packageName, si.name));
}
/**
@@ -75,7 +75,7 @@
* Loads the user-displayed label for this TV input service.
*
* @param pm Supplies a PackageManager used to load the TV input's resources.
- * @return Returns a CharSequence containing the TV input's label. If the TV input does not have
+ * @return a CharSequence containing the TV input's label. If the TV input does not have
* a label, its name is returned.
*/
public CharSequence loadLabel(PackageManager pm) {
@@ -128,6 +128,17 @@
}
/**
+ * Used to generate an input id from a ComponentName.
+ *
+ * @param name the component name for generating an input id.
+ * @return the generated input id for the given {@code name}.
+ * @hide
+ */
+ public static final String generateInputIdForComponenetName(ComponentName name) {
+ return name.flattenToShortString();
+ }
+
+ /**
* Used to make this class parcelable.
*
* @hide
diff --git a/core/java/android/tv/TvInputManager.java b/core/java/android/tv/TvInputManager.java
index 7b9b1fb..c5f179a 100644
--- a/core/java/android/tv/TvInputManager.java
+++ b/core/java/android/tv/TvInputManager.java
@@ -16,7 +16,6 @@
package android.tv;
-import android.content.ComponentName;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Handler;
@@ -50,15 +49,15 @@
private final ITvInputManager mService;
// A mapping from an input to the list of its TvInputListenerRecords.
- private final Map<ComponentName, List<TvInputListenerRecord>> mTvInputListenerRecordsMap =
- new HashMap<ComponentName, List<TvInputListenerRecord>>();
+ private final Map<String, List<TvInputListenerRecord>> mTvInputListenerRecordsMap =
+ new HashMap<String, List<TvInputListenerRecord>>();
- // A mapping from the sequence number of a session to its SessionCreateCallbackRecord.
- private final SparseArray<SessionCreateCallbackRecord> mSessionCreateCallbackRecordMap =
- new SparseArray<SessionCreateCallbackRecord>();
+ // A mapping from the sequence number of a session to its SessionCallbackRecord.
+ private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap =
+ new SparseArray<SessionCallbackRecord>();
// A sequence number for the next session to be created. Should be protected by a lock
- // {@code mSessionCreateCallbackRecordMap}.
+ // {@code mSessionCallbackRecordMap}.
private int mNextSeq;
private final ITvInputClient mClient;
@@ -68,31 +67,52 @@
/**
* Interface used to receive the created session.
*/
- public interface SessionCreateCallback {
+ public abstract static class SessionCallback {
/**
* This is called after {@link TvInputManager#createSession} has been processed.
*
* @param session A {@link TvInputManager.Session} instance created. This can be
* {@code null} if the creation request failed.
*/
- void onSessionCreated(Session session);
+ public void onSessionCreated(Session session) {
+ }
+
+ /**
+ * This is called when {@link TvInputManager.Session} is released.
+ * This typically happens when the process hosting the session has crashed or been killed.
+ *
+ * @param session A {@link TvInputManager.Session} instance released.
+ */
+ public void onSessionReleased(Session session) {
+ }
}
- private static final class SessionCreateCallbackRecord {
- private final SessionCreateCallback mSessionCreateCallback;
+ private static final class SessionCallbackRecord {
+ private final SessionCallback mSessionCallback;
private final Handler mHandler;
+ private Session mSession;
- public SessionCreateCallbackRecord(SessionCreateCallback sessionCreateCallback,
+ public SessionCallbackRecord(SessionCallback sessionCallback,
Handler handler) {
- mSessionCreateCallback = sessionCreateCallback;
+ mSessionCallback = sessionCallback;
mHandler = handler;
}
public void postSessionCreated(final Session session) {
+ mSession = session;
mHandler.post(new Runnable() {
@Override
public void run() {
- mSessionCreateCallback.onSessionCreated(session);
+ mSessionCallback.onSessionCreated(session);
+ }
+ });
+ }
+
+ public void postSessionReleased() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onSessionReleased(mSession);
}
});
}
@@ -105,12 +125,11 @@
/**
* This is called when the availability status of a given TV input is changed.
*
- * @param name {@link ComponentName} of {@link android.app.Service} that implements the
- * given TV input.
+ * @param inputId the id of the TV input.
* @param isAvailable {@code true} if the given TV input is available to show TV programs.
* {@code false} otherwise.
*/
- public void onAvailabilityChanged(ComponentName name, boolean isAvailable) {
+ public void onAvailabilityChanged(String inputId, boolean isAvailable) {
}
}
@@ -127,11 +146,11 @@
return mListener;
}
- public void postAvailabilityChanged(final ComponentName name, final boolean isAvailable) {
+ public void postAvailabilityChanged(final String inputId, final boolean isAvailable) {
mHandler.post(new Runnable() {
@Override
public void run() {
- mListener.onAvailabilityChanged(name, isAvailable);
+ mListener.onAvailabilityChanged(inputId, isAvailable);
}
});
}
@@ -145,34 +164,48 @@
mUserId = userId;
mClient = new ITvInputClient.Stub() {
@Override
- public void onSessionCreated(ComponentName name, IBinder token, InputChannel channel,
+ public void onSessionCreated(String inputId, IBinder token, InputChannel channel,
int seq) {
- synchronized (mSessionCreateCallbackRecordMap) {
- SessionCreateCallbackRecord record = mSessionCreateCallbackRecordMap.get(seq);
- mSessionCreateCallbackRecordMap.delete(seq);
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
if (record == null) {
Log.e(TAG, "Callback not found for " + token);
return;
}
Session session = null;
if (token != null) {
- session = new Session(token, channel, mService, mUserId);
+ session = new Session(token, channel, mService, mUserId, seq,
+ mSessionCallbackRecordMap);
}
record.postSessionCreated(session);
}
}
@Override
- public void onAvailabilityChanged(ComponentName name, boolean isAvailable) {
+ public void onSessionReleased(int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ mSessionCallbackRecordMap.delete(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq:" + seq);
+ return;
+ }
+ record.mSession.releaseInternal();
+ record.postSessionReleased();
+ }
+ }
+
+ @Override
+ public void onAvailabilityChanged(String inputId, boolean isAvailable) {
synchronized (mTvInputListenerRecordsMap) {
- List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(name);
+ List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
if (records == null) {
// Silently ignore - no listener is registered yet.
return;
}
int recordsCount = records.size();
for (int i = 0; i < recordsCount; i++) {
- records.get(i).postAvailabilityChanged(name, isAvailable);
+ records.get(i).postAvailabilityChanged(inputId, isAvailable);
}
}
}
@@ -195,24 +228,23 @@
/**
* Returns the availability of a given TV input.
*
- * @param name {@link ComponentName} of {@link android.app.Service} that implements the given TV
- * input.
+ * @param inputId the id of the TV input.
* @throws IllegalArgumentException if the argument is {@code null}.
* @throws IllegalStateException If there is no {@link TvInputListener} registered on the given
* TV input.
*/
- public boolean getAvailability(ComponentName name) {
- if (name == null) {
- throw new IllegalArgumentException("name cannot be null");
+ public boolean getAvailability(String inputId) {
+ if (inputId == null) {
+ throw new IllegalArgumentException("id cannot be null");
}
synchronized (mTvInputListenerRecordsMap) {
- List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(name);
+ List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
if (records == null || records.size() == 0) {
throw new IllegalStateException("At least one listener should be registered.");
}
}
try {
- return mService.getAvailability(mClient, name, mUserId);
+ return mService.getAvailability(mClient, inputId, mUserId);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -221,15 +253,14 @@
/**
* Registers a {@link TvInputListener} for a given TV input.
*
- * @param name {@link ComponentName} of {@link android.app.Service} that implements the given TV
- * input.
+ * @param inputId the id of the TV input.
* @param listener a listener used to monitor status of the given TV input.
* @param handler a {@link Handler} that the status change will be delivered to.
* @throws IllegalArgumentException if any of the arguments is {@code null}.
*/
- public void registerListener(ComponentName name, TvInputListener listener, Handler handler) {
- if (name == null) {
- throw new IllegalArgumentException("name cannot be null");
+ public void registerListener(String inputId, TvInputListener listener, Handler handler) {
+ if (inputId == null) {
+ throw new IllegalArgumentException("id cannot be null");
}
if (listener == null) {
throw new IllegalArgumentException("listener cannot be null");
@@ -238,12 +269,12 @@
throw new IllegalArgumentException("handler cannot be null");
}
synchronized (mTvInputListenerRecordsMap) {
- List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(name);
+ List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
if (records == null) {
records = new ArrayList<TvInputListenerRecord>();
- mTvInputListenerRecordsMap.put(name, records);
+ mTvInputListenerRecordsMap.put(inputId, records);
try {
- mService.registerCallback(mClient, name, mUserId);
+ mService.registerCallback(mClient, inputId, mUserId);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -255,22 +286,21 @@
/**
* Unregisters the existing {@link TvInputListener} for a given TV input.
*
- * @param name {@link ComponentName} of {@link android.app.Service} that implements the given TV
- * input.
+ * @param inputId the id of the TV input.
* @param listener the existing listener to remove for the given TV input.
* @throws IllegalArgumentException if any of the arguments is {@code null}.
*/
- public void unregisterListener(ComponentName name, final TvInputListener listener) {
- if (name == null) {
- throw new IllegalArgumentException("name cannot be null");
+ public void unregisterListener(String inputId, final TvInputListener listener) {
+ if (inputId == null) {
+ throw new IllegalArgumentException("id cannot be null");
}
if (listener == null) {
throw new IllegalArgumentException("listener cannot be null");
}
synchronized (mTvInputListenerRecordsMap) {
- List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(name);
+ List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
if (records == null) {
- Log.e(TAG, "No listener found for " + name.getClassName());
+ Log.e(TAG, "No listener found for " + inputId);
return;
}
for (Iterator<TvInputListenerRecord> it = records.iterator(); it.hasNext();) {
@@ -281,11 +311,11 @@
}
if (records.isEmpty()) {
try {
- mService.unregisterCallback(mClient, name, mUserId);
+ mService.unregisterCallback(mClient, inputId, mUserId);
} catch (RemoteException e) {
throw new RuntimeException(e);
} finally {
- mTvInputListenerRecordsMap.remove(name);
+ mTvInputListenerRecordsMap.remove(inputId);
}
}
}
@@ -298,16 +328,15 @@
* the given TV input.
* </p>
*
- * @param name {@link ComponentName} of {@link android.app.Service} that implements the given TV
- * input.
+ * @param inputId the id of the TV input.
* @param callback a callback used to receive the created session.
* @param handler a {@link Handler} that the session creation will be delivered to.
* @throws IllegalArgumentException if any of the arguments is {@code null}.
*/
- public void createSession(ComponentName name, final SessionCreateCallback callback,
+ public void createSession(String inputId, final SessionCallback callback,
Handler handler) {
- if (name == null) {
- throw new IllegalArgumentException("name cannot be null");
+ if (inputId == null) {
+ throw new IllegalArgumentException("id cannot be null");
}
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null");
@@ -315,12 +344,12 @@
if (handler == null) {
throw new IllegalArgumentException("handler cannot be null");
}
- SessionCreateCallbackRecord record = new SessionCreateCallbackRecord(callback, handler);
- synchronized (mSessionCreateCallbackRecordMap) {
+ SessionCallbackRecord record = new SessionCallbackRecord(callback, handler);
+ synchronized (mSessionCallbackRecordMap) {
int seq = mNextSeq++;
- mSessionCreateCallbackRecordMap.put(seq, record);
+ mSessionCallbackRecordMap.put(seq, record);
try {
- mService.createSession(mClient, name, seq, mUserId);
+ mService.createSession(mClient, inputId, seq, mUserId);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -337,6 +366,7 @@
private final ITvInputManager mService;
private final int mUserId;
+ private final int mSeq;
// For scheduling input event handling on the main thread. This also serves as a lock to
// protect pending input events and the input channel.
@@ -344,17 +374,21 @@
private final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
+ private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap;
private IBinder mToken;
private TvInputEventSender mSender;
private InputChannel mChannel;
/** @hide */
- private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId) {
+ private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId,
+ int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
mToken = token;
mChannel = channel;
mService = service;
mUserId = userId;
+ mSeq = seq;
+ mSessionCallbackRecordMap = sessionCallbackRecordMap;
}
/**
@@ -368,22 +402,11 @@
}
try {
mService.releaseSession(mToken, mUserId);
- mToken = null;
} catch (RemoteException e) {
throw new RuntimeException(e);
}
- synchronized (mHandler) {
- if (mChannel != null) {
- if (mSender != null) {
- flushPendingEventsLocked();
- mSender.dispose();
- mSender = null;
- }
- mChannel.dispose();
- mChannel = null;
- }
- }
+ releaseInternal();
}
/**
@@ -675,6 +698,24 @@
mPendingEventPool.release(p);
}
+ private void releaseInternal() {
+ mToken = null;
+ synchronized (mHandler) {
+ if (mChannel != null) {
+ if (mSender != null) {
+ flushPendingEventsLocked();
+ mSender.dispose();
+ mSender = null;
+ }
+ mChannel.dispose();
+ mChannel = null;
+ }
+ }
+ synchronized (mSessionCallbackRecordMap) {
+ mSessionCallbackRecordMap.remove(mSeq);
+ }
+ }
+
private final class InputEventHandler extends Handler {
public static final int MSG_SEND_INPUT_EVENT = 1;
public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
diff --git a/core/java/android/tv/TvInputService.java b/core/java/android/tv/TvInputService.java
index 70e7f95..1d6298d 100644
--- a/core/java/android/tv/TvInputService.java
+++ b/core/java/android/tv/TvInputService.java
@@ -60,7 +60,7 @@
*/
public static final String SERVICE_INTERFACE = "android.tv.TvInputService";
- private ComponentName mComponentName;
+ private String mId;
private final Handler mHandler = new ServiceHandler();
private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks =
new RemoteCallbackList<ITvInputServiceCallback>();
@@ -69,7 +69,8 @@
@Override
public void onCreate() {
super.onCreate();
- mComponentName = new ComponentName(getPackageName(), getClass().getName());
+ mId = TvInputInfo.generateInputIdForComponenetName(
+ new ComponentName(getPackageName(), getClass().getName()));
}
@Override
@@ -82,7 +83,7 @@
// The first time a callback is registered, the service needs to report its
// availability status so that the system can know its initial value.
try {
- cb.onAvailabilityChanged(mComponentName, mAvailable);
+ cb.onAvailabilityChanged(mId, mAvailable);
} catch (RemoteException e) {
Log.e(TAG, "error in onAvailabilityChanged", e);
}
@@ -531,8 +532,7 @@
int n = mCallbacks.beginBroadcast();
try {
for (int i = 0; i < n; i++) {
- mCallbacks.getBroadcastItem(i).onAvailabilityChanged(mComponentName,
- isAvailable);
+ mCallbacks.getBroadcastItem(i).onAvailabilityChanged(mId, isAvailable);
}
} catch (RemoteException e) {
Log.e(TAG, "Unexpected exception", e);
diff --git a/core/java/android/tv/TvView.java b/core/java/android/tv/TvView.java
index 289823b..80501e8 100644
--- a/core/java/android/tv/TvView.java
+++ b/core/java/android/tv/TvView.java
@@ -16,13 +16,13 @@
package android.tv;
-import android.content.ComponentName;
import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
+import android.text.TextUtils;
import android.tv.TvInputManager.Session;
import android.tv.TvInputManager.Session.FinishedInputEventCallback;
-import android.tv.TvInputManager.SessionCreateCallback;
+import android.tv.TvInputManager.SessionCallback;
import android.util.AttributeSet;
import android.util.Log;
import android.view.InputEvent;
@@ -46,7 +46,7 @@
private boolean mOverlayViewCreated;
private Rect mOverlayViewFrame;
private final TvInputManager mTvInputManager;
- private SessionCreateCallback mSessionCreateCallback;
+ private SessionCallback mSessionCallback;
private OnUnhandledInputEventListener mOnUnhandledInputEventListener;
private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
@@ -108,19 +108,19 @@
}
/**
- * Binds a TV input to this view. {@link SessionCreateCallback#onSessionCreated} will be
+ * Binds a TV input to this view. {@link SessionCallback#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 inputId the id of TV input which 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");
+ public void bindTvInput(String inputId, SessionCallback callback) {
+ if (TextUtils.isEmpty(inputId)) {
+ throw new IllegalArgumentException("inputId cannot be null or an empty string");
}
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null");
@@ -130,11 +130,11 @@
}
// 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
+ // The previous callbacks will be ignored. For the logic, mSessionCallback
// is newly assigned for every bindTvInput call and compared with
// MySessionCreateCallback.this.
- mSessionCreateCallback = new MySessionCreateCallback(callback);
- mTvInputManager.createSession(name, mSessionCreateCallback, mHandler);
+ mSessionCallback = new MySessionCallback(callback);
+ mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
}
/**
@@ -196,7 +196,9 @@
if (mSession == null) {
return false;
}
- int ret = mSession.dispatchInputEvent(event, event, mFinishedInputEventCallback, mHandler);
+ InputEvent copiedEvent = event.copy();
+ int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
+ mHandler);
return ret != Session.DISPATCH_NOT_HANDLED;
}
@@ -209,7 +211,9 @@
if (mSession == null) {
return false;
}
- int ret = mSession.dispatchInputEvent(event, event, mFinishedInputEventCallback, mHandler);
+ InputEvent copiedEvent = event.copy();
+ int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
+ mHandler);
return ret != Session.DISPATCH_NOT_HANDLED;
}
@@ -222,7 +226,9 @@
if (mSession == null) {
return false;
}
- int ret = mSession.dispatchInputEvent(event, event, mFinishedInputEventCallback, mHandler);
+ InputEvent copiedEvent = event.copy();
+ int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
+ mHandler);
return ret != Session.DISPATCH_NOT_HANDLED;
}
@@ -235,7 +241,9 @@
if (mSession == null) {
return false;
}
- int ret = mSession.dispatchInputEvent(event, event, mFinishedInputEventCallback, mHandler);
+ InputEvent copiedEvent = event.copy();
+ int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
+ mHandler);
return ret != Session.DISPATCH_NOT_HANDLED;
}
@@ -328,16 +336,16 @@
boolean onUnhandledInputEvent(InputEvent event);
}
- private class MySessionCreateCallback implements SessionCreateCallback {
- final SessionCreateCallback mExternalCallback;
+ private class MySessionCallback extends SessionCallback {
+ final SessionCallback mExternalCallback;
- MySessionCreateCallback(SessionCreateCallback externalCallback) {
+ MySessionCallback(SessionCallback externalCallback) {
mExternalCallback = externalCallback;
}
@Override
public void onSessionCreated(Session session) {
- if (this != mSessionCreateCallback) {
+ if (this != mSessionCallback) {
// This callback is obsolete.
session.release();
return;
@@ -356,5 +364,13 @@
mExternalCallback.onSessionCreated(session);
}
}
+
+ @Override
+ public void onSessionReleased(Session session) {
+ mSession = null;
+ if (mExternalCallback != null) {
+ mExternalCallback.onSessionReleased(session);
+ }
+ }
}
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index be0c27d..3fee0ac 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -24,6 +24,8 @@
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputBindResult;
+import libcore.util.Objects;
+
import android.content.Context;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
@@ -317,10 +319,17 @@
int mCursorSelEnd;
int mCursorCandStart;
int mCursorCandEnd;
+
+ /**
+ * The instance that has previously been sent to the input method.
+ */
+ private CursorAnchorInfo mCursorAnchorInfo = null;
+
/**
* The buffer to retrieve the view location in screen coordinates in {@link #updateCursor}.
*/
private final int[] mViewTopLeft = new int[2];
+
// -----------------------------------------------------------
/**
@@ -489,6 +498,9 @@
case SET_CURSOR_ANCHOR_MONITOR_MODE: {
synchronized (mH) {
mCursorAnchorMonitorMode = msg.arg1;
+ // Clear the cache.
+ mCursorRect.setEmpty();
+ mCursorAnchorInfo = null;
}
return;
}
@@ -1173,6 +1185,7 @@
mCursorCandStart = -1;
mCursorCandEnd = -1;
mCursorRect.setEmpty();
+ mCursorAnchorInfo = null;
servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic, this);
} else {
servedContext = null;
@@ -1535,10 +1548,9 @@
|| mCurrentTextBoxAttribute == null || mCurMethod == null) {
return;
}
+ if (DEBUG) Log.d(TAG, "updateCursor");
mTmpCursorRect.set(left, top, right, bottom);
- if (!mCursorRect.equals(mTmpCursorRect)) {
- if (DEBUG) Log.d(TAG, "updateCursor");
-
+ if (!Objects.equal(mCursorRect, mTmpCursorRect)) {
try {
if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
mCursorRect.set(mTmpCursorRect);
@@ -1569,10 +1581,14 @@
|| mCurrentTextBoxAttribute == null || mCurMethod == null) {
return;
}
- if (DEBUG) Log.d(TAG, "updateCursorAnchorInfo");
-
+ if (Objects.equal(mCursorAnchorInfo, cursorAnchorInfo)) {
+ Log.w(TAG, "Ignoring redundant updateCursorAnchorInfo: info=" + cursorAnchorInfo);
+ return;
+ }
+ if (DEBUG) Log.v(TAG, "updateCursorAnchorInfo: " + cursorAnchorInfo);
try {
mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo);
+ mCursorAnchorInfo = cursorAnchorInfo;
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 565ea13..d9a4f57 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -743,7 +743,7 @@
*
* @param view The view whose scroll state is being reported
*
- * @param scrollState The current scroll state. One of
+ * @param scrollState The current scroll state. One of
* {@link #SCROLL_STATE_TOUCH_SCROLL} or {@link #SCROLL_STATE_IDLE}.
*/
public void onScrollStateChanged(AbsListView view, int scrollState);
@@ -3305,18 +3305,22 @@
private void scrollIfNeeded(int x, int y, MotionEvent vtev) {
int rawDeltaY = y - mMotionY;
+ int scrollOffsetCorrection = 0;
+ int scrollConsumedCorrection = 0;
+ if (mLastY == Integer.MIN_VALUE) {
+ rawDeltaY -= mMotionCorrection;
+ }
if (dispatchNestedPreScroll(0, rawDeltaY, mScrollConsumed, mScrollOffset)) {
rawDeltaY -= mScrollConsumed[1];
- mMotionCorrection -= mScrollOffset[1];
- if (mLastY != Integer.MIN_VALUE) {
- mLastY -= mScrollOffset[1] + mScrollConsumed[1];
- }
+ scrollOffsetCorrection -= mScrollOffset[1];
+ scrollConsumedCorrection -= mScrollConsumed[1];
if (vtev != null) {
vtev.offsetLocation(0, mScrollOffset[1]);
}
}
- final int deltaY = rawDeltaY - mMotionCorrection;
- int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
+ final int deltaY = rawDeltaY;
+ int incrementalDeltaY =
+ mLastY != Integer.MIN_VALUE ? y - mLastY + scrollConsumedCorrection : deltaY;
int lastYCorrection = 0;
if (mTouchMode == TOUCH_MODE_SCROLL) {
@@ -3378,7 +3382,6 @@
(motionViewRealTop - motionViewPrevTop);
if (dispatchNestedScroll(0, overscroll - incrementalDeltaY, 0, overscroll,
mScrollOffset)) {
- mMotionCorrection -= mScrollOffset[1];
lastYCorrection -= mScrollOffset[1];
if (vtev != null) {
vtev.offsetLocation(0, mScrollOffset[1]);
@@ -3421,9 +3424,9 @@
}
}
}
- mMotionY = y;
+ mMotionY = y + scrollOffsetCorrection;
}
- mLastY = y + lastYCorrection;
+ mLastY = y + lastYCorrection + scrollOffsetCorrection;
}
} else if (mTouchMode == TOUCH_MODE_OVERSCROLL) {
if (y != mLastY) {
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 225cd6d..4f2d9c6 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -19,7 +19,9 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.Insets;
import android.graphics.Rect;
+import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.AttributeSet;
@@ -32,9 +34,12 @@
import com.android.internal.R;
public abstract class AbsSeekBar extends ProgressBar {
+ private final Rect mTempRect = new Rect();
+
private Drawable mThumb;
private int mThumbOffset;
-
+ private boolean mSplitTrack;
+
/**
* On touch, this offset plus the scaled value from the position of the
* touch will form the progress value. Usually 0.
@@ -51,10 +56,10 @@
* progress.
*/
private int mKeyProgressIncrement = 1;
-
+
private static final int NO_ALPHA = 0xFF;
private float mDisabledAlpha;
-
+
private int mScaledTouchSlop;
private float mTouchDownX;
private boolean mIsDragging;
@@ -76,12 +81,16 @@
TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.SeekBar, defStyleAttr, defStyleRes);
- Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
- setThumb(thumb); // will guess mThumbOffset if thumb != null...
- // ...but allow layout to override this
- int thumbOffset = a.getDimensionPixelOffset(
+
+ final Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
+ setThumb(thumb);
+
+ // Guess thumb offset if thumb != null, but allow layout to override.
+ final int thumbOffset = a.getDimensionPixelOffset(
com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset());
setThumbOffset(thumbOffset);
+
+ mSplitTrack = a.getBoolean(com.android.internal.R.styleable.SeekBar_splitTrack, false);
a.recycle();
a = context.obtainStyledAttributes(attrs,
@@ -97,7 +106,7 @@
* <p>
* If the thumb is a valid drawable (i.e. not null), half its width will be
* used as the new thumb offset (@see #setThumbOffset(int)).
- *
+ *
* @param thumb Drawable representing the thumb
*/
public void setThumb(Drawable thumb) {
@@ -132,7 +141,7 @@
mThumb = thumb;
invalidate();
if (needUpdate) {
- updateThumbPos(getWidth(), getHeight());
+ updateThumbAndTrackPos(getWidth(), getHeight());
if (thumb != null && thumb.isStateful()) {
// Note that if the states are different this won't work.
// For now, let's consider that an app bug.
@@ -162,7 +171,7 @@
/**
* Sets the thumb offset that allows the thumb to extend out of the range of
* the track.
- *
+ *
* @param thumbOffset The offset amount in pixels.
*/
public void setThumbOffset(int thumbOffset) {
@@ -171,8 +180,27 @@
}
/**
+ * Specifies whether the track should be split by the thumb. When true,
+ * the thumb's optical bounds will be clipped out of the track drawable,
+ * then the thumb will be drawn into the resulting gap.
+ *
+ * @param splitTrack Whether the track should be split by the thumb
+ */
+ public void setSplitTrack(boolean splitTrack) {
+ mSplitTrack = splitTrack;
+ invalidate();
+ }
+
+ /**
+ * Returns whether the track should be split by the thumb.
+ */
+ public boolean getSplitTrack() {
+ return mSplitTrack;
+ }
+
+ /**
* Sets the amount of progress changed via the arrow keys.
- *
+ *
* @param increment The amount to increment or decrement when the user
* presses the arrow keys.
*/
@@ -184,14 +212,14 @@
* Returns the amount of progress changed via the arrow keys.
* <p>
* By default, this will be a value that is derived from the max progress.
- *
+ *
* @return The amount to increment or decrement when the user presses the
* arrow keys. This will be positive.
*/
public int getKeyProgressIncrement() {
return mKeyProgressIncrement;
}
-
+
@Override
public synchronized void setMax(int max) {
super.setMax(max);
@@ -217,79 +245,85 @@
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
-
- Drawable progressDrawable = getProgressDrawable();
+
+ final Drawable progressDrawable = getProgressDrawable();
if (progressDrawable != null) {
progressDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha));
}
-
- if (mThumb != null && mThumb.isStateful()) {
- int[] state = getDrawableState();
- mThumb.setState(state);
+
+ final Drawable thumb = mThumb;
+ if (thumb != null && thumb.isStateful()) {
+ thumb.setState(getDrawableState());
}
}
-
+
@Override
void onProgressRefresh(float scale, boolean fromUser) {
super.onProgressRefresh(scale, fromUser);
- Drawable thumb = mThumb;
+
+ final Drawable thumb = mThumb;
if (thumb != null) {
setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
- /*
- * Since we draw translated, the drawable's bounds that it signals
- * for invalidation won't be the actual bounds we want invalidated,
- * so just invalidate this whole view.
- */
+
+ // Since we draw translated, the drawable's bounds that it signals
+ // for invalidation won't be the actual bounds we want invalidated,
+ // so just invalidate this whole view.
invalidate();
}
}
-
-
+
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
- updateThumbPos(w, h);
+
+ updateThumbAndTrackPos(w, h);
}
- private void updateThumbPos(int w, int h) {
- Drawable d = getCurrentDrawable();
- Drawable thumb = mThumb;
- int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight();
+ private void updateThumbAndTrackPos(int w, int h) {
+ final Drawable track = getCurrentDrawable();
+ final Drawable thumb = mThumb;
+
// The max height does not incorporate padding, whereas the height
- // parameter does
- int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom);
-
- int max = getMax();
- float scale = max > 0 ? (float) getProgress() / (float) max : 0;
-
+ // parameter does.
+ final int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom);
+ final int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight();
+
+ // Apply offset to whichever item is taller.
+ final int trackOffset;
+ final int thumbOffset;
if (thumbHeight > trackHeight) {
- if (thumb != null) {
- setThumbPos(w, thumb, scale, 0);
- }
- int gapForCenteringTrack = (thumbHeight - trackHeight) / 2;
- if (d != null) {
- // Canvas will be translated by the padding, so 0,0 is where we start drawing
- d.setBounds(0, gapForCenteringTrack,
- w - mPaddingRight - mPaddingLeft, h - mPaddingBottom - gapForCenteringTrack
- - mPaddingTop);
- }
+ trackOffset = (thumbHeight - trackHeight) / 2;
+ thumbOffset = 0;
} else {
- if (d != null) {
- // Canvas will be translated by the padding, so 0,0 is where we start drawing
- d.setBounds(0, 0, w - mPaddingRight - mPaddingLeft, h - mPaddingBottom
- - mPaddingTop);
- }
- int gap = (trackHeight - thumbHeight) / 2;
- if (thumb != null) {
- setThumbPos(w, thumb, scale, gap);
- }
+ trackOffset = 0;
+ thumbOffset = (trackHeight - thumbHeight) / 2;
+ }
+
+ if (track != null) {
+ track.setBounds(0, trackOffset, w - mPaddingRight - mPaddingLeft,
+ h - mPaddingBottom - trackOffset - mPaddingTop);
+ }
+
+ if (thumb != null) {
+ setThumbPos(w, thumb, getScale(), thumbOffset);
}
}
+ private float getScale() {
+ final int max = getMax();
+ return max > 0 ? getProgress() / (float) max : 0;
+ }
+
/**
- * @param gap If set to {@link Integer#MIN_VALUE}, this will be ignored and
+ * Updates the thumb drawable bounds.
+ *
+ * @param w Width of the view, including padding
+ * @param thumb Drawable used for the thumb
+ * @param scale Current progress between 0 and 1
+ * @param offset Vertical offset for centering. If set to
+ * {@link Integer#MIN_VALUE}, the current offset will be used.
*/
- private void setThumbPos(int w, Drawable thumb, float scale, int gap) {
+ private void setThumbPos(int w, Drawable thumb, float scale, int offset) {
int available = w - mPaddingLeft - mPaddingRight;
final int thumbWidth = thumb.getIntrinsicWidth();
final int thumbHeight = thumb.getIntrinsicHeight();
@@ -301,13 +335,13 @@
final int thumbPos = (int) (scale * available + 0.5f);
final int top, bottom;
- if (gap == Integer.MIN_VALUE) {
+ if (offset == Integer.MIN_VALUE) {
final Rect oldBounds = thumb.getBounds();
top = oldBounds.top;
bottom = oldBounds.bottom;
} else {
- top = gap;
- bottom = gap + thumbHeight;
+ top = offset;
+ bottom = offset + thumbHeight;
}
final int left = (isLayoutRtl() && mMirrorForRtl) ? available - thumbPos : thumbPos;
@@ -342,6 +376,33 @@
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
+ drawThumb(canvas);
+ }
+
+ @Override
+ void drawTrack(Canvas canvas) {
+ final Drawable thumbDrawable = mThumb;
+ if (thumbDrawable != null && mSplitTrack) {
+ final Insets insets = thumbDrawable.getOpticalInsets();
+ final Rect tempRect = mTempRect;
+ thumbDrawable.copyBounds(tempRect);
+ tempRect.offset(mPaddingLeft - mThumbOffset, mPaddingTop);
+ tempRect.left += insets.left;
+ tempRect.right -= insets.right;
+
+ final int saveCount = canvas.save();
+ canvas.clipRect(tempRect, Op.DIFFERENCE);
+ super.drawTrack(canvas);
+ canvas.restoreToCount(saveCount);
+ } else {
+ super.drawTrack(canvas);
+ }
+ }
+
+ /**
+ * Draw the thumb.
+ */
+ void drawThumb(Canvas canvas) {
if (mThumb != null) {
canvas.save();
// Translate the padding. For the x, we need to allow the thumb to
@@ -366,17 +427,17 @@
}
dw += mPaddingLeft + mPaddingRight;
dh += mPaddingTop + mPaddingBottom;
-
+
setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0),
resolveSizeAndState(dh, heightMeasureSpec, 0));
}
-
+
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!mIsUserSeekable || !isEnabled()) {
return false;
}
-
+
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (isInScrollingContainer()) {
@@ -391,7 +452,7 @@
attemptClaimDrag();
}
break;
-
+
case MotionEvent.ACTION_MOVE:
if (mIsDragging) {
trackTouchEvent(event);
@@ -408,7 +469,7 @@
}
}
break;
-
+
case MotionEvent.ACTION_UP:
if (mIsDragging) {
trackTouchEvent(event);
@@ -426,7 +487,7 @@
// value has not apparently changed)
invalidate();
break;
-
+
case MotionEvent.ACTION_CANCEL:
if (mIsDragging) {
onStopTrackingTouch();
@@ -493,7 +554,7 @@
mParent.requestDisallowInterceptTouchEvent(true);
}
}
-
+
/**
* This is called when the user has started touching this widget.
*/
@@ -526,7 +587,7 @@
setProgress(progress - mKeyProgressIncrement, true);
onKeyChange();
return true;
-
+
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (progress >= getMax()) break;
setProgress(progress + mKeyProgressIncrement, true);
@@ -595,17 +656,13 @@
public void onRtlPropertiesChanged(int layoutDirection) {
super.onRtlPropertiesChanged(layoutDirection);
- int max = getMax();
- float scale = max > 0 ? (float) getProgress() / (float) max : 0;
-
- Drawable thumb = mThumb;
+ final Drawable thumb = mThumb;
if (thumb != null) {
- setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
- /*
- * Since we draw translated, the drawable's bounds that it signals
- * for invalidation won't be the actual bounds we want invalidated,
- * so just invalidate this whole view.
- */
+ setThumbPos(getWidth(), thumb, getScale(), Integer.MIN_VALUE);
+
+ // Since we draw translated, the drawable's bounds that it signals
+ // for invalidation won't be the actual bounds we want invalidated,
+ // so just invalidate this whole view.
invalidate();
}
}
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index f7e81b8..0c3715d 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -1066,21 +1066,30 @@
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
- Drawable d = mCurrentDrawable;
+ drawTrack(canvas);
+ }
+
+ /**
+ * Draws the progress bar track.
+ */
+ void drawTrack(Canvas canvas) {
+ final Drawable d = mCurrentDrawable;
if (d != null) {
// Translate canvas so a indeterminate circular progress bar with padding
// rotates properly in its animation
- canvas.save();
+ final int saveCount = canvas.save();
+
if(isLayoutRtl() && mMirrorForRtl) {
canvas.translate(getWidth() - mPaddingRight, mPaddingTop);
canvas.scale(-1.0f, 1.0f);
} else {
canvas.translate(mPaddingLeft, mPaddingTop);
}
- long time = getDrawingTime();
+
+ final long time = getDrawingTime();
if (mHasAnimation) {
mAnimation.getTransformation(time, mTransformation);
- float scale = mTransformation.getAlpha();
+ final float scale = mTransformation.getAlpha();
try {
mInDrawing = true;
d.setLevel((int) (scale * MAX_LEVEL));
@@ -1089,8 +1098,10 @@
}
postInvalidateOnAnimation();
}
+
d.draw(canvas);
- canvas.restore();
+ canvas.restoreToCount(saveCount);
+
if (mShouldStartAnimationDrawable && d instanceof Animatable) {
((Animatable) d).start();
mShouldStartAnimationDrawable = false;
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 08af4de..438e164 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -22,9 +22,11 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
+import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.StaticLayout;
@@ -85,6 +87,7 @@
private int mThumbTextPadding;
private int mSwitchMinWidth;
private int mSwitchPadding;
+ private boolean mSplitTrack;
private CharSequence mTextOn;
private CharSequence mTextOff;
@@ -174,13 +177,13 @@
super(context, attrs, defStyleAttr, defStyleRes);
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
- Resources res = getResources();
+
+ final Resources res = getResources();
mTextPaint.density = res.getDisplayMetrics().density;
mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.Switch, defStyleAttr, defStyleRes);
-
mThumbDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_thumb);
mTrackDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_track);
mTextOn = a.getText(com.android.internal.R.styleable.Switch_textOn);
@@ -191,15 +194,16 @@
com.android.internal.R.styleable.Switch_switchMinWidth, 0);
mSwitchPadding = a.getDimensionPixelSize(
com.android.internal.R.styleable.Switch_switchPadding, 0);
+ mSplitTrack = a.getBoolean(com.android.internal.R.styleable.Switch_splitTrack, false);
- int appearance = a.getResourceId(
+ final int appearance = a.getResourceId(
com.android.internal.R.styleable.Switch_switchTextAppearance, 0);
if (appearance != 0) {
setSwitchTextAppearance(context, appearance);
}
a.recycle();
- ViewConfiguration config = ViewConfiguration.get(context);
+ final ViewConfiguration config = ViewConfiguration.get(context);
mTouchSlop = config.getScaledTouchSlop();
mMinFlingVelocity = config.getScaledMinimumFlingVelocity();
@@ -469,6 +473,29 @@
}
/**
+ * Specifies whether the track should be split by the thumb. When true,
+ * the thumb's optical bounds will be clipped out of the track drawable,
+ * then the thumb will be drawn into the resulting gap.
+ *
+ * @param splitTrack Whether the track should be split by the thumb
+ *
+ * @attr ref android.R.styleable#Switch_splitTrack
+ */
+ public void setSplitTrack(boolean splitTrack) {
+ mSplitTrack = splitTrack;
+ invalidate();
+ }
+
+ /**
+ * Returns whether the track should be split by the thumb.
+ *
+ * @attr ref android.R.styleable#Switch_splitTrack
+ */
+ public boolean getSplitTrack() {
+ return mSplitTrack;
+ }
+
+ /**
* Returns the text displayed when the button is in the checked state.
*
* @attr ref android.R.styleable#Switch_textOn
@@ -518,13 +545,15 @@
mTrackDrawable.getPadding(mTempRect);
- final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth());
+ final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
+ + mThumbTextPadding * 2;
+ mThumbWidth = Math.max(maxTextWidth, mThumbDrawable.getIntrinsicWidth());
+
final int switchWidth = Math.max(mSwitchMinWidth,
- maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right);
+ 2 * mThumbWidth + mTempRect.left + mTempRect.right);
final int switchHeight = Math.max(mTrackDrawable.getIntrinsicHeight(),
mThumbDrawable.getIntrinsicHeight());
- mThumbWidth = maxTextWidth + mThumbTextPadding * 2;
mSwitchWidth = switchWidth;
mSwitchHeight = switchHeight;
@@ -777,7 +806,7 @@
final Drawable trackDrawable = mTrackDrawable;
final Drawable thumbDrawable = mThumbDrawable;
- // Draw the switch
+ // Layout the track.
final int switchLeft = mSwitchLeft;
final int switchTop = mSwitchTop;
final int switchRight = mSwitchRight;
@@ -793,9 +822,10 @@
// Relies on mTempRect, MUST be called first!
final int thumbPos = getThumbOffset();
+ // Layout the thumb.
thumbDrawable.getPadding(tempRect);
- int thumbLeft = switchInnerLeft - tempRect.left + thumbPos;
- int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right;
+ final int thumbLeft = switchInnerLeft - tempRect.left + thumbPos;
+ final int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right;
thumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
final Drawable background = getBackground();
@@ -805,20 +835,32 @@
super.onDraw(canvas);
- trackDrawable.draw(canvas);
+ if (mSplitTrack) {
+ final Insets insets = thumbDrawable.getOpticalInsets();
+ thumbDrawable.copyBounds(tempRect);
+ tempRect.left += insets.left;
+ tempRect.right -= insets.right;
+
+ final int saveCount = canvas.save();
+ canvas.clipRect(tempRect, Op.DIFFERENCE);
+ trackDrawable.draw(canvas);
+ canvas.restoreToCount(saveCount);
+ } else {
+ trackDrawable.draw(canvas);
+ }
final int saveCount = canvas.save();
canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);
thumbDrawable.draw(canvas);
- final int drawableState[] = getDrawableState();
- if (mTextColors != null) {
- mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0));
- }
- mTextPaint.drawableState = drawableState;
-
final Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
if (switchText != null) {
+ final int drawableState[] = getDrawableState();
+ if (mTextColors != null) {
+ mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0));
+ }
+ mTextPaint.drawableState = drawableState;
+
final int left = (thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2;
final int top = (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2;
canvas.translate(left, top);
@@ -889,12 +931,16 @@
protected void drawableStateChanged() {
super.drawableStateChanged();
- int[] myDrawableState = getDrawableState();
+ final int[] myDrawableState = getDrawableState();
- // Set the state of the Drawable
- // Drawable may be null when checked state is set from XML, from super constructor
- if (mThumbDrawable != null) mThumbDrawable.setState(myDrawableState);
- if (mTrackDrawable != null) mTrackDrawable.setState(myDrawableState);
+ if (mThumbDrawable != null && mThumbDrawable.setState(myDrawableState)) {
+ // Handle changes to thumb width and height.
+ requestLayout();
+ }
+
+ if (mTrackDrawable != null) {
+ mTrackDrawable.setState(myDrawableState);
+ }
invalidate();
}
diff --git a/core/java/com/android/internal/notification/DemoContactNotificationScorer.java b/core/java/com/android/internal/notification/DemoContactNotificationScorer.java
deleted file mode 100644
index f484724..0000000
--- a/core/java/com/android/internal/notification/DemoContactNotificationScorer.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-package com.android.internal.notification;
-
-import android.app.Notification;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract;
-import android.provider.Settings;
-import android.text.SpannableString;
-import android.util.Slog;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * This NotificationScorer bumps up the priority of notifications that contain references to the
- * display names of starred contacts. The references it picks up are spannable strings which, in
- * their entirety, match the display name of some starred contact. The magnitude of the bump ranges
- * from 0 to 15 (assuming NOTIFICATION_PRIORITY_MULTIPLIER = 10) depending on the initial score, and
- * the mapping is defined by priorityBumpMap. In a production version of this scorer, a notification
- * extra will be used to specify contact identifiers.
- */
-
-public class DemoContactNotificationScorer implements NotificationScorer {
- private static final String TAG = "DemoContactNotificationScorer";
- private static final boolean DBG = false;
-
- protected static final boolean ENABLE_CONTACT_SCORER = true;
- private static final String SETTING_ENABLE_SCORER = "contact_scorer_enabled";
- protected boolean mEnabled;
-
- // see NotificationManagerService
- private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
-
- private Context mContext;
-
- private static final List<String> RELEVANT_KEYS_LIST = Arrays.asList(
- Notification.EXTRA_INFO_TEXT, Notification.EXTRA_TEXT, Notification.EXTRA_TEXT_LINES,
- Notification.EXTRA_SUB_TEXT, Notification.EXTRA_TITLE
- );
-
- private static final String[] PROJECTION = new String[] {
- ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME
- };
-
- private static final Uri CONTACTS_URI = ContactsContract.Contacts.CONTENT_URI;
-
- private static List<String> extractSpannedStrings(CharSequence charSequence) {
- if (charSequence == null) return Collections.emptyList();
- if (!(charSequence instanceof SpannableString)) {
- return Arrays.asList(charSequence.toString());
- }
- SpannableString spannableString = (SpannableString)charSequence;
- // get all spans
- Object[] ssArr = spannableString.getSpans(0, spannableString.length(), Object.class);
- // spanned string sequences
- ArrayList<String> sss = new ArrayList<String>();
- for (Object spanObj : ssArr) {
- try {
- sss.add(spannableString.subSequence(spannableString.getSpanStart(spanObj),
- spannableString.getSpanEnd(spanObj)).toString());
- } catch(StringIndexOutOfBoundsException e) {
- Slog.e(TAG, "Bad indices when extracting spanned subsequence", e);
- }
- }
- return sss;
- };
-
- private static String getQuestionMarksInParens(int n) {
- StringBuilder sb = new StringBuilder("(");
- for (int i = 0; i < n; i++) {
- if (sb.length() > 1) sb.append(',');
- sb.append('?');
- }
- sb.append(")");
- return sb.toString();
- }
-
- private boolean hasStarredContact(Bundle extras) {
- if (extras == null) return false;
- ArrayList<String> qStrings = new ArrayList<String>();
- // build list to query against the database for display names.
- for (String rk: RELEVANT_KEYS_LIST) {
- if (extras.get(rk) == null) {
- continue;
- } else if (extras.get(rk) instanceof CharSequence) {
- qStrings.addAll(extractSpannedStrings((CharSequence) extras.get(rk)));
- } else if (extras.get(rk) instanceof CharSequence[]) {
- // this is intended for Notification.EXTRA_TEXT_LINES
- for (CharSequence line: (CharSequence[]) extras.get(rk)){
- qStrings.addAll(extractSpannedStrings(line));
- }
- } else {
- Slog.w(TAG, "Strange, the extra " + rk + " is of unexpected type.");
- }
- }
- if (qStrings.isEmpty()) return false;
- String[] qStringsArr = qStrings.toArray(new String[qStrings.size()]);
-
- String selection = ContactsContract.Contacts.DISPLAY_NAME + " IN "
- + getQuestionMarksInParens(qStringsArr.length) + " AND "
- + ContactsContract.Contacts.STARRED+" ='1'";
-
- Cursor c = null;
- try {
- c = mContext.getContentResolver().query(
- CONTACTS_URI, PROJECTION, selection, qStringsArr, null);
- if (c != null) return c.getCount() > 0;
- } catch(Throwable t) {
- Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return false;
- }
-
- private final static int clamp(int x, int low, int high) {
- return (x < low) ? low : ((x > high) ? high : x);
- }
-
- private static int priorityBumpMap(int incomingScore) {
- //assumption is that scale runs from [-2*pm, 2*pm]
- int pm = NOTIFICATION_PRIORITY_MULTIPLIER;
- int theScore = incomingScore;
- // enforce input in range
- theScore = clamp(theScore, -2 * pm, 2 * pm);
- if (theScore != incomingScore) return incomingScore;
- // map -20 -> -20 and -10 -> 5 (when pm = 10)
- if (theScore <= -pm) {
- theScore += 1.5 * (theScore + 2 * pm);
- } else {
- // map 0 -> 10, 10 -> 15, 20 -> 20;
- theScore += 0.5 * (2 * pm - theScore);
- }
- if (DBG) Slog.v(TAG, "priorityBumpMap: score before: " + incomingScore
- + ", score after " + theScore + ".");
- return theScore;
- }
-
- @Override
- public void initialize(Context context) {
- if (DBG) Slog.v(TAG, "Initializing " + getClass().getSimpleName() + ".");
- mContext = context;
- mEnabled = ENABLE_CONTACT_SCORER && 1 == Settings.Global.getInt(
- mContext.getContentResolver(), SETTING_ENABLE_SCORER, 0);
- }
-
- @Override
- public int getScore(Notification notification, int score) {
- if (notification == null || !mEnabled) {
- if (DBG) Slog.w(TAG, "empty notification? scorer disabled?");
- return score;
- }
- boolean hasStarredPriority = hasStarredContact(notification.extras);
-
- if (DBG) {
- if (hasStarredPriority) {
- Slog.v(TAG, "Notification references starred contact. Promoted!");
- } else {
- Slog.v(TAG, "Notification lacks any starred contact reference. Not promoted!");
- }
- }
- if (hasStarredPriority) score = priorityBumpMap(score);
- return score;
- }
-}
-
diff --git a/core/java/com/android/internal/notification/NotificationScorer.java b/core/java/com/android/internal/notification/NotificationScorer.java
deleted file mode 100644
index 863c08c..0000000
--- a/core/java/com/android/internal/notification/NotificationScorer.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-package com.android.internal.notification;
-
-import android.app.Notification;
-import android.content.Context;
-
-public interface NotificationScorer {
-
- public void initialize(Context context);
- public int getScore(Notification notification, int score);
-
-}
diff --git a/core/java/com/android/internal/notification/PeopleNotificationScorer.java b/core/java/com/android/internal/notification/PeopleNotificationScorer.java
deleted file mode 100644
index efb5f63..0000000
--- a/core/java/com/android/internal/notification/PeopleNotificationScorer.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
-* Copyright (C) 2014 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-package com.android.internal.notification;
-
-import android.app.Notification;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Contacts;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.LruCache;
-import android.util.Slog;
-
-/**
- * This {@link NotificationScorer} attempts to validate people references.
- * Also elevates the priority of real people.
- */
-public class PeopleNotificationScorer implements NotificationScorer {
- private static final String TAG = "PeopleNotificationScorer";
- private static final boolean DBG = false;
-
- private static final boolean ENABLE_PEOPLE_SCORER = true;
- private static final String SETTING_ENABLE_PEOPLE_SCORER = "people_scorer_enabled";
- private static final String[] LOOKUP_PROJECTION = { Contacts._ID };
- private static final int MAX_PEOPLE = 10;
- private static final int PEOPLE_CACHE_SIZE = 200;
- // see NotificationManagerService
- private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
-
- protected boolean mEnabled;
- private Context mContext;
-
- // maps raw person handle to resolved person object
- private LruCache<String, LookupResult> mPeopleCache;
-
- private float findMaxContactScore(Bundle extras) {
- if (extras == null) {
- return 0f;
- }
-
- final String[] people = extras.getStringArray(Notification.EXTRA_PEOPLE);
- if (people == null || people.length == 0) {
- return 0f;
- }
-
- float rank = 0f;
- for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) {
- final String handle = people[personIdx];
- if (TextUtils.isEmpty(handle)) continue;
-
- LookupResult lookupResult = mPeopleCache.get(handle);
- if (lookupResult == null || lookupResult.isExpired()) {
- final Uri uri = Uri.parse(handle);
- if ("tel".equals(uri.getScheme())) {
- if (DBG) Slog.w(TAG, "checking telephone URI: " + handle);
- lookupResult = lookupPhoneContact(handle, uri.getSchemeSpecificPart());
- } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
- if (DBG) Slog.w(TAG, "checking lookup URI: " + handle);
- lookupResult = resolveContactsUri(handle, uri);
- } else {
- if (DBG) Slog.w(TAG, "unsupported URI " + handle);
- }
- } else {
- if (DBG) Slog.w(TAG, "using cached lookupResult: " + lookupResult.mId);
- }
- if (lookupResult != null) {
- rank = Math.max(rank, lookupResult.getRank());
- }
- }
- return rank;
- }
-
- private LookupResult lookupPhoneContact(final String handle, final String number) {
- LookupResult lookupResult = null;
- Cursor c = null;
- try {
- Uri numberUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
- Uri.encode(number));
- c = mContext.getContentResolver().query(numberUri, LOOKUP_PROJECTION, null, null, null);
- if (c != null && c.getCount() > 0) {
- c.moveToFirst();
- final int idIdx = c.getColumnIndex(Contacts._ID);
- final int id = c.getInt(idIdx);
- if (DBG) Slog.w(TAG, "is valid: " + id);
- lookupResult = new LookupResult(id);
- }
- } catch(Throwable t) {
- Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
- } finally {
- if (c != null) {
- c.close();
- }
- }
- if (lookupResult == null) {
- lookupResult = new LookupResult(LookupResult.INVALID_ID);
- }
- mPeopleCache.put(handle, lookupResult);
- return lookupResult;
- }
-
- private LookupResult resolveContactsUri(String handle, final Uri personUri) {
- LookupResult lookupResult = null;
- Cursor c = null;
- try {
- c = mContext.getContentResolver().query(personUri, LOOKUP_PROJECTION, null, null, null);
- if (c != null && c.getCount() > 0) {
- c.moveToFirst();
- final int idIdx = c.getColumnIndex(Contacts._ID);
- final int id = c.getInt(idIdx);
- if (DBG) Slog.w(TAG, "is valid: " + id);
- lookupResult = new LookupResult(id);
- }
- } catch(Throwable t) {
- Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
- } finally {
- if (c != null) {
- c.close();
- }
- }
- if (lookupResult == null) {
- lookupResult = new LookupResult(LookupResult.INVALID_ID);
- }
- mPeopleCache.put(handle, lookupResult);
- return lookupResult;
- }
-
- private final static int clamp(int x, int low, int high) {
- return (x < low) ? low : ((x > high) ? high : x);
- }
-
- // TODO: rework this function before shipping
- private static int priorityBumpMap(int incomingScore) {
- //assumption is that scale runs from [-2*pm, 2*pm]
- int pm = NOTIFICATION_PRIORITY_MULTIPLIER;
- int theScore = incomingScore;
- // enforce input in range
- theScore = clamp(theScore, -2 * pm, 2 * pm);
- if (theScore != incomingScore) return incomingScore;
- // map -20 -> -20 and -10 -> 5 (when pm = 10)
- if (theScore <= -pm) {
- theScore += 1.5 * (theScore + 2 * pm);
- } else {
- // map 0 -> 10, 10 -> 15, 20 -> 20;
- theScore += 0.5 * (2 * pm - theScore);
- }
- if (DBG) Slog.v(TAG, "priorityBumpMap: score before: " + incomingScore
- + ", score after " + theScore + ".");
- return theScore;
- }
-
- @Override
- public void initialize(Context context) {
- if (DBG) Slog.v(TAG, "Initializing " + getClass().getSimpleName() + ".");
- mContext = context;
- mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE);
- mEnabled = ENABLE_PEOPLE_SCORER && 1 == Settings.Global.getInt(
- mContext.getContentResolver(), SETTING_ENABLE_PEOPLE_SCORER, 0);
- }
-
- @Override
- public int getScore(Notification notification, int score) {
- if (notification == null || !mEnabled) {
- if (DBG) Slog.w(TAG, "empty notification? scorer disabled?");
- return score;
- }
- float contactScore = findMaxContactScore(notification.extras);
- if (contactScore > 0f) {
- if (DBG) Slog.v(TAG, "Notification references a real contact. Promoted!");
- score = priorityBumpMap(score);
- } else {
- if (DBG) Slog.v(TAG, "Notification lacks any valid contact reference. Not promoted!");
- }
- return score;
- }
-
- private static class LookupResult {
- private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr
- public static final int INVALID_ID = -1;
-
- private final long mExpireMillis;
- private int mId;
-
- public LookupResult(int id) {
- mId = id;
- mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS;
- }
-
- public boolean isExpired() {
- return mExpireMillis < System.currentTimeMillis();
- }
-
- public boolean isInvalid() {
- return mId == INVALID_ID || isExpired();
- }
-
- public float getRank() {
- if (isInvalid()) {
- return 0f;
- } else {
- return 1f; // TODO: finer grained score
- }
- }
-
- public LookupResult setId(int id) {
- mId = id;
- return this;
- }
- }
-}
-
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 52281d9..34f62ba 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -450,6 +450,7 @@
public void disconnect() {
if ((mConnection != null) && (mSrcContext != null)) {
mSrcContext.unbindService(mConnection);
+ mConnection = null;
}
try {
// Send the DISCONNECTED, although it may not be received
@@ -463,10 +464,12 @@
// Tell source we're disconnected.
if (mSrcHandler != null) {
replyDisconnected(STATUS_SUCCESSFUL);
+ mSrcHandler = null;
}
// Unlink only when bindService isn't used
if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) {
mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0);
+ mDeathMonitor = null;
}
}
diff --git a/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png
index f1023ea..5a99528 100644
--- a/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png
+++ b/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png
index 15ceeee..79de664 100644
--- a/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png
+++ b/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png
index 90b1498..73e8f1c 100644
--- a/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png
+++ b/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png
index b535758..ff6affe 100644
--- a/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png
+++ b/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png
index 1833704..e40cba8 100644
--- a/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png
+++ b/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png
index e64d3f2..437a3e3 100644
--- a/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png
+++ b/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png
index ffd6c39..8949b52 100644
--- a/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png
+++ b/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png
index 15faff0..d727683 100644
--- a/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png
+++ b/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml
index 8eb00fa..668cff7 100644
--- a/core/res/res/drawable-nodpi/platlogo.xml
+++ b/core/res/res/drawable-nodpi/platlogo.xml
@@ -18,22 +18,21 @@
<viewport android:viewportHeight="25" android:viewportWidth="25" />
- <group>
- <path
- android:name="shadow"
- android:pathData="m12,2.5 a11,11 0 1,0 1,0
- M6.5,7.5
- l5,0 l0,7 l7,0 l0,5 l-12,0 z"
- android:fill="#40000000"
- />
- <path
- android:name="circle-L-ranch"
- android:pathData="m12,1.5 a11,11 0 1,0 1,0
- M6.5,6.5
- l5,0 l0,7 l7,0 l0,5 l-12,0 z"
- android:fill="#FFFFFF40"
- />
- </group>
+ <path
+ android:name="shadow"
+ android:pathData="m12,2.5 a11,11 0 1,0 1,0
+ M6.5,7.5
+ l5,0 l0,7 l7,0 l0,5 l-12,0 z"
+ android:fill="#40000000"
+ />
+ <path
+ android:name="circle-L-ranch"
+ android:pathData="m12,1.5 a11,11 0 1,0 1,0
+ M6.5,6.5
+ l5,0 l0,7 l7,0 l0,5 l-12,0 z"
+ android:fill="#FFFFFF40"
+ />
+
</vector>
diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml
index 49028b4..b8ddb77 100644
--- a/core/res/res/drawable-nodpi/stat_sys_adb.xml
+++ b/core/res/res/drawable-nodpi/stat_sys_adb.xml
@@ -18,13 +18,13 @@
<viewport android:viewportHeight="25" android:viewportWidth="25" />
- <group>
- <path
- android:name="adb"
- android:pathData="m3,3l8,0l0,11l11,0l0,8l-19,0z"
- android:fill="#FFFFFFFF"
- />
- </group>
+
+ <path
+ android:name="adb"
+ android:pathData="m3,3l8,0l0,11l11,0l0,8l-19,0z"
+ android:fill="#FFFFFFFF"
+ />
+
</vector>
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png
index ad72f06..729e0bf 100644
--- a/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png
+++ b/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png
index 7aceed1..d018a7c 100644
--- a/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png
+++ b/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png
index 309b528..a7a972c 100644
--- a/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png
+++ b/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png
index 139795e..dd8910b 100644
--- a/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png
+++ b/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png
index c11b0ae..a2b5716 100644
--- a/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png
index cde797e..caabc2c 100644
--- a/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png
index 9e234af..8d79a13 100644
--- a/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png
+++ b/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png
index b371eab..e0e4ef9 100644
--- a/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png
+++ b/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml b/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml
index d172b05..f82fe7a 100644
--- a/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml
+++ b/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml
@@ -16,22 +16,26 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false">
- <bitmap android:src="@drawable/scrubber_track_qntm_alpha"
+ <nine-patch android:src="@drawable/scrubber_track_qntm_alpha"
android:tint="?attr/colorControlNormal" />
</item>
<item>
<layer-list>
<item android:id="@id/background">
- <bitmap android:src="@drawable/scrubber_track_qntm_alpha"
+ <nine-patch android:src="@drawable/scrubber_track_qntm_alpha"
android:tint="?attr/colorControlNormal" />
</item>
<item android:id="@id/secondaryProgress">
- <bitmap android:src="@drawable/scrubber_primary_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
+ <scale android:scaleWidth="100%">
+ <nine-patch android:src="@drawable/scrubber_primary_qntm_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </scale>
</item>
<item android:id="@id/progress">
- <bitmap android:src="@drawable/scrubber_primary_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <scale android:scaleWidth="100%">
+ <nine-patch android:src="@drawable/scrubber_primary_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </scale>
</item>
</layer-list>
</item>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 4d6f0a3..bee6c21 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -68,7 +68,7 @@
</plurals>
<string name="imei" msgid="2625429890869005782">"IMEI"</string>
<string name="meid" msgid="4841221237681254195">"MEID"</string>
- <string name="ClipMmi" msgid="6952821216480289285">"លេខសម្គាល់អ្នកហៅចូល"</string>
+ <string name="ClipMmi" msgid="6952821216480289285">"លេខសម្គាល់អ្នកហៅចូល"</string>
<string name="ClirMmi" msgid="7784673673446833091">"លេខសម្គាល់អ្នកហៅចេញ"</string>
<string name="CfMmi" msgid="5123218989141573515">"បញ្ជូនការហៅបន្ត"</string>
<string name="CwMmi" msgid="9129678056795016867">"រង់ចាំការហៅ"</string>
@@ -125,7 +125,7 @@
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> ៖ មិនបានបញ្ជូនបន្ត"</string>
<string name="fcComplete" msgid="3118848230966886575">"កូដលក្ខណៈពេញលេញ។"</string>
<string name="fcError" msgid="3327560126588500777">"បញ្ហាការតភ្ជាប់ ឬកូដលក្ខណៈមិនត្រឹមត្រូវ។"</string>
- <string name="httpErrorOk" msgid="1191919378083472204">"យល់ព្រម"</string>
+ <string name="httpErrorOk" msgid="1191919378083472204">"យល់ព្រម"</string>
<string name="httpError" msgid="7956392511146698522">"មានកំហុសបណ្ដាញ។"</string>
<string name="httpErrorLookup" msgid="4711687456111963163">"រកមិនឃើញ URL ។"</string>
<string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"គ្រោងការណ៍ផ្ទៀងផ្ទាត់តំបន់បណ្ដាញមិនត្រូវបានគាំទ្រ។"</string>
@@ -183,7 +183,7 @@
<string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"បើកសំឡេង"</string>
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ពេលជិះយន្តហោះ"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"បានបើករបៀបពេលជិះយន្តហោះ"</string>
- <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បានបិទរបៀបពេលជិះយន្តហោះ"</string>
+ <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បានបិទរបៀបពេលជិះយន្តហោះ"</string>
<string name="global_action_settings" msgid="1756531602592545966">"ការកំណត់"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"របៀបសុវត្ថិភាព"</string>
@@ -195,7 +195,7 @@
<string name="permgrouplab_messages" msgid="7521249148445456662">"សាររបស់អ្នក"</string>
<string name="permgroupdesc_messages" msgid="7821999071003699236">"អាន និងសរសេរសារ SMS, អ៊ីមែល និងសារផ្សេងៗទៀតរបស់អ្នក។"</string>
<string name="permgrouplab_personalInfo" msgid="3519163141070533474">"ព័ត៌មានផ្ទាល់ខ្លួនរបស់អ្នក"</string>
- <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូលដំណើរការព័ត៌មានដោយផ្ទាល់អំពីអ្នក ដែលបានរក្សាទុកក្នុងកាតទំនាក់ទំនងរបស់អ្នក។"</string>
+ <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូលដំណើរការព័ត៌មានដោយផ្ទាល់អំពីអ្នក ដែលបានរក្សាទុកក្នុងកាតទំនាក់ទំនងរបស់អ្នក។"</string>
<string name="permgrouplab_socialInfo" msgid="5799096623412043791">"ព័ត៌មានសង្គមរបស់អ្នក"</string>
<string name="permgroupdesc_socialInfo" msgid="7129842457611643493">"ចូលដំណើរការព័ត៌មានដោយផ្ទាល់អំពីទំនាក់ទំនង និងការភ្ជាប់សង្គមរបស់អ្នក។"</string>
<string name="permgrouplab_location" msgid="635149742436692049">"ទីតាំងរបស់អ្នក"</string>
@@ -386,7 +386,7 @@
<string name="permdesc_readInputState" msgid="8387754901688728043">"ឲ្យកម្មវិធីមើលគ្រាប់ចុចដែលអ្នកចុចពេលមានអន្តរកម្មជាមួយកម្មវិធីផ្សេង (ដូចជា បញ្ចូលពាក្យសម្ងាត់)។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"ចងទៅវិធីសាស្ត្របញ្ចូល"</string>
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃវិធីសាស្ត្របញ្ចូល។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
- <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចងសេវាកម្មភាពមធ្យោបាយងាយស្រួល"</string>
+ <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចងសេវាកម្មភាពមធ្យោបាយងាយស្រួល"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មភាពងាយស្រួល។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindPrintService" msgid="8462815179572748761">"ចងសេវាកម្មបោះពុម្ព"</string>
<string name="permdesc_bindPrintService" msgid="7960067623209111135">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មធាតុក្រាហ្វិក។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
@@ -404,7 +404,7 @@
<string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅចំណុចប្រទាក់កម្រិតកំពូលរបស់សេវាកម្មអន្តរកម្មសំឡេង។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ភ្ជាប់ទៅការបង្ហាញពីចម្ងាយ"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅចំណុចប្រទាក់កម្រិតកំពូលនៃការបង្ហាញពីចម្ងាយ។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
- <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចងសេវាកម្មធាតុក្រាហ្វិក"</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>
@@ -412,7 +412,7 @@
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"ឲ្យម្ចាស់ផ្ញើគោលបំណងទៅអ្នកគ្រប់គ្រងឧបករណ៍។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"ភ្ជាប់ទៅការបញ្ចូលទូរទស្សន៍"</string>
<string name="permdesc_bindTvInput" msgid="2371008331852001924">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅចំណុចប្រទាក់កម្រិតខ្ពស់នៃការបញ្ចូលទូរទស្សន៍។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
- <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម ឬលុបកម្មវិធីគ្រប់គ្រងឧបករណ៍"</string>
+ <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម ឬលុបកម្មវិធីគ្រប់គ្រងឧបករណ៍"</string>
<string name="permdesc_manageDeviceAdmins" msgid="5025608167709942485">"អនុញ្ញាតឲ្យម្ចាស់បន្ថែម ឬលុបកម្មវិធីគ្រប់គ្រងឧបករណ៍សកម្មចេញ។ មិនគួរប្រើសម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"ប្ដូរទិសអេក្រង់"</string>
<string name="permdesc_setOrientation" msgid="3046126619316671476">"ឲ្យកម្មវិធីប្ដូរការបង្វិលអេក្រង់នៅពេលណាមួយ។ មិនចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
@@ -424,9 +424,9 @@
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"ឲ្យកម្មវិធីស្នើសញ្ញាដែលបានផ្ដល់ត្រូវផ្ញើទៅដំណើរការស្ថិតស្ថេរទាំងអស់។"</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"ធ្វើឲ្យកម្មវិធីដំណើរការជានិច្ច"</string>
<string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"ឲ្យកម្មវិធីធ្វើជាផ្នែកស្ថិតស្ថេរដោយខ្លួនឯងក្នុងអង្គចងចាំ។ វាអាចកំណត់អង្គចងចាំដែលអាចប្រើបានចំពោះកម្មវិធីផ្សេងៗ ដោយធ្វើឲ្យកុំព្យូទ័របន្ទះយឺត។"</string>
- <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យកម្មវិធី ធ្វើជាផ្នែកអចិន្ត្រៃយ៍នៃខ្លួនក្នុងអង្គចងចាំ។ វាអាចកម្រិតអង្គចងចាំអាចប្រើបាន ដើម្បីធ្វើឲ្យកម្មវិធីផ្សេងធ្វើឲ្យទូរស័ព្ទរបស់អ្នកយឺត។"</string>
+ <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យកម្មវិធី ធ្វើជាផ្នែកអចិន្ត្រៃយ៍នៃខ្លួនក្នុងអង្គចងចាំ។ វាអាចកម្រិតអង្គចងចាំអាចប្រើបាន ដើម្បីធ្វើឲ្យកម្មវិធីផ្សេងធ្វើឲ្យទូរស័ព្ទរបស់អ្នកយឺត។"</string>
<string name="permlab_deletePackages" msgid="184385129537705938">"លុបកម្មវិធី"</string>
- <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យកម្មវិធីលុបកញ្ចប់ Android ។ កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីលុបកម្មវិធីសំខាន់ៗ។"</string>
+ <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យកម្មវិធីលុបកញ្ចប់ Android ។ កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីលុបកម្មវិធីសំខាន់ៗ។ "</string>
<string name="permlab_clearAppUserData" msgid="274109191845842756">"លុបទិន្នន័យរបស់កម្មវិធីផ្សេង"</string>
<string name="permdesc_clearAppUserData" msgid="4625323684125459488">"ឲ្យកម្មវិធីសម្អាតទិន្នន័យអ្នកប្រើ។"</string>
<string name="permlab_deleteCacheFiles" msgid="3128665571837408675">"លុបឃ្លាំងសម្ងាត់កម្មវិធីផ្សេងៗ"</string>
@@ -477,7 +477,7 @@
<string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"ឲ្យកម្មវិធីកែទិន្នន័យអំពីទំនាក់ទំនងរបស់អ្នកដែលបានរក្សាទុកក្នុងកុំព្យូទ័របន្ទះ រួមមានប្រេកង់ដែលអ្នកបានហៅ អ៊ីមែល ឬទាក់ទងតាមវិធីផ្សេងៗជាមួយទំនាក់ទំនងជាក់លាក់។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីលុបទិន្នន័យទំនាក់ទំនងរបស់អ្នក។"</string>
<string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"ឲ្យកម្មវិធីកែទិន្នន័យអំពីទំនាក់ទំនងរបស់អ្នកដែលបានរក្សាទុកក្នុងទូរស័ព្ទរបស់អ្នក រួមមានប្រេកង់ដែលអ្នកបានហៅ អ៊ីមែល ឬបានទាក់ទងតាមវិធីផ្សេងៗជាមួយទំនាក់ទំនាក់ជាក់លាក់។ សិទ្ធិនេះឲ្យកម្មវិធីលុបទិន្នន័យទំនាក់ទំនង។"</string>
<string name="permlab_readCallLog" msgid="3478133184624102739">"អានកំណត់ហេតុហៅ"</string>
- <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យកម្មវិធីអានបញ្ជីហៅកុំព្យូទ័របន្ទះរបស់អ្នក រួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីរក្សាទុកទិន្នន័យបញ្ជីហៅរបស់អ្នក ហើយកម្មវិធីព្យាបាទអាចចែករំលែកទិន្នន័យបញ្ជីហៅដោយមិនឲ្យអ្នកដឹង។"</string>
+ <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យកម្មវិធីអានបញ្ជីហៅកុំព្យូទ័របន្ទះរបស់អ្នក រួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីរក្សាទុកទិន្នន័យបញ្ជីហៅរបស់អ្នក ហើយកម្មវិធីព្យាបាទអាចចែករំលែកទិន្នន័យបញ្ជីហៅដោយមិនឲ្យអ្នកដឹង។"</string>
<string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"ឲ្យកម្មវិធីអានបញ្ជីហៅទូរស័ព្ទរបស់អ្នក រួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីរក្សាទុកទិន្នន័យបញ្ជីហៅរបស់អ្នក ហើយកម្មវិធីព្យាបាទអាចចែករំលែកទិន្នន័យបញ្ជីហៅដោយមិនឲ្យអ្នកដឹង។"</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"សរសេរបញ្ជីហៅ"</string>
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ឲ្យកម្មវិធីកែបញ្ជីហៅកុំព្យូទ័របន្ទះរបស់អ្នករួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីលុប ឬកែបញ្ជីហៅរបស់អ្នក។"</string>
@@ -611,7 +611,7 @@
<string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"ឲ្យកម្មវិធីកំណត់ជំនួយទំហំផ្ទាំងរូបភាពប្រព័ន្ធ។"</string>
<string name="permlab_masterClear" msgid="2315750423139697397">"កំណត់ប្រព័ន្ធទៅលំនាំដើមរោងចក្រឡើងវិញ"</string>
<string name="permdesc_masterClear" msgid="3665380492633910226">"ឲ្យកម្មវិធីកំណត់ប្រព័ន្ធដូចការកំណត់ចេញពីរោងចក្រឡើងវិញពេញលេញ ដោយលុបទិន្នន័យ ការកំណត់រចនាសម្ព័ន្ធ និងកម្មវិធីបានដំឡើង។"</string>
- <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់ម៉ោង"</string>
+ <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់ម៉ោង"</string>
<string name="permdesc_setTime" product="tablet" msgid="1896341438151152881">"ឲ្យកម្មវិធីប្ដូរម៉ោងកុំព្យូទ័របន្ទះ។"</string>
<string name="permdesc_setTime" product="default" msgid="1855702730738020">"ឲ្យកម្មវិធីប្ដូរម៉ោងទូរស័ព្ទ។"</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"កំណត់តំបន់ពេលវេលា"</string>
@@ -777,7 +777,7 @@
<string-array name="organizationTypes">
<item msgid="7546335612189115615">"កន្លែងធ្វើការ"</item>
<item msgid="4378074129049520373">"ផ្សេងៗ"</item>
- <item msgid="3455047468583965104">"តាមតម្រូវការ"</item>
+ <item msgid="3455047468583965104">"តាមតម្រូវការ"</item>
</string-array>
<string-array name="imProtocols">
<item msgid="8595261363518459565">"AIM"</item>
@@ -793,7 +793,7 @@
<string name="phoneTypeHome" msgid="2570923463033985887">"ផ្ទះ"</string>
<string name="phoneTypeMobile" msgid="6501463557754751037">"ចល័ត"</string>
<string name="phoneTypeWork" msgid="8863939667059911633">"កន្លែងធ្វើការ"</string>
- <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារកន្លែងធ្វើការ"</string>
+ <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារកន្លែងធ្វើការ"</string>
<string name="phoneTypeFaxHome" msgid="2067265972322971467">"ទូរសារផ្ទះ"</string>
<string name="phoneTypePager" msgid="7582359955394921732">"ភេយ័រ"</string>
<string name="phoneTypeOther" msgid="1544425847868765990">"ផ្សេងៗ"</string>
@@ -918,7 +918,7 @@
<string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"ព្យាយាមលំនាំច្រើនពេក"</string>
<string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"ដើម្បីដោះសោ ចូលគណនី Google របស់អ្នក។"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"ឈ្មោះអ្នកប្រើ (អ៊ីមែល)"</string>
- <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់"</string>
+ <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ចូល"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"ឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។"</string>
<string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"ភ្លេចឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់របស់អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
@@ -963,7 +963,7 @@
<string name="factorytest_failed" msgid="5410270329114212041">"បានបរាជ័យក្នុងការសាកល្បងរោងចក្រ"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"សកម្មភាព FACTORY_TEST ត្រូវបានគាំទ្រសម្រាប់តែកញ្ចប់បានដំឡើងក្នុង /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"រកមិនឃើញកញ្ចប់ដែលផ្ដល់សកម្មភាព FACTORY_TEST ។"</string>
- <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់ផ្ដើមឡើងវិញ"</string>
+ <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់ផ្ដើមឡើងវិញ"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"ទំព័រមានចំណងជើង \"<xliff:g id="TITLE">%s</xliff:g>\" សរសេរ៖"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload_title" msgid="2619376555525116593">"បញ្ជាក់ការរុករក"</string>
@@ -1021,7 +1021,7 @@
<string name="prepend_shortcut_label" msgid="2572214461676015642">"ម៉ឺនុយ +"</string>
<string name="menu_space_shortcut_label" msgid="2410328639272162537">"ដកឃ្លា"</string>
<string name="menu_enter_shortcut_label" msgid="2743362785111309668">"enter"</string>
- <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប"</string>
+ <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប"</string>
<string name="search_go" msgid="8298016669822141719">"ស្វែងរក"</string>
<string name="searchview_description_search" msgid="6749826639098512120">"ស្វែងរក"</string>
<string name="searchview_description_query" msgid="5911778593125355124">"ស្វែងរកសំណួរ"</string>
@@ -1105,18 +1105,18 @@
<string name="preposition_for_date" msgid="9093949757757445117">"នៅ <xliff:g id="DATE">%s</xliff:g>"</string>
<string name="preposition_for_time" msgid="5506831244263083793">"នៅម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="preposition_for_year" msgid="5040395640711867177">"ក្នុងឆ្នាំ <xliff:g id="YEAR">%s</xliff:g>"</string>
- <string name="day" msgid="8144195776058119424">"ថ្ងៃ"</string>
+ <string name="day" msgid="8144195776058119424">"ថ្ងៃ"</string>
<string name="days" msgid="4774547661021344602">"ថ្ងៃ"</string>
<string name="hour" msgid="2126771916426189481">"ម៉ោង"</string>
<string name="hours" msgid="894424005266852993">"ម៉ោង"</string>
- <string name="minute" msgid="9148878657703769868">"នាទី"</string>
+ <string name="minute" msgid="9148878657703769868">"នាទី"</string>
<string name="minutes" msgid="5646001005827034509">"នាទី"</string>
- <string name="second" msgid="3184235808021478">"វិនាទី"</string>
+ <string name="second" msgid="3184235808021478">"វិនាទី"</string>
<string name="seconds" msgid="3161515347216589235">"វិនាទី"</string>
- <string name="week" msgid="5617961537173061583">"សប្ដាហ៍"</string>
- <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍"</string>
- <string name="year" msgid="4001118221013892076">"ឆ្នាំ"</string>
- <string name="years" msgid="6881577717993213522">"ឆ្នាំ"</string>
+ <string name="week" msgid="5617961537173061583">"សប្ដាហ៍"</string>
+ <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍"</string>
+ <string name="year" msgid="4001118221013892076">"ឆ្នាំ"</string>
+ <string name="years" msgid="6881577717993213522">"ឆ្នាំ"</string>
<plurals name="duration_seconds">
<item quantity="one" msgid="6962015528372969481">"1 វិនាទី"</item>
<item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> វិនាទី"</item>
@@ -1132,12 +1132,12 @@
<string name="VideoView_error_title" msgid="3534509135438353077">"បញ្ហាវីដេអូ"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"វីដេអូនេះមិនត្រឹមត្រូវសម្រាប់ចរន្តចូលឧបករណ៍នេះ។"</string>
<string name="VideoView_error_text_unknown" msgid="3450439155187810085">"មិនអាចចាក់វីដេអូនេះ។"</string>
- <string name="VideoView_error_button" msgid="2822238215100679592">"យល់ព្រម"</string>
+ <string name="VideoView_error_button" msgid="2822238215100679592">"យល់ព្រម"</string>
<string name="relative_time" msgid="1818557177829411417">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="noon" msgid="7245353528818587908">"រសៀល"</string>
<string name="Noon" msgid="3342127745230013127">"រសៀល"</string>
<string name="midnight" msgid="7166259508850457595">"កណ្ដាលអធ្រាត្រ"</string>
- <string name="Midnight" msgid="5630806906897892201">"កណ្ដាលអធ្រាត្រ"</string>
+ <string name="Midnight" msgid="5630806906897892201">"កណ្ដាលអធ្រាត្រ"</string>
<string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
<string name="selectAll" msgid="6876518925844129331">"ជ្រើសទាំងអស់"</string>
@@ -1154,13 +1154,13 @@
<string name="inputMethod" msgid="1653630062304567879">"វិធីសាស្ត្របញ្ចូល"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"សកម្មភាពអត្ថបទ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"អស់ទំហំផ្ទុក"</string>
- <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារប្រព័ន្ធមួយចំនួនអាចមិនដំណើរការ"</string>
+ <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារប្រព័ន្ធមួយចំនួនអាចមិនដំណើរការ"</string>
<string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងដំណើរការ"</string>
<string name="app_running_notification_text" msgid="4653586947747330058">"ប៉ះ ដើម្បីមើលព័ត៌មានបន្ថែម ឬបញ្ឈប់កម្មវិធី។"</string>
- <string name="ok" msgid="5970060430562524910">"យល់ព្រម"</string>
- <string name="cancel" msgid="6442560571259935130">"បោះបង់"</string>
- <string name="yes" msgid="5362982303337969312">"យល់ព្រម"</string>
- <string name="no" msgid="5141531044935541497">"បោះបង់"</string>
+ <string name="ok" msgid="5970060430562524910">"យល់ព្រម"</string>
+ <string name="cancel" msgid="6442560571259935130">"បោះបង់"</string>
+ <string name="yes" msgid="5362982303337969312">"យល់ព្រម"</string>
+ <string name="no" msgid="5141531044935541497">"បោះបង់"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"ប្រយ័ត្ន"</string>
<string name="loading" msgid="7933681260296021180">"កំពុងផ្ទុក..."</string>
<string name="capital_on" msgid="1544682755514494298">"បើក"</string>
@@ -1169,7 +1169,7 @@
<string name="whichHomeApplication" msgid="4616420172727326782">"ជ្រើសកម្មវិធីដើម"</string>
<string name="alwaysUse" msgid="4583018368000610438">"ប្រើតាមលំនាំដើមសម្រាប់សកម្មភាពនេះ។"</string>
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"សម្អាតលំនាំដើមក្នុងការកំណត់ប្រព័ន្ធ > កម្មវិធី > ទាញយក។"</string>
- <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើសសកម្មភាព"</string>
+ <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើសសកម្មភាព"</string>
<string name="chooseUsbActivity" msgid="6894748416073583509">"ជ្រើសកម្មវិធីសម្រាប់ឧបករណ៍យូអេសប៊ី"</string>
<string name="noApplications" msgid="2991814273936504689">"គ្មានកម្មវិធីអាចអនុវត្តសកម្មភាពនេះ។"</string>
<string name="aerr_title" msgid="1905800560317137752"></string>
@@ -1180,7 +1180,7 @@
<string name="anr_activity_process" msgid="5776209883299089767">"សកម្មភាព <xliff:g id="ACTIVITY">%1$s</xliff:g> មិនឆ្លើយតប។\n\nតើអ្នកចង់បិទវា?"</string>
<string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> មិនឆ្លើយតប។ តើអ្នកចង់បិទវា?"</string>
<string name="anr_process" msgid="6513209874880517125">"ដំណើរការ <xliff:g id="PROCESS">%1$s</xliff:g> មិនឆ្លើយតប។ \n\nតើអ្នកចង់បិទវាឬ?"</string>
- <string name="force_close" msgid="8346072094521265605">"យល់ព្រម"</string>
+ <string name="force_close" msgid="8346072094521265605">"យល់ព្រម"</string>
<string name="report" msgid="4060218260984795706">"រាយការណ៍"</string>
<string name="wait" msgid="7147118217226317732">"រង់ចាំ"</string>
<string name="webpage_unresponsive" msgid="3272758351138122503">"ទំព័រក្លាយជាមិនឆ្លើយតប។\n\nតើអ្នកចង់បិទវា?"</string>
@@ -1262,19 +1262,19 @@
<string name="sms_short_code_details" msgid="3492025719868078457"><font fgcolor="#ffffb060">"នេះអាចកាត់លុយ"</font>" លើគណនីចល័តរបស់អ្នក។"</string>
<string name="sms_premium_short_code_details" msgid="5523826349105123687"><font fgcolor="#ffffb060">"វានឹងគិតថ្លៃសេវាកម្មលើគណនីចល័តរបស់អ្នក។"</font></string>
<string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"ផ្ញើ"</string>
- <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះបង់"</string>
+ <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះបង់"</string>
<string name="sms_short_code_remember_choice" msgid="5289538592272218136">"ចងចាំជម្រើសរបស់ខ្ញុំ"</string>
<string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"អ្នកអាចប្ដូរវាពេលក្រោយក្នុងការកំណត់ > កម្មវិធី"</string>
<string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"អនុញ្ញាតជានិច្ច"</string>
<string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"កុំអនុញ្ញាត"</string>
<string name="sim_removed_title" msgid="6227712319223226185">"បានដកស៊ីមកាតចេញ"</string>
- <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញចល័តនឹងប្រើលែងបានរហូតដល់អ្នកចាប់ផ្ដើមជាមួយស៊ីមកាតដែលបាបញ្ចូលត្រឹមត្រូវ។"</string>
+ <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញចល័តនឹងប្រើលែងបានរហូតដល់អ្នកចាប់ផ្ដើមជាមួយស៊ីមកាតដែលបាបញ្ចូលត្រឹមត្រូវ។"</string>
<string name="sim_done_button" msgid="827949989369963775">"រួចរាល់"</string>
<string name="sim_added_title" msgid="3719670512889674693">"បានបន្ថែមស៊ីមកាត"</string>
<string name="sim_added_message" msgid="6599945301141050216">"ចាប់ផ្ដើមឧបករណ៍របស់អ្នកឡើងវិញ ដើម្បីចូលដំណើរការបណ្ដាញចល័ត។"</string>
<string name="sim_restart_button" msgid="4722407842815232347">"ចាប់ផ្ដើមឡើងវិញ"</string>
- <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់ម៉ោង"</string>
- <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់កាលបរិច្ឆេទ"</string>
+ <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់ម៉ោង"</string>
+ <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់កាលបរិច្ឆេទ"</string>
<string name="date_time_set" msgid="5777075614321087758">"កំណត់"</string>
<string name="date_time_done" msgid="2507683751759308828">"រួចរាល់"</string>
<string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"ថ្មី៖ "</font></string>
@@ -1352,7 +1352,7 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"ឲ្យកម្មវិធីដកសេវាកម្មនៃកម្មវិធីផ្ទុកលំនាំដើម ដើម្បីចម្លងមាតិកា។ មិនសម្រាប់ប្រើដោយកម្មវិធីលំនាំដើម។"</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"នាំផ្លូវលទ្ធផលមេឌៀ"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"ឲ្យកម្មវិធីនាំផ្លូវលទ្ធផលមេឌៀទៅឧបករណ៍ខាងក្រៅផ្សេង។"</string>
- <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូលដំណើរការឧបករណ៍ផ្ទុកសុវត្ថិភាព"</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូលដំណើរការឧបករណ៍ផ្ទុកសុវត្ថិភាព"</string>
<string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"ឲ្យកម្មវិធីចូលការផ្ទុកមានសុវត្ថិភាព keguard ។"</string>
<string name="permlab_control_keyguard" msgid="172195184207828387">"ពិនិត្យការបង្ហាញ និងលាក់ការការពារ"</string>
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"ឲ្យកម្មវិធីគ្រប់គ្រង keguard ។"</string>
@@ -1367,7 +1367,7 @@
<string name="ime_action_go" msgid="8320845651737369027">"ទៅ"</string>
<string name="ime_action_search" msgid="658110271822807811">"ស្វែងរក"</string>
<string name="ime_action_send" msgid="2316166556349314424">"ផ្ញើ"</string>
- <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់"</string>
+ <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់"</string>
<string name="ime_action_done" msgid="8971516117910934605">"រួចរាល់"</string>
<string name="ime_action_previous" msgid="1443550039250105948">"មុន"</string>
<string name="ime_action_default" msgid="2840921885558045721">"អនុវត្ត"</string>
@@ -1376,7 +1376,7 @@
<string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"កម្មវិធីមួយ ឬច្រើនដូចខាងក្រោមស្នើសិទ្ធិ ដើម្បីចូលគណនីរបស់អ្នកឥឡូវ និងពេលអនាគត។"</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"តើអ្នកចង់អនុញ្ញាតសំណើនេះ?"</string>
<string name="grant_permissions_header_text" msgid="6874497408201826708">"ស្នើចូល"</string>
- <string name="allow" msgid="7225948811296386551">"អនុញ្ញាត"</string>
+ <string name="allow" msgid="7225948811296386551">"អនុញ្ញាត"</string>
<string name="deny" msgid="2081879885755434506">"បដិសេធ"</string>
<string name="permission_request_notification_title" msgid="6486759795926237907">"បានស្នើសិទ្ធិ"</string>
<string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"បានស្នើសិទ្ធិ\nសម្រាប់គណនី <xliff:g id="ACCOUNT">%s</xliff:g> ។"</string>
@@ -1399,12 +1399,12 @@
<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>
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បានបើករបៀបរថយន្ត"</string>
<string name="car_mode_disable_notification_message" msgid="8035230537563503262">"ប៉ះ ដើម្បីចេញពីរបៀបរថយន្ត។"</string>
<string name="tethered_notification_title" msgid="3146694234398202601">"ភ្ជាប់ ឬហតស្ពតសកម្ម"</string>
<string name="tethered_notification_message" msgid="6857031760103062982">"ប៉ះ ដើម្បីរៀបចំ។"</string>
<string name="back_button_label" msgid="2300470004503343439">"ថយក្រោយ"</string>
- <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់"</string>
+ <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់"</string>
<string name="skip_button_label" msgid="1275362299471631819">"រំលង"</string>
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"ការប្រើទិន្នន័យចល័តខ្ពស់"</string>
<string name="throttle_warning_notification_message" msgid="3340822228599337743">"ប៉ះ ដើម្បីស្វែងយល់បន្ថែមអំពីការប្រើទិន្នន័យចល័ត។"</string>
@@ -1430,7 +1430,7 @@
<string name="media_shared" product="nosdcard" msgid="5830814349250834225">"ឧបករណ៍ផ្ទុកយូអេសប៊ីបច្ចុប្បន្នកំពុងប្រើដោយកុំព្យូទ័រ។"</string>
<string name="media_shared" product="default" msgid="5706130568133540435">"បច្ចុប្បន្នកាតអេសឌីកំពុងប្រើដោយកុំព្យូទ័រ"</string>
<string name="media_unknown_state" msgid="729192782197290385">"មិនស្គាល់ស្ថានភាពមេឌៀខាងក្រៅ។"</string>
- <string name="share" msgid="1778686618230011964">"ចែករំលែក"</string>
+ <string name="share" msgid="1778686618230011964">"ចែករំលែក"</string>
<string name="find" msgid="4808270900322985960">"រក"</string>
<string name="websearch" msgid="4337157977400211589">"ស្វែងរកតាមបណ្ដាញ"</string>
<string name="find_next" msgid="5742124618942193978">"រកបន្ទាប់"</string>
@@ -1446,7 +1446,7 @@
<string name="sync_undo_deletes" msgid="2941317360600338602">"មិនធ្វើការលុបវិញ"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"មិនធ្វើអ្វីទេឥឡូវ"</string>
<string name="choose_account_label" msgid="5655203089746423927">"ជ្រើសគណនី"</string>
- <string name="add_account_label" msgid="2935267344849993553">"បន្ថែមគណនីថ្មី"</string>
+ <string name="add_account_label" msgid="2935267344849993553">"បន្ថែមគណនីថ្មី"</string>
<string name="add_account_button_label" msgid="3611982894853435874">"បន្ថែមគណនី"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"បង្កើន"</string>
<string name="number_picker_decrement_button" msgid="476050778386779067">"បន្ថយ"</string>
@@ -1465,15 +1465,15 @@
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"បង្កើនឆ្នាំ"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"បន្ថយឆ្នាំ"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
- <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string>
<string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string>
<string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូររបៀប"</string>
<string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
<string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
- <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើសកម្មវិធី"</string>
+ <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើសកម្មវិធី"</string>
<string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"មិនអាចចាប់ផ្ដើម <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
- <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែកជាមួយ"</string>
+ <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែកជាមួយ"</string>
<string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"ចែករំលែកជាមួយ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
<string name="content_description_sliding_handle" msgid="415975056159262248">"គ្រប់គ្រងការរុញ។ ប៉ះ & សង្កត់។"</string>
<string name="description_target_unlock_tablet" msgid="3833195335629795055">"អូស ដើម្បីដោះសោ។"</string>
@@ -1487,7 +1487,7 @@
<string name="storage_internal" msgid="4891916833657929263">"ឧបករណ៍ផ្ទុកខាងក្នុង"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"កាតអេសឌី"</string>
<string name="storage_usb" msgid="3017954059538517278">"ឧបករណ៍ផ្ទុកយូអេសប៊ី"</string>
- <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string>
+ <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"ការព្រមានប្រើទិន្នន័យ"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"ប៉ះ ដើម្បីមើលការប្រើ និងការកំណត់។"</string>
<string name="data_usage_3g_limit_title" msgid="7093334419518706686">"បានបិទទិន្នន័យ 2G-3G"</string>
@@ -1544,7 +1544,7 @@
<string name="media_route_status_available" msgid="6983258067194649391">"ទំនេរ"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"មិនទំនេរ"</string>
<string name="media_route_status_in_use" msgid="4533786031090198063">"កំពុងប្រើ"</string>
- <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់ជាប់"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់ជាប់"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"អេក្រង់ HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"#<xliff:g id="ID">%1$d</xliff:g> ត្រួតគ្នា"</string>
<string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
@@ -1576,7 +1576,7 @@
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាមលំនាំច្រើនពេក"</string>
<string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បីដោះសោ ចូលក្នុងគណនី Google ។"</string>
<string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះអ្នកប្រើ (អ៊ីម៉ែល)"</string>
- <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
<string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string>
<string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេចឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់របស់អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
@@ -1685,7 +1685,7 @@
<string name="mediasize_japanese_you4" msgid="2091777168747058008">"You4"</string>
<string name="mediasize_unknown_portrait" msgid="3088043641616409762">"មិនស្គាល់បញ្ឈរ"</string>
<string name="mediasize_unknown_landscape" msgid="4876995327029361552">"មិនស្គាល់ទេសភាព"</string>
- <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បានបោះបង់"</string>
+ <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បានបោះបង់"</string>
<string name="write_fail_reason_cannot_write" msgid="8132505417935337724">"កំហុសក្នុងការសរសេរមាតិកា"</string>
<string name="reason_unknown" msgid="6048913880184628119">"មិនស្គាល់"</string>
<string name="reason_service_unavailable" msgid="7824008732243903268">"មិនបានបើកសេវាកម្មបោះពុម្ព"</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 08ba7e1..7ec1376 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -1697,7 +1697,7 @@
<string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"PIN ປະຈຸບັນ"</string>
<string name="restr_pin_enter_new_pin" msgid="5959606691619959184">"ລະຫັດ PIN ໃໝ່"</string>
<string name="restr_pin_confirm_pin" msgid="8501523829633146239">"ຢືນຢັນລະຫັດ PIN ໃໝ່"</string>
- <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາລັບການປັບປຸງຂໍ້ຈໍາກັດ"</string>
+ <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາລັບການປັບປຸງຂໍ້ຈໍາກັດ"</string>
<string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"PIN ບໍ່ກົງກັນ. ລອງໃໝ່ອີກຄັ້ງ."</string>
<string name="restr_pin_error_too_short" msgid="8173982756265777792">"PIN ສັ້ນເກີນໄປ. ຕ້ອງມີຢ່າງໜ້ອຍ 4 ຫຼັກ."</string>
<plurals name="restr_pin_countdown">
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7a6832e..cedb92d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3343,6 +3343,8 @@
<attr name="thumb" format="reference" />
<!-- An offset for the thumb that allows it to extend out of the range of the track. -->
<attr name="thumbOffset" format="dimension" />
+ <!-- Whether to split the track and leave a gap for the thumb drawable. -->
+ <attr name="splitTrack" format="boolean" />
</declare-styleable>
<declare-styleable name="StackView">
@@ -6393,6 +6395,8 @@
<attr name="switchMinWidth" format="dimension" />
<!-- Minimum space between the switch and caption text -->
<attr name="switchPadding" format="dimension" />
+ <!-- Whether to split the track and leave a gap for the thumb drawable. -->
+ <attr name="splitTrack" />
</declare-styleable>
<declare-styleable name="Pointer">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f39155b..83cbb74 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1393,8 +1393,10 @@
<item>com.android.inputmethod.latin</item>
</string-array>
- <string-array name="config_notificationScorers">
- <item>com.android.internal.notification.PeopleNotificationScorer</item>
+ <!-- The list of classes that should be added to the notification ranking pipline.
+ See {@link com.android.server.notification.NotificationSignalExtractortor} -->
+ <string-array name="config_notificationSignalExtractors">
+ <item>com.android.server.notification.ValidateNotificationPeople</item>
</string-array>
<!-- Flag indicating that this device does not rotate and will always remain in its default
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index ecb22ae..dc5efea 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2169,6 +2169,7 @@
<public type="attr" name="toId" />
<public type="attr" name="fromId" />
<public type="attr" name="reversible" />
+ <public type="attr" name="splitTrack" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index 88a2a9f..e693673 100644
--- a/core/res/res/values/styles_quantum.xml
+++ b/core/res/res/values/styles_quantum.xml
@@ -455,10 +455,10 @@
<style name="Widget.Quantum.CompoundButton.Switch">
<item name="track">@drawable/switch_track_quantum</item>
<item name="thumb">@drawable/switch_inner_quantum</item>
+ <item name="splitTrack">true</item>
<item name="switchTextAppearance">@style/TextAppearance.Quantum.Widget.Switch</item>
<item name="textOn"></item>
<item name="textOff"></item>
- <item name="thumbTextPadding">12dip</item>
<item name="switchMinWidth">72dip</item>
<item name="switchPadding">16dip</item>
<item name="background">?attr/selectableItemBackground</item>
@@ -572,10 +572,8 @@
<item name="indeterminateOnly">false</item>
<item name="progressDrawable">@drawable/scrubber_progress_horizontal_quantum</item>
<item name="indeterminateDrawable">@drawable/scrubber_progress_horizontal_quantum</item>
- <item name="minHeight">13dip</item>
- <item name="maxHeight">13dip</item>
<item name="thumb">@drawable/scrubber_control_selector_quantum</item>
- <item name="thumbOffset">16dip</item>
+ <item name="splitTrack">true</item>
<item name="focusable">true</item>
<item name="paddingStart">16dip</item>
<item name="paddingEnd">16dip</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5a78bfe..1057cc2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1655,7 +1655,7 @@
<java-symbol type="id" name="button_always" />
<java-symbol type="integer" name="config_globalActionsKeyTimeout" />
<java-symbol type="integer" name="config_maxResolverActivityColumns" />
- <java-symbol type="array" name="config_notificationScorers" />
+ <java-symbol type="array" name="config_notificationSignalExtractors" />
<java-symbol type="layout" name="notification_quantum_action" />
<java-symbol type="layout" name="notification_quantum_action_list" />
diff --git a/core/tests/coretests/src/android/net/LinkSocketTest.java b/core/tests/coretests/src/android/net/LinkSocketTest.java
deleted file mode 100644
index af77d63..0000000
--- a/core/tests/coretests/src/android/net/LinkSocketTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2010 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.LinkSocket;
-import android.test.suitebuilder.annotation.SmallTest;
-import junit.framework.TestCase;
-
-/**
- * Test LinkSocket
- */
-public class LinkSocketTest extends TestCase {
-
- @SmallTest
- public void testBasic() throws Exception {
- LinkSocket ls;
-
- ls = new LinkSocket();
- ls.close();
- }
-
- @SmallTest
- public void testLinkCapabilities() throws Exception {
- LinkCapabilities lc;
-
- lc = new LinkCapabilities();
- assertEquals(0, lc.size());
- assertEquals(true, lc.isEmpty());
- }
-}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index ff4ab98..2da86154 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -46,7 +46,7 @@
* This lets you create a drawable based on an XML vector graphic It can be
* defined in an XML file with the <code><vector></code> element.
* <p/>
- * The vector drawable has 6 elements:
+ * The vector drawable has the following elements:
* <p/>
* <dl>
* <dt><code><vector></code></dt>
@@ -59,15 +59,15 @@
* <dd>Used to defined the size of the virtual canvas the paths are drawn on.
* The size is defined using the attributes <code>android:viewportHeight</code>
* <code>android:viewportWidth</code></dd>
- * <dt><code><group></code></dt>
- * <dd>Defines the static 2D image.</dd>
* <dt><code><path></code></dt>
- * <dd>Defines paths to be drawn. The path elements must be within a group
+ * <dd>Defines paths to be drawn. Multiple paths can be defined in one xml file.
+ * The paths are drawn in the order of their definition order.
* <dl>
* <dt><code>android:name</code>
* <dd>Defines the name of the path.</dd></dt>
* <dt><code>android:pathData</code>
- * <dd>Defines path string.</dd></dt>
+ * <dd>Defines path string. This is using exactly same format as "d" attribute
+ * in the SVG's path data</dd></dt>
* <dt><code>android:fill</code>
* <dd>Defines the color to fill the path (none if not present).</dd></dt>
* <dt><code>android:stroke</code>
@@ -108,7 +108,6 @@
private static final String SHAPE_SIZE = "size";
private static final String SHAPE_VIEWPORT = "viewport";
- private static final String SHAPE_GROUP = "group";
private static final String SHAPE_PATH = "path";
private static final String SHAPE_VECTOR = "vector";
@@ -266,10 +265,9 @@
boolean noSizeTag = true;
boolean noViewportTag = true;
- boolean noGroupTag = true;
boolean noPathTag = true;
- VGroup currentGroup = null;
+ VGroup currentGroup = new VGroup();
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
@@ -286,10 +284,6 @@
} else if (SHAPE_VIEWPORT.equals(tagName)) {
pathRenderer.parseViewport(res, attrs);
noViewportTag = false;
- } else if (SHAPE_GROUP.equals(tagName)) {
- currentGroup = new VGroup();
- pathRenderer.mGroupList.add(currentGroup);
- noGroupTag = false;
} else if (SHAPE_VECTOR.equals(tagName)) {
final TypedArray a = res.obtainAttributes(attrs, R.styleable.VectorDrawable);
@@ -310,7 +304,7 @@
eventType = parser.next();
}
- if (noSizeTag || noViewportTag || noGroupTag || noPathTag) {
+ if (noSizeTag || noViewportTag || noPathTag) {
final StringBuffer tag = new StringBuffer();
if (noSizeTag) {
@@ -324,13 +318,6 @@
tag.append(SHAPE_SIZE);
}
- if (noGroupTag) {
- if (tag.length() > 0) {
- tag.append(" & ");
- }
- tag.append(SHAPE_GROUP);
- }
-
if (noPathTag) {
if (tag.length() > 0) {
tag.append(" or ");
@@ -341,6 +328,7 @@
throw new XmlPullParserException("no " + tag + " defined");
}
+ pathRenderer.mCurrentGroup = currentGroup;
// post parse cleanup
pathRenderer.parseFinish();
return pathRenderer;
@@ -394,7 +382,7 @@
private Paint mFillPaint;
private PathMeasure mPathMeasure;
- final ArrayList<VGroup> mGroupList = new ArrayList<VGroup>();
+ private VGroup mCurrentGroup = new VGroup();
float mBaseWidth = 1;
float mBaseHeight = 1;
@@ -405,7 +393,7 @@
}
public VPathRenderer(VPathRenderer copy) {
- mGroupList.addAll(copy.mGroupList);
+ mCurrentGroup = copy.mCurrentGroup;
if (copy.mCurrentPaths != null) {
mCurrentPaths = new VPath[copy.mCurrentPaths.length];
for (int i = 0; i < mCurrentPaths.length; i++) {
@@ -420,32 +408,24 @@
}
public boolean canApplyTheme() {
- final ArrayList<VGroup> groups = mGroupList;
- for (int i = groups.size() - 1; i >= 0; i--) {
- final ArrayList<VPath> paths = groups.get(i).mVGList;
- for (int j = paths.size() - 1; j >= 0; j--) {
- final VPath path = paths.get(j);
- if (path.canApplyTheme()) {
- return true;
- }
+ final ArrayList<VPath> paths = mCurrentGroup.mVGList;
+ for (int j = paths.size() - 1; j >= 0; j--) {
+ final VPath path = paths.get(j);
+ if (path.canApplyTheme()) {
+ return true;
}
}
-
return false;
}
public void applyTheme(Theme t) {
- final ArrayList<VGroup> groups = mGroupList;
- for (int i = groups.size() - 1; i >= 0; i--) {
- final ArrayList<VPath> paths = groups.get(i).mVGList;
- for (int j = paths.size() - 1; j >= 0; j--) {
- final VPath path = paths.get(j);
- if (path.canApplyTheme()) {
- path.applyTheme(t);
- }
+ final ArrayList<VPath> paths = mCurrentGroup.mVGList;
+ for (int j = paths.size() - 1; j >= 0; j--) {
+ final VPath path = paths.get(j);
+ if (path.canApplyTheme()) {
+ path.applyTheme(t);
}
}
-
}
public void draw(Canvas canvas, int w, int h) {
@@ -537,11 +517,11 @@
}
/**
- * Build the "current" path based on the first group
+ * Build the "current" path based on the current group
* TODO: improve memory use & performance or move to C++
*/
public void parseFinish() {
- final Collection<VPath> paths = mGroupList.get(0).getPaths();
+ final Collection<VPath> paths = mCurrentGroup.getPaths();
mCurrentPaths = paths.toArray(new VPath[paths.size()]);
for (int i = 0; i < mCurrentPaths.length; i++) {
mCurrentPaths[i] = new VPath(mCurrentPaths[i]);
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 098753b..6aad5fb 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2452,15 +2452,19 @@
if (mcc != 0) {
if (res.size() > 0) res.append("-");
- res.appendFormat("%dmcc", dtohs(mcc));
+ res.appendFormat("mcc%d", dtohs(mcc));
}
if (mnc != 0) {
if (res.size() > 0) res.append("-");
- res.appendFormat("%dmnc", dtohs(mnc));
+ res.appendFormat("mnc%d", dtohs(mnc));
}
+
char localeStr[RESTABLE_MAX_LOCALE_LEN];
getBcp47Locale(localeStr);
- res.append(localeStr);
+ if (strlen(localeStr) > 0) {
+ if (res.size() > 0) res.append("-");
+ res.append(localeStr);
+ }
if ((screenLayout&MASK_LAYOUTDIR) != 0) {
if (res.size() > 0) res.append("-");
@@ -2627,6 +2631,20 @@
break;
}
}
+ if ((inputFlags&MASK_KEYSHIDDEN) != 0) {
+ if (res.size() > 0) res.append("-");
+ switch (inputFlags&MASK_KEYSHIDDEN) {
+ case ResTable_config::KEYSHIDDEN_NO:
+ res.append("keysexposed");
+ break;
+ case ResTable_config::KEYSHIDDEN_YES:
+ res.append("keyshidden");
+ break;
+ case ResTable_config::KEYSHIDDEN_SOFT:
+ res.append("keyssoft");
+ break;
+ }
+ }
if (keyboard != KEYBOARD_ANY) {
if (res.size() > 0) res.append("-");
switch (keyboard) {
@@ -2644,17 +2662,18 @@
break;
}
}
- if ((inputFlags&MASK_KEYSHIDDEN) != 0) {
+ if ((inputFlags&MASK_NAVHIDDEN) != 0) {
if (res.size() > 0) res.append("-");
- switch (inputFlags&MASK_KEYSHIDDEN) {
- case ResTable_config::KEYSHIDDEN_NO:
- res.append("keysexposed");
+ switch (inputFlags&MASK_NAVHIDDEN) {
+ case ResTable_config::NAVHIDDEN_NO:
+ res.append("navexposed");
break;
- case ResTable_config::KEYSHIDDEN_YES:
- res.append("keyshidden");
+ case ResTable_config::NAVHIDDEN_YES:
+ res.append("navhidden");
break;
- case ResTable_config::KEYSHIDDEN_SOFT:
- res.append("keyssoft");
+ default:
+ res.appendFormat("inputFlagsNavHidden=%d",
+ dtohs(inputFlags&MASK_NAVHIDDEN));
break;
}
}
@@ -2678,21 +2697,6 @@
break;
}
}
- if ((inputFlags&MASK_NAVHIDDEN) != 0) {
- if (res.size() > 0) res.append("-");
- switch (inputFlags&MASK_NAVHIDDEN) {
- case ResTable_config::NAVHIDDEN_NO:
- res.append("navsexposed");
- break;
- case ResTable_config::NAVHIDDEN_YES:
- res.append("navhidden");
- break;
- default:
- res.appendFormat("inputFlagsNavHidden=%d",
- dtohs(inputFlags&MASK_NAVHIDDEN));
- break;
- }
- }
if (screenSize != 0) {
if (res.size() > 0) res.append("-");
res.appendFormat("%dx%d", dtohs(screenWidth), dtohs(screenHeight));
@@ -5503,7 +5507,25 @@
if (package == NULL) {
return (mError=NO_MEMORY);
}
-
+
+ if (idmap_id == 0) {
+ err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
+ header->dataEnd-(base+dtohl(pkg->typeStrings)));
+ if (err != NO_ERROR) {
+ delete group;
+ delete package;
+ return (mError=err);
+ }
+
+ err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
+ header->dataEnd-(base+dtohl(pkg->keyStrings)));
+ if (err != NO_ERROR) {
+ delete group;
+ delete package;
+ return (mError=err);
+ }
+ }
+
if (id == 0) {
// This is a library so assign an ID
id = mNextPackageId++;
@@ -5521,21 +5543,6 @@
return (mError=NO_MEMORY);
}
- err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
- header->dataEnd-(base+dtohl(pkg->typeStrings)));
- if (err != NO_ERROR) {
- delete group;
- delete package;
- return (mError=err);
- }
- err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
- header->dataEnd-(base+dtohl(pkg->keyStrings)));
- if (err != NO_ERROR) {
- delete group;
- delete package;
- return (mError=err);
- }
-
//printf("Adding new package id %d at index %d\n", id, idx);
err = mPackageGroups.add(group);
if (err < NO_ERROR) {
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
new file mode 100644
index 0000000..bb23a36
--- /dev/null
+++ b/media/java/android/media/AudioAttributes.java
@@ -0,0 +1,444 @@
+/*
+ * 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;
+
+import android.annotation.IntDef;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A class to encapsulate a collection of attributes describing information about an audio
+ * player or recorder.
+ */
+public final class AudioAttributes {
+ private final static String TAG = "AudioAttributes";
+
+ /**
+ * Content type value to use when the content type is unknown, or other than the ones defined.
+ */
+ public final static int CONTENT_TYPE_UNKNOWN = 0;
+ /**
+ * Content type value to use when the content type is speech.
+ */
+ public final static int CONTENT_TYPE_SPEECH = 1;
+ /**
+ * Content type value to use when the content type is music.
+ */
+ public final static int CONTENT_TYPE_MUSIC = 2;
+ /**
+ * Content type value to use when the content type is a soundtrack, typically accompanying
+ * a movie or TV program.
+ */
+ public final static int CONTENT_TYPE_MOVIE = 3;
+ /**
+ * Content type value to use when the content type is a sound used to accompany a user
+ * action, such as a beep or sound effect expressing a key click, or event, such as the
+ * type of a sound for a bonus being received in a game. These sounds are mostly synthesized
+ * or short Foley sounds.
+ */
+ public final static int CONTENT_TYPE_SONIFICATION = 4;
+
+ /**
+ * Usage value to use when the usage is unknown.
+ */
+ public final static int USAGE_UNKNOWN = 0;
+ /**
+ * Usage value to use when the usage is media, such as music, or movie
+ * soundtracks.
+ */
+ public final static int USAGE_MEDIA = 1;
+ /**
+ * Usage value to use when the usage is voice communications, such as telephony
+ * or VoIP.
+ */
+ public final static int USAGE_VOICE_COMMUNICATION = 2;
+ /**
+ * Usage value to use when the usage is in-call signalling, such as with
+ * a "busy" beep, or DTMF tones.
+ */
+ public final static int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3;
+ /**
+ * Usage value to use when the usage is an alarm (e.g. wake-up alarm).
+ */
+ public final static int USAGE_ALARM = 4;
+ /**
+ * Usage value to use when the usage is notification. See other
+ * notification usages for more specialized uses.
+ */
+ public final static int USAGE_NOTIFICATION = 5;
+ /**
+ * Usage value to use when the usage is telephony ringtone.
+ */
+ public final static int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6;
+ /**
+ * Usage value to use when the usage is a request to enter/end a
+ * communication, such as a VoIP communication or video-conference.
+ */
+ public final static int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7;
+ /**
+ * Usage value to use when the usage is notification for an "instant"
+ * communication such as a chat, or SMS.
+ */
+ public final static int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8;
+ /**
+ * Usage value to use when the usage is notification for a
+ * non-immediate type of communication such as e-mail.
+ */
+ public final static int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9;
+ /**
+ * Usage value to use when the usage is to attract the user's attention,
+ * such as a reminder or low battery warning.
+ */
+ public final static int USAGE_NOTIFICATION_EVENT = 10;
+ /**
+ * Usage value to use when the usage is for accessibility, such as with
+ * a screen reader.
+ */
+ public final static int USAGE_ASSISTANCE_ACCESSIBILITY = 11;
+ /**
+ * Usage value to use when the usage is driving or navigation directions.
+ */
+ public final static int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12;
+ /**
+ * Usage value to use when the usage is sonification, such as with user
+ * interface sounds.
+ */
+ public final static int USAGE_ASSISTANCE_SONIFICATION = 13;
+ /**
+ * Usage value to use when the usage is for game audio.
+ */
+ public final static int USAGE_GAME = 14;
+
+ /**
+ * Flag defining a behavior where the audibility of the sound will be ensured by the system.
+ */
+ public final static int FLAG_AUDIBILITY_ENFORCED = 0x1 << 0;
+ /**
+ * @hide
+ * Flag defining a behavior where the playback of the sound is ensured without
+ * degradation only when going to a secure sink.
+ */
+ // FIXME not guaranteed yet
+ // TODO add OR to getFlags() when supported and in public API
+ public final static int FLAG_SECURE = 0x1 << 1;
+ /**
+ * @hide
+ * Flag to enable when the stream is associated with SCO usage.
+ * Internal use only for dealing with legacy STREAM_BLUETOOTH_SCO
+ */
+ public final static int FLAG_SCO = 0x1 << 2;
+
+
+ private int mUsage = USAGE_UNKNOWN;
+ private int mContentType = CONTENT_TYPE_UNKNOWN;
+ private int mFlags = 0x0;
+ private HashSet<String> mTags;
+
+ private AudioAttributes() {
+ }
+
+ /**
+ * Return the content type.
+ * @return one of the values that can be set in {@link Builder#setContentType(int)}
+ */
+ public int getContentType() {
+ return mContentType;
+ }
+
+ /**
+ * Return the usage.
+ * @return one of the values that can be set in {@link Builder#setUsage(int)}
+ */
+ public int getUsage() {
+ return mUsage;
+ }
+
+ /**
+ * Return the flags.
+ * @return a combined mask of all flags
+ */
+ public int getFlags() {
+ // only return the flags that are public
+ return (mFlags & (FLAG_AUDIBILITY_ENFORCED));
+ }
+
+ /**
+ * @hide
+ * Return all the flags, even the non-public ones.
+ * Internal use only
+ * @return a combined mask of all flags
+ */
+ public int getAllFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Return the set of tags.
+ * @return a read-only set of all tags stored as strings.
+ */
+ public Set<String> getTags() {
+ return Collections.unmodifiableSet(mTags);
+ }
+
+ /**
+ * Builder class for {@link AudioAttributes} objects.
+ */
+ public static class Builder {
+ private int mUsage = USAGE_UNKNOWN;
+ private int mContentType = CONTENT_TYPE_UNKNOWN;
+ private int mFlags = 0x0;
+ private HashSet<String> mTags = new HashSet<String>();
+
+ /**
+ * Constructs a new Builder with the defaults.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Constructs a new Builder from a given AudioAttributes
+ * @param aa the AudioAttributes object whose data will be reused in the new Builder.
+ */
+ @SuppressWarnings("unchecked") // for cloning of mTags
+ public Builder(AudioAttributes aa) {
+ mUsage = aa.mUsage;
+ mContentType = aa.mContentType;
+ mFlags = aa.mFlags;
+ mTags = (HashSet<String>) aa.mTags.clone();
+ }
+
+ /**
+ * Combines all of the attributes that have been set and return a new
+ * {@link AudioAttributes} object.
+ * @return a new {@link AudioAttributes} object
+ */
+ @SuppressWarnings("unchecked") // for cloning of mTags
+ public AudioAttributes build() {
+ AudioAttributes aa = new AudioAttributes();
+ aa.mContentType = mContentType;
+ aa.mUsage = mUsage;
+ aa.mFlags = mFlags;
+ aa.mTags = (HashSet<String>) mTags.clone();
+ return aa;
+ }
+
+ /**
+ * Sets the attribute describing what is the intended use of the the audio signal,
+ * such as alarm or ringtone.
+ * @param usage one of {@link AudioAttributes#USAGE_UNKNOWN},
+ * {@link AudioAttributes#USAGE_MEDIA},
+ * {@link AudioAttributes#USAGE_VOICE_COMMUNICATION},
+ * {@link AudioAttributes#USAGE_VOICE_COMMUNICATION_SIGNALLING},
+ * {@link AudioAttributes#USAGE_ALARM}, {@link AudioAttributes#USAGE_NOTIFICATION},
+ * {@link AudioAttributes#USAGE_NOTIFICATION_TELEPHONY_RINGTONE},
+ * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_REQUEST},
+ * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_INSTANT},
+ * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_DELAYED},
+ * {@link AudioAttributes#USAGE_NOTIFICATION_EVENT},
+ * {@link AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY},
+ * {@link AudioAttributes#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE},
+ * {@link AudioAttributes#USAGE_ASSISTANCE_SONIFICATION},
+ * {@link AudioAttributes#USAGE_GAME}.
+ * @return the same Builder instance.
+ */
+ public Builder setUsage(@AttributeUsage int usage) {
+ switch (usage) {
+ case USAGE_UNKNOWN:
+ case USAGE_MEDIA:
+ case USAGE_VOICE_COMMUNICATION:
+ case USAGE_VOICE_COMMUNICATION_SIGNALLING:
+ case USAGE_ALARM:
+ case USAGE_NOTIFICATION:
+ case USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
+ case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+ case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+ case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+ case USAGE_NOTIFICATION_EVENT:
+ case USAGE_ASSISTANCE_ACCESSIBILITY:
+ case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
+ case USAGE_ASSISTANCE_SONIFICATION:
+ case USAGE_GAME:
+ mUsage = usage;
+ break;
+ default:
+ mUsage = USAGE_UNKNOWN;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the attribute describing the content type of the audio signal, such as speech,
+ * or music.
+ * @param contentType the content type values, one of
+ * {@link AudioAttributes#CONTENT_TYPE_MOVIE},
+ * {@link AudioAttributes#CONTENT_TYPE_MUSIC},
+ * {@link AudioAttributes#CONTENT_TYPE_SONIFICATION},
+ * {@link AudioAttributes#CONTENT_TYPE_SPEECH},
+ * {@link AudioAttributes#CONTENT_TYPE_UNKNOWN}.
+ * @return the same Builder instance.
+ */
+ public Builder setContentType(@AttributeContentType int contentType) {
+ switch (contentType) {
+ case CONTENT_TYPE_UNKNOWN:
+ case CONTENT_TYPE_MOVIE:
+ case CONTENT_TYPE_MUSIC:
+ case CONTENT_TYPE_SONIFICATION:
+ case CONTENT_TYPE_SPEECH:
+ mContentType = contentType;
+ break;
+ default:
+ mUsage = CONTENT_TYPE_UNKNOWN;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the combination of flags.
+ * @param flags the {@link AudioAttributes#FLAG_AUDIBILITY_ENFORCED} flag.
+ * @return the same Builder instance.
+ */
+ public Builder setFlags(int flags) {
+ flags &= (AudioAttributes.FLAG_AUDIBILITY_ENFORCED | AudioAttributes.FLAG_SCO
+ | AudioAttributes.FLAG_SECURE);
+ mFlags |= flags;
+ return this;
+ }
+
+ /**
+ * Add a custom tag stored as a string
+ * @param tag
+ * @return the same Builder instance.
+ */
+ public Builder addTag(String tag) {
+ mTags.add(tag);
+ return this;
+ }
+
+ /**
+ * Adds attributes inferred from the legacy stream types.
+ * @param streamType one of {@link AudioManager#STREAM_VOICE_CALL},
+ * {@link AudioManager#STREAM_SYSTEM}, {@link AudioManager#STREAM_RING},
+ * {@link AudioManager#STREAM_MUSIC}, {@link AudioManager#STREAM_ALARM},
+ * or {@link AudioManager#STREAM_NOTIFICATION}.
+ * @return the same Builder instance.
+ */
+ public Builder setLegacyStreamType(int streamType) {
+ return setInternalLegacyStreamType(streamType);
+ }
+
+ /**
+ * @hide
+ * For internal framework use only, enables building from hidden stream types.
+ * @param streamType
+ * @return the same Builder instance.
+ */
+ public Builder setInternalLegacyStreamType(int streamType) {
+ switch(streamType) {
+ case AudioSystem.STREAM_VOICE_CALL:
+ mContentType = CONTENT_TYPE_SPEECH;
+ mUsage = USAGE_VOICE_COMMUNICATION;
+ break;
+ case AudioSystem.STREAM_SYSTEM_ENFORCED:
+ mFlags |= FLAG_AUDIBILITY_ENFORCED;
+ // intended fall through, attributes in common with STREAM_SYSTEM
+ case AudioSystem.STREAM_SYSTEM:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mUsage = USAGE_ASSISTANCE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_RING:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mUsage = USAGE_NOTIFICATION_TELEPHONY_RINGTONE;
+ break;
+ case AudioSystem.STREAM_MUSIC:
+ mContentType = CONTENT_TYPE_MUSIC;
+ mUsage = USAGE_MEDIA;
+ break;
+ case AudioSystem.STREAM_ALARM:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mUsage = USAGE_ALARM;
+ break;
+ case AudioSystem.STREAM_NOTIFICATION:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mUsage = USAGE_NOTIFICATION;
+ break;
+ case AudioSystem.STREAM_BLUETOOTH_SCO:
+ mContentType = CONTENT_TYPE_SPEECH;
+ mUsage = USAGE_VOICE_COMMUNICATION;
+ mFlags |= FLAG_SCO;
+ break;
+ case AudioSystem.STREAM_DTMF:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mUsage = USAGE_VOICE_COMMUNICATION_SIGNALLING;
+ break;
+ case AudioSystem.STREAM_TTS:
+ mContentType = CONTENT_TYPE_SPEECH;
+ mUsage = USAGE_ASSISTANCE_ACCESSIBILITY;
+ break;
+ default:
+ Log.e(TAG, "Invalid stream type " + streamType + " in for AudioAttributes");
+ }
+ return this;
+ }
+ };
+
+ /** @hide */
+ @Override
+ public String toString () {
+ return new String("AudioAttributes:"
+ + " usage=" + mUsage
+ + " content=" + mContentType
+ + " flags=0x" + Integer.toHexString(mFlags)
+ + " tags=" + mTags);
+ }
+
+ /** @hide */
+ @IntDef({
+ USAGE_UNKNOWN,
+ USAGE_MEDIA,
+ USAGE_VOICE_COMMUNICATION,
+ USAGE_VOICE_COMMUNICATION_SIGNALLING,
+ USAGE_ALARM,
+ USAGE_NOTIFICATION,
+ USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
+ USAGE_NOTIFICATION_COMMUNICATION_REQUEST,
+ USAGE_NOTIFICATION_COMMUNICATION_INSTANT,
+ USAGE_NOTIFICATION_COMMUNICATION_DELAYED,
+ USAGE_NOTIFICATION_EVENT,
+ USAGE_ASSISTANCE_ACCESSIBILITY,
+ USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+ USAGE_ASSISTANCE_SONIFICATION,
+ USAGE_GAME
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AttributeUsage {}
+
+ /** @hide */
+ @IntDef({
+ CONTENT_TYPE_UNKNOWN,
+ CONTENT_TYPE_SPEECH,
+ CONTENT_TYPE_MUSIC,
+ CONTENT_TYPE_MOVIE,
+ CONTENT_TYPE_SONIFICATION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AttributeContentType {}
+}
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 7a8c22e..e341647 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -25,6 +25,6 @@
* @hide
*/
interface ISessionManager {
- ISession createSession(String packageName, in ISessionCallback cb, String tag);
- List<IBinder> getSessions(in ComponentName compName);
+ ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
+ List<IBinder> getSessions(in ComponentName compName, int userId);
}
\ No newline at end of file
diff --git a/media/java/android/media/session/SessionManager.java b/media/java/android/media/session/SessionManager.java
index fd022fc..1eb3b7a 100644
--- a/media/java/android/media/session/SessionManager.java
+++ b/media/java/android/media/session/SessionManager.java
@@ -22,6 +22,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.util.Log;
@@ -65,10 +66,25 @@
* @return a {@link Session} for the new session
*/
public Session createSession(String tag) {
+ return createSessionAsUser(tag, UserHandle.myUserId());
+ }
+
+ /**
+ * Creates a new session as the specified user. To create a session as a
+ * user other than your own you must hold the
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+ * permission.
+ *
+ * @param tag A short name for debugging purposes
+ * @param userId The user id to create the session as.
+ * @return a {@link Session} for the new session
+ * @hide
+ */
+ public Session createSessionAsUser(String tag, int userId) {
try {
Session.CallbackStub cbStub = new Session.CallbackStub();
Session session = new Session(mService
- .createSession(mContext.getPackageName(), cbStub, tag), cbStub);
+ .createSession(mContext.getPackageName(), cbStub, tag, userId), cbStub);
cbStub.setMediaSession(session);
return session;
@@ -91,9 +107,27 @@
* @return A list of controllers for ongoing sessions
*/
public List<SessionController> getActiveSessions(ComponentName notificationListener) {
+ return getActiveSessionsForUser(notificationListener, UserHandle.myUserId());
+ }
+
+ /**
+ * Get active sessions for a specific user. To retrieve actions for a user
+ * other than your own you must hold the
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission
+ * in addition to any other requirements. If you are an enabled notification
+ * listener you may only get sessions for the users you are enabled for.
+ *
+ * @param notificationListener The enabled notification listener component.
+ * May be null.
+ * @param userId The user id to fetch sessions for.
+ * @return A list of controllers for ongoing sessions.
+ * @hide
+ */
+ public List<SessionController> getActiveSessionsForUser(ComponentName notificationListener,
+ int userId) {
ArrayList<SessionController> controllers = new ArrayList<SessionController>();
try {
- List<IBinder> binders = mService.getSessions(notificationListener);
+ List<IBinder> binders = mService.getSessions(notificationListener, userId);
for (int i = binders.size() - 1; i >= 0; i--) {
SessionController controller = SessionController.fromBinder(ISessionController.Stub
.asInterface(binders.get(i)));
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index e8944ec..8c9030d 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -20,14 +20,14 @@
<string name="title_open" msgid="4353228937663917801">"បើកពី"</string>
<string name="title_save" msgid="2433679664882857999">"រក្សាទុកទៅ"</string>
<string name="menu_create_dir" msgid="5947289605844398389">"បង្កើតថត"</string>
- <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាពក្រឡា"</string>
+ <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាពក្រឡា"</string>
<string name="menu_list" msgid="7279285939892417279">"ទិដ្ឋភាពបញ្ជី"</string>
<string name="menu_sort" msgid="7677740407158414452">"តម្រៀបតាម"</string>
<string name="menu_search" msgid="3816712084502856974">"ស្វែងរក"</string>
<string name="menu_settings" msgid="6008033148948428823">"ការកំណត់"</string>
<string name="menu_open" msgid="432922957274920903">"បើក"</string>
<string name="menu_save" msgid="2394743337684426338">"រក្សាទុក"</string>
- <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក"</string>
+ <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក"</string>
<string name="menu_delete" msgid="8138799623850614177">"លុប"</string>
<string name="menu_select" msgid="8711270657353563424">"ជ្រើស \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"បានជ្រើស <xliff:g id="COUNT">%1$d</xliff:g>"</string>
@@ -48,7 +48,7 @@
<string name="pref_advanced_devices" msgid="903257239609301276">"បង្ហាញឧបករណ៍កម្រិតខ្ពស់"</string>
<string name="pref_file_size" msgid="2826879315743961459">"បង្ហាញទំហំឯកសារ"</string>
<string name="pref_device_size" msgid="3542106883278997222">"បង្ហាញទំហំឧបករណ៍"</string>
- <string name="empty" msgid="7858882803708117596">"គ្មានធាតុ"</string>
+ <string name="empty" msgid="7858882803708117596">"គ្មានធាតុ"</string>
<string name="toast_no_application" msgid="1339885974067891667">"មិនអាចបើកឯកសារ"</string>
<string name="toast_failed_delete" msgid="2180678019407244069">"មិនអាចលុបឯកសារមួយចំនួន"</string>
<string name="share_via" msgid="8966594246261344259">"ចែករំលែកតាម"</string>
diff --git a/packages/Keyguard/res/values-km-rKH/strings.xml b/packages/Keyguard/res/values-km-rKH/strings.xml
index a2e54a7..18b59f1 100644
--- a/packages/Keyguard/res/values-km-rKH/strings.xml
+++ b/packages/Keyguard/res/values-km-rKH/strings.xml
@@ -83,7 +83,7 @@
<string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
<string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
- <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string>
<string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string>
<string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូររបៀប"</string>
@@ -120,7 +120,7 @@
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាមលំនាំច្រើនពេក"</string>
<string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បីដោះសោ ចូលក្នុងគណនី Google ។"</string>
<string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះអ្នកប្រើ (អ៊ីម៉ែល)"</string>
- <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
<string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string>
<string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេចឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់របស់អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
diff --git a/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml
index c89f9bf..ba3c042 100644
--- a/packages/PrintSpooler/res/values-km-rKH/strings.xml
+++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml
@@ -60,7 +60,7 @@
</plurals>
<string name="cancel" msgid="4373674107267141885">"បោះបង់"</string>
<string name="restart" msgid="2472034227037808749">"ចាប់ផ្ដើមឡើងវិញ"</string>
- <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មានការភ្ជាប់ទៅម៉ាស៊ីនបោះពុម្ព"</string>
+ <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មានការភ្ជាប់ទៅម៉ាស៊ីនបោះពុម្ព"</string>
<string name="reason_unknown" msgid="5507940196503246139">"មិនស្គាល់"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – មិនអាចប្រើបាន"</string>
<string name="print_error_default_message" msgid="8568506918983980567">"មិនអាចបង្កើតការងារបោះពុម្ព"</string>
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_contrast_alpha.png
deleted file mode 100644
index 0f9dfc7..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_contrast_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_contrast_alpha.png
deleted file mode 100644
index a4dd087..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_contrast_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_contrast_alpha.png
deleted file mode 100644
index 9331e52..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_contrast_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_contrast_alpha.png
deleted file mode 100644
index 82c3842..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_contrast_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_off.xml b/packages/SystemUI/res/drawable/ic_qs_contrast_off.xml
deleted file mode 100644
index 5f65d8a..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_contrast_off.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_qs_contrast_alpha"
- android:tint="@color/ic_qs_off" />
diff --git a/packages/SystemUI/res/drawable/ic_settings_24dp.xml b/packages/SystemUI/res/drawable/ic_settings_24dp.xml
index 5c38a22..a2f7822 100644
--- a/packages/SystemUI/res/drawable/ic_settings_24dp.xml
+++ b/packages/SystemUI/res/drawable/ic_settings_24dp.xml
@@ -20,10 +20,10 @@
<viewport android:viewportWidth="24.0"
android:viewportHeight="24.0"/>
-<group>
+
<path
android:pathData="M19.4,13.0c0.0,-0.3 0.1,-0.6 0.1,-1.0s0.0,-0.7 -0.1,-1.0l2.1,-1.7c0.2,-0.2 0.2,-0.4 0.1,-0.6l-2.0,-3.5C19.5,5.1 19.3,5.0 19.0,5.1l-2.5,1.0c-0.5,-0.4 -1.1,-0.7 -1.7,-1.0l-0.4,-2.6C14.5,2.2 14.2,2.0 14.0,2.0l-4.0,0.0C9.8,2.0 9.5,2.2 9.5,2.4L9.1,5.1C8.5,5.3 8.0,5.7 7.4,6.1L5.0,5.1C4.7,5.0 4.5,5.1 4.3,5.3l-2.0,3.5C2.2,8.9 2.3,9.2 2.5,9.4L4.6,11.0c0.0,0.3 -0.1,0.6 -0.1,1.0s0.0,0.7 0.1,1.0l-2.1,1.7c-0.2,0.2 -0.2,0.4 -0.1,0.6l2.0,3.5C4.5,18.9 4.7,19.0 5.0,18.9l2.5,-1.0c0.5,0.4 1.1,0.7 1.7,1.0l0.4,2.6c0.0,0.2 0.2,0.4 0.5,0.4l4.0,0.0c0.2,0.0 0.5,-0.2 0.5,-0.4l0.4,-2.6c0.6,-0.3 1.2,-0.6 1.7,-1.0l2.5,1.0c0.2,0.1 0.5,0.0 0.6,-0.2l2.0,-3.5c0.1,-0.2 0.1,-0.5 -0.1,-0.6L19.4,13.0zM12.0,15.5c-1.9,0.0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5s3.5,1.6 3.5,3.5S13.9,15.5 12.0,15.5z"
android:fill="#ffffffff"
/>
-</group>
+
</vector>
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_dark.xml b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
index c015cc8..744795e 100644
--- a/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
+++ b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
@@ -24,12 +24,11 @@
android:viewportHeight="100"
android:viewportWidth="100" />
- <group>
- <path
- android:name="x"
- android:pathData="M0,0L100,100M0,100L100,0z"
- android:stroke="@color/recents_task_bar_dark_dismiss_color"
- android:strokeWidth="8.0"
- android:strokeLineCap="square" />
- </group>
+ <path
+ android:name="x"
+ android:pathData="M0,0L100,100M0,100L100,0z"
+ android:stroke="@color/recents_task_bar_dark_dismiss_color"
+ android:strokeWidth="8.0"
+ android:strokeLineCap="square" />
+
</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_light.xml b/packages/SystemUI/res/drawable/recents_dismiss_light.xml
index 9c93db9..96bfbe1 100644
--- a/packages/SystemUI/res/drawable/recents_dismiss_light.xml
+++ b/packages/SystemUI/res/drawable/recents_dismiss_light.xml
@@ -24,12 +24,12 @@
android:viewportHeight="100"
android:viewportWidth="100" />
- <group>
- <path
- android:name="x"
- android:pathData="M0,0L100,100M0,100L100,0z"
- android:stroke="@color/recents_task_bar_light_dismiss_color"
- android:strokeWidth="8.0"
- android:strokeLineCap="square" />
- </group>
+
+ <path
+ android:name="x"
+ android:pathData="M0,0L100,100M0,100L100,0z"
+ android:stroke="@color/recents_task_bar_light_dismiss_color"
+ android:strokeWidth="8.0"
+ android:strokeLineCap="square" />
+
</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 8ec48c8..eb66a59 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Onlangse programme"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Deursoek"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Foon"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Knoppie vir wissel van invoermetode."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Versoenbaarheid-zoem se knoppie."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoem kleiner na groter skerm."</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 7aaaaf3..a3eca6a 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"የቅርብ ጊዜ መተግበሪያዎች"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"ፈልግ"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"ካሜራ"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"ስልክ"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"የግቤት ስልት አዝራር ቀይር"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"የተኳኋኝአጉላ አዝራር።"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"አነስተኛውን ማያ ወደ ትልቅ አጉላ።"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index febcab6..e03a2f9 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Скорошни приложения"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Търсене"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Камера"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Телефон"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Бутон за превключване на метода на въвеждане."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Бутон за промяна на мащаба с цел съвместимост."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Промяна на мащаба на екрана от по-малък до по-голям."</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 658ece0..e50a155 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Aplicacions recents"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Cerca"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Càmera"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Telèfon"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Botó de canvi del mètode d\'entrada."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botó de zoom de compatibilitat."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Amplia menys com més gran sigui la pantalla."</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index c7d2703..a2d9d9c 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Nové aplikace"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Hledat"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Fotoaparát"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Tlačítko přepnutí metody zadávání"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tlačítko úpravy velikosti z důvodu kompatibility"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zvětšit menší obrázek na větší obrazovku."</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 51198e6..00041a2 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Aplicaciones recientes"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Buscar"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Cámara"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Teléfono"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Botón Cambiar método de entrada"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botón de zoom de compatibilidad"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de pantalla más pequeña a más grande"</string>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 0f95c60..4c42a32 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Hiljutised rakendused"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Otsing"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Kaamera"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Sisestusmeetodi vahetamise nupp."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Sobivussuumi nupp."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Suumi suuremale ekraanile vähem."</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index f36d04b..a9c6af7a 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"برنامههای اخیر"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"جستجو"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"دوربین"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"تلفن"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"کلید تغییر روش ورود متن."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"دکمه بزرگنمایی سازگار."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"بزرگنمایی از صفحههای کوچک تا بزرگ."</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 4ae3851..8ddf070 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Viimeaikaiset sovellukset"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Haku"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Puhelin"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Syöttötavan vaihtopainike."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Yhteensopivuuszoomaus-painike."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoomaa pienemmältä suuremmalle ruudulle."</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index c55a5fc..69b5acc 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Applications récentes"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Rechercher"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Appareil photo"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Téléphone"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Bouton \"Changer le mode de saisie\""</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Bouton \"Zoom de compatibilité\""</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilité avec la taille de l\'écran"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 1121918..85f817b 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Applications récentes"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Rechercher"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Appareil photo"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Téléphoner"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Bouton \"Changer le mode de saisie\""</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Bouton \"Zoom de compatibilité\""</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilité avec la taille de l\'écran"</string>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 7a8ab10..8c26a8a 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Վերջին ծրագրերը"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Որոնել"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Ֆոտոխցիկ"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Հեռախոս"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Միացնել մուտքագրման եղանակի կոճակը:"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Համատեղելիության խոշորացման կոճակը:"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Դիտափոխել փոքրից ավելի մեծ էկրան:"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 08ffc54..e1625076 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"אפליקציות אחרונות"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"חפש"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"מצלמה"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"טלפון"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"לחצן החלפת שיטת קלט."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"לחצן מרחק מתצוגה של תאימות."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"שנה מרחק מתצוגה של מסך קטן לגדול יותר."</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 2caa069..a9e7935 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"最近使ったアプリ"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"検索"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"カメラ"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"電話"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"入力方法の切り替えボタン。"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"互換ズームボタン。"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"小さい画面から大きい画面に拡大。"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 62e40d0..c56864b 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"ბოლოს გამოყენებული აპები"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"ძიება"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"კამერა"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"ტელეფონი"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"შეყვანის მეთოდის გადართვის ღილაკი."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"თავსებადი მასშტაბირების ღილაკი."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"შეცვალეთ პატარა ეკრანი უფრო დიდით."</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 35a6917..a5008b3 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -64,7 +64,7 @@
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"កំពុងរក្សាទុករូបថតអេក្រង់…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"កំពុងរក្សាទុករូបថតអេក្រង់..."</string>
<string name="screenshot_saving_text" msgid="2419718443411738818">"រូបថតអេក្រង់កំពុងត្រូវបានរក្សាទុក។"</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"បានចាប់យករូបថតអេក្រង់។"</string>
+ <string name="screenshot_saved_title" msgid="6461865960961414961">"បានចាប់យករូបថតអេក្រង់។"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"ប៉ះ ដើម្បីមើលរូបថតអេក្រង់របស់អ្នក។"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"មិនអាចចាប់យករូបថតអេក្រង់។"</string>
<string name="screenshot_failed_text" msgid="8134011269572415402">"មិនអាចរក្សាទុករូបថតអេក្រង់។ ឧបករណ៍ផ្ទុកអាចកំពុងប្រើ។"</string>
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"កម្មវិធីថ្មីៗ"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"ស្វែងរក"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"ម៉ាស៊ីនថត"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"ទូរស័ព្ទ"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"ប្ដូរប៊ូតុងវិធីសាស្ត្របញ្ចូល។"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ប៊ូតុងពង្រីកត្រូវគ្នា។"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ពង្រីក/បង្រួមអេក្រង់ពីទៅធំ"</string>
@@ -141,7 +140,7 @@
<string name="accessibility_remove_notification" msgid="3603099514902182350">"សម្អាតការជូនដំណឹង។"</string>
<string name="accessibility_gps_enabled" msgid="3511469499240123019">"បានបើក GPS ។"</string>
<string name="accessibility_gps_acquiring" msgid="8959333351058967158">"ទទួល GPS ។"</string>
- <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បានបើកម៉ាស៊ីនអង្គុលីលេខ"</string>
+ <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បានបើកម៉ាស៊ីនអង្គុលីលេខ"</string>
<string name="accessibility_ringer_vibrate" msgid="666585363364155055">"កម្មវិធីរោទ៍ញ័រ។"</string>
<string name="accessibility_ringer_silent" msgid="9061243307939135383">"កម្មវិធីរោទ៍ស្ងាត់។"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> បដិសេធ។"</string>
@@ -188,7 +187,7 @@
<string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"ចាក់សោបញ្ឈរ"</string>
<string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"ចាក់សោផ្ដេក"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"វិធីសាស្ត្របញ្ចូល"</string>
- <string name="quick_settings_location_label" msgid="5011327048748762257">"ទីតាំង"</string>
+ <string name="quick_settings_location_label" msgid="5011327048748762257">"ទីតាំង"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"ទីតាំងបានបិទ"</string>
<string name="quick_settings_media_device_label" msgid="1302906836372603762">"ឧបករណ៍មេឌៀ"</string>
<string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
@@ -209,7 +208,7 @@
<string name="recents_empty_message" msgid="2269156590813544104">"ថ្មីៗ"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"ព័ត៌មានកម្មវិធី"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"ស្វែងរក"</string>
- <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញអាច\nត្រូវបានត្រួតពិនិត្យ"</string>
+ <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញអាច\nត្រូវបានត្រួតពិនិត្យ"</string>
<string name="description_target_search" msgid="3091587249776033139">"ស្វែងរក"</string>
<string name="description_direction_up" msgid="7169032478259485180">"រុញឡើងលើដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string>
<string name="description_direction_left" msgid="7207478719805562165">"រុញទៅឆ្វេងដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index d7cd13b..bd7a257 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"최근에 사용한 앱"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"검색"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"카메라"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"전화"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"입력 방법 버튼을 전환합니다."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"호환성 확대/축소 버튼입니다."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"작은 화면을 큰 화면으로 확대합니다."</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 87087b4..b160a5a 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -181,7 +181,7 @@
<string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetooth"</string>
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"„Bluetooth“ (<xliff:g id="NUMBER">%d</xliff:g> įreng.)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"„Bluetooth“ išjungta"</string>
- <string name="quick_settings_brightness_label" msgid="6968372297018755815">"Skaistis"</string>
+ <string name="quick_settings_brightness_label" msgid="6968372297018755815">"Šviesumas"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Automatiškai sukti"</string>
<string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Sukimas užrakintas"</string>
<string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Užrakinta stačia padėtis"</string>
@@ -200,7 +200,7 @@
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Tinklo nėra"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"„Wi-Fi“ išjungta"</string>
<string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Perduoti ekraną"</string>
- <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Skaistis"</string>
+ <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Šviesumas"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATINIS"</string>
<string name="quick_settings_inversion_label" msgid="1666358784283020762">"Spalvų inversijos režimas"</string>
<string name="quick_settings_contrast_label" msgid="3319507551689108692">"Patobulinto kontrasto režimas"</string>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index 3e7b660..4669d99 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Сүүлийн апп"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Хайх"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Камер"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Утас"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Оруулах аргыг сэлгэх товч."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Тохиромжтой өсгөх товч."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Жижгээс том дэлгэцрүү өсгөх."</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index af50786..60e5527 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Aplikasi terbaharu"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Cari"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Butang tukar kaedah input."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Butang zum keserasian."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Skrin zum lebih kecil kepada lebih besar."</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index e51341d..00bea9c 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Ostatnie aplikacje"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Szukaj"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Aparat"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Przycisk przełączania metody wprowadzania."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Przycisk powiększenia na potrzeby zgodności."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Powiększa mniejszy ekran do większego."</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 162d763..2337a7d 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Aplicativos recentes"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Pesquisar"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Câmera"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefone"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Alterar botão do método de entrada."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botão de zoom da compatibilidade."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Aumentar a tela com zoom."</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 75ba35b..4f2c471 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Aplicaţii recente"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Căutați"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Cameră foto"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Buton pentru comutarea metodei de introducere."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Buton zoom pentru compatibilitate."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Faceţi zoom de la o imagine mai mică la una mai mare."</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 6bdc6f4..837e98e 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Недавние приложения"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Поиск"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Камера"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Телефон."</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Кнопка переключения способа ввода."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Кнопка масштабирования (режим совместимости)"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Уменьшение изображения для увеличения свободного места на экране."</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 12c90ec..ae70ed6 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Nové aplikácie"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Hľadať"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Fotoaparát"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefón"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Tlačidlo prepnutia metódy vstupu."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tlačidlo úpravy veľkosti z dôvodu kompatibility."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zväčšiť menší obrázok na väčšiu obrazovku."</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index b626f78..2713cf9 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -76,8 +76,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Programu za hivi karibuni"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Tafuta"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Simu"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Swichi kitufe cha mbinu ingizi."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Kichupo cha kukuza kwa utangamanifu"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kuza kidogo kwa skrini kubwa."</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 6cb4a48..4dc3d22 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"แอปพลิเคชันล่าสุด"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"ค้นหา"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"กล้องถ่ายรูป"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"โทรศัพท์"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"ปุ่มสลับวิธีการป้อนข้อมูล"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ปุ่มซูมที่ใช้งานร่วมกันได้"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ซูมหน้าจอให้มีขนาดใหญ่ขึ้น"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 17e720e..e50f723 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Kamakailang apps"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Hanapin"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Camera"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Telepono"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Ilipat ang button na pamamaraan ng pag-input."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Button ng zoom ng pagiging tugma."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Mag-zoom nang mas maliit sa mas malaking screen."</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 1bf033f5..9100055 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Son uygulamalar"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Ara"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Giriş yöntemini değiştirme düğmesi."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Uyumluluk zum düğmesi."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Daha büyük ekrana daha küçük yakınlaştır."</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 8df2449..a4aadb3 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"Ứng dụng gần đây"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"Tìm kiếm"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"Máy ảnh"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"Điện thoại"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Nút chuyển phương thức nhập."</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Nút thu phóng khả năng tương thích."</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Thu phóng màn hình lớn hơn hoặc nhỏ hơn."</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 8643ca8..a5ae2b7 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"最近运行的应用"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"搜索"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"相机"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"电话"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"输入法切换按钮。"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"兼容性缩放按钮。"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"将小屏幕的图片放大在较大屏幕上显示。"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 07b7841..1e3f455 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -78,8 +78,7 @@
<string name="accessibility_recent" msgid="8571350598987952883">"最近使用的應用程式"</string>
<string name="accessibility_search_light" msgid="1103867596330271848">"搜尋"</string>
<string name="accessibility_camera_button" msgid="8064671582820358152">"相機"</string>
- <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
- <skip />
+ <string name="accessibility_phone_button" msgid="6738112589538563574">"電話"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"切換輸入法按鈕。"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"相容性縮放按鈕。"</string>
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"將較小螢幕的畫面放大在較大螢幕上顯示。"</string>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3f0a60f..3d3cdf6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -504,8 +504,6 @@
<string name="quick_settings_brightness_dialog_auto_brightness_label">AUTO</string>
<!-- QuickSettings: Label for the toggle that controls whether display inversion is enabled. [CHAR LIMIT=NONE] -->
<string name="quick_settings_inversion_label">Color inversion mode</string>
- <!-- QuickSettings: Label for the toggle that controls whether display contrast enhancement is enabled. [CHAR LIMIT=NONE] -->
- <string name="quick_settings_contrast_label">Enhanced contrast mode</string>
<!-- QuickSettings: Label for the toggle that controls whether display color correction is enabled. [CHAR LIMIT=NONE] -->
<string name="quick_settings_color_space_label">Color correction mode</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 281bd2d..4bd0e1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -112,7 +112,7 @@
* @return The desired notification height.
*/
public int getIntrinsicHeight() {
- return mActualHeight;
+ return getHeight();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
new file mode 100644
index 0000000..0606a94
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+/**
+ * Utility class to calculate general fling animation when the finger is released.
+ */
+public class FlingAnimationUtils {
+
+ private static final float LINEAR_OUT_SLOW_IN_Y2 = 0.35f;
+ private static final float MAX_LENGTH_SECONDS = 0.4f;
+ private static final float MIN_VELOCITY_DP_PER_SECOND = 250;
+
+ /**
+ * Crazy math. http://en.wikipedia.org/wiki/B%C3%A9zier_curve
+ */
+ private static final float LINEAR_OUT_SLOW_IN_START_GRADIENT = 1/LINEAR_OUT_SLOW_IN_Y2;
+
+ private Interpolator mLinearOutSlowIn;
+ private Interpolator mFastOutSlowIn;
+ private float mMinVelocityPxPerSecond;
+
+ public FlingAnimationUtils(Context ctx) {
+ mLinearOutSlowIn = new PathInterpolator(0, 0, LINEAR_OUT_SLOW_IN_Y2, 1);
+ mFastOutSlowIn
+ = AnimationUtils.loadInterpolator(ctx, android.R.interpolator.fast_out_slow_in);
+ mMinVelocityPxPerSecond
+ = MIN_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
+ }
+
+ /**
+ * Applies the interpolator and length to the animator, such that the fling animation is
+ * consistent with the finger motion.
+ *
+ * @param animator the animator to apply
+ * @param currValue the current value
+ * @param endValue the end value of the animator
+ * @param velocity the current velocity of the motion
+ */
+ public void apply(ValueAnimator animator, float currValue, float endValue, float velocity) {
+ float diff = Math.abs(endValue - currValue);
+ float velAbs = Math.abs(velocity);
+ float durationSeconds = LINEAR_OUT_SLOW_IN_START_GRADIENT * diff / velAbs;
+ if (durationSeconds <= MAX_LENGTH_SECONDS) {
+ animator.setInterpolator(mLinearOutSlowIn);
+ } else if (velAbs >= mMinVelocityPxPerSecond) {
+
+ // Cross fade between fast-out-slow-in and linear interpolator with current velocity.
+ durationSeconds = MAX_LENGTH_SECONDS;
+ VelocityInterpolator velocityInterpolator
+ = new VelocityInterpolator(durationSeconds, velAbs, diff);
+ InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
+ velocityInterpolator, mLinearOutSlowIn, mLinearOutSlowIn);
+ animator.setInterpolator(superInterpolator);
+ } else {
+
+ // Just use a normal interpolator which doesn't take the velocity into account.
+ durationSeconds = MAX_LENGTH_SECONDS;
+ animator.setInterpolator(mFastOutSlowIn);
+ }
+ animator.setDuration((long) (durationSeconds * 1000));
+ }
+
+ /**
+ * An interpolator which interpolates two interpolators with an interpolator.
+ */
+ private static final class InterpolatorInterpolator implements Interpolator {
+
+ private Interpolator mInterpolator1;
+ private Interpolator mInterpolator2;
+ private Interpolator mCrossfader;
+
+ InterpolatorInterpolator(Interpolator interpolator1, Interpolator interpolator2,
+ Interpolator crossfader) {
+ mInterpolator1 = interpolator1;
+ mInterpolator2 = interpolator2;
+ mCrossfader = crossfader;
+ }
+
+ @Override
+ public float getInterpolation(float input) {
+ float t = mCrossfader.getInterpolation(input);
+ return (1 - t) * mInterpolator1.getInterpolation(input)
+ + t * mInterpolator2.getInterpolation(input);
+ }
+ }
+
+ /**
+ * An interpolator which interpolates with a fixed velocity.
+ */
+ private static final class VelocityInterpolator implements Interpolator {
+
+ private float mDurationSeconds;
+ private float mVelocity;
+ private float mDiff;
+
+ private VelocityInterpolator(float durationSeconds, float velocity, float diff) {
+ mDurationSeconds = durationSeconds;
+ mVelocity = velocity;
+ mDiff = diff;
+ }
+
+ @Override
+ public float getInterpolation(float input) {
+ float time = input * mDurationSeconds;
+ return time * mVelocity / mDiff;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
index 864c597..451c5c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
@@ -34,21 +34,6 @@
}
@Override
- public void setActualHeight(int currentHeight, boolean notifyListeners) {
- // noop
- }
-
- @Override
- public int getActualHeight() {
- return getHeight();
- }
-
- @Override
- public void setClipTopAmount(int clipTopAmount) {
- // noop
- }
-
- @Override
protected void onFinishInflate() {
super.onFinishInflate();
mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view);
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 8387e36..150db63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -32,6 +32,7 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -53,15 +54,21 @@
private int mNotificationTopPadding;
private boolean mAnimateNextTopPaddingChange;
- private Interpolator mExpansionInterpolator;
-
private int mTrackingPointer;
private VelocityTracker mVelocityTracker;
private boolean mTracking;
+
+ /**
+ * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't
+ * intercepted yet.
+ */
+ private boolean mIntercepting;
private boolean mQsExpanded;
private float mInitialHeightOnTouch;
private float mInitialTouchX;
private float mInitialTouchY;
+ private float mLastTouchX;
+ private float mLastTouchY;
private float mQsExpansionHeight;
private int mQsMinExpansionHeight;
private int mQsMaxExpansionHeight;
@@ -70,6 +77,7 @@
private int mStackScrollerIntrinsicPadding;
private boolean mQsExpansionEnabled = true;
private ValueAnimator mQsExpansionAnimator;
+ private FlingAnimationUtils mFlingAnimationUtils;
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -107,8 +115,7 @@
mNotificationTopPadding = getResources().getDimensionPixelSize(
R.dimen.notifications_top_padding);
mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height);
- mExpansionInterpolator = AnimationUtils.loadInterpolator(
- getContext(), android.R.interpolator.fast_out_slow_in);
+ mFlingAnimationUtils = new FlingAnimationUtils(getContext());
}
@Override
@@ -195,6 +202,7 @@
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
+ mIntercepting = true;
mInitialTouchY = y;
mInitialTouchX = x;
initVelocityTracker();
@@ -217,6 +225,16 @@
case MotionEvent.ACTION_MOVE:
final float h = y - mInitialTouchY;
trackMovement(event);
+ if (mTracking) {
+
+ // Already tracking because onOverscrolled was called. We need to update here
+ // so we don't stop for a frame until the next touch event gets handled in
+ // onTouchEvent.
+ setQsExpansion(h + mInitialHeightOnTouch);
+ trackMovement(event);
+ mIntercepting = false;
+ return true;
+ }
if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
&& shouldIntercept(mInitialTouchX, mInitialTouchY, h)) {
onQsExpansionStarted();
@@ -224,14 +242,41 @@
mInitialTouchY = y;
mInitialTouchX = x;
mTracking = true;
+ mIntercepting = false;
return true;
}
break;
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ trackMovement(event);
+ if (mTracking) {
+ flingWithCurrentVelocity();
+ mTracking = false;
+ }
+ mIntercepting = false;
+ break;
}
return !mQsExpanded && super.onInterceptTouchEvent(event);
}
@Override
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+
+ // Block request so we can still intercept the scrolling when QS is expanded.
+ if (!mQsExpanded) {
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ }
+ }
+
+ private void flingWithCurrentVelocity() {
+ float vel = getCurrentVelocity();
+
+ // TODO: Better logic whether we should expand or not.
+ flingSettings(vel, vel > 0);
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
// TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
// implementation.
@@ -280,12 +325,7 @@
mTracking = false;
mTrackingPointer = -1;
trackMovement(event);
-
- float vel = getCurrentVelocity();
-
- // TODO: Better logic whether we should expand or not.
- flingSettings(vel, vel > 0);
-
+ flingWithCurrentVelocity();
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
@@ -299,11 +339,26 @@
return mQsExpanded || super.onTouchEvent(event);
}
+ @Override
+ public void onOverscrolled(int amount) {
+ if (mIntercepting) {
+ onQsExpansionStarted(amount);
+ mInitialHeightOnTouch = mQsExpansionHeight;
+ mInitialTouchY = mLastTouchY;
+ mInitialTouchX = mLastTouchX;
+ mTracking = true;
+ }
+ }
+
private void onQsExpansionStarted() {
+ onQsExpansionStarted(0);
+ }
+
+ private void onQsExpansionStarted(int overscrollAmount) {
cancelAnimation();
// Reset scroll position and apply that position to the expanded height.
- float height = mQsExpansionHeight - mScrollView.getScrollY();
+ float height = mQsExpansionHeight - mScrollView.getScrollY() - overscrollAmount;
mScrollView.scrollTo(0, 0);
setQsExpansion(height);
}
@@ -361,6 +416,8 @@
private void trackMovement(MotionEvent event) {
if (mVelocityTracker != null) mVelocityTracker.addMovement(event);
+ mLastTouchX = event.getX();
+ mLastTouchY = event.getY();
}
private void initVelocityTracker() {
@@ -384,13 +441,9 @@
}
}
private void flingSettings(float vel, boolean expand) {
-
- // TODO: Actually use velocity.
-
float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight;
ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
- animator.setDuration(EXPANSION_ANIMATION_LENGTH);
- animator.setInterpolator(mExpansionInterpolator);
+ mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
index 46484f3..ba0b66e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
@@ -27,6 +27,7 @@
public class ObservableScrollView extends ScrollView {
private Listener mListener;
+ private int mLastOverscrollAmount;
public ObservableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -58,7 +59,25 @@
}
}
+ @Override
+ protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY,
+ int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY,
+ boolean isTouchEvent) {
+ mLastOverscrollAmount = Math.max(0, scrollY + deltaY - getMaxScrollY());
+ return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY,
+ maxOverScrollX, maxOverScrollY, isTouchEvent);
+ }
+
+ @Override
+ protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
+ super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
+ if (mListener != null && mLastOverscrollAmount > 0) {
+ mListener.onOverscrolled(mLastOverscrollAmount);
+ }
+ }
+
public interface Listener {
void onScrollChanged();
+ void onOverscrolled(int amount);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index f3ebf1b..8ce7279 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -323,14 +323,6 @@
mModel.addInversionTile(inversionTile, inversionTile.getRefreshCallback());
parent.addView(inversionTile);
- // Contrast enhancement tile
- final SystemSettingTile contrastTile = new SystemSettingTile(mContext);
- contrastTile.setUri(Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED,
- SystemSettingTile.TYPE_SECURE);
- contrastTile.setFragment("Settings$AccessibilityContrastSettingsActivity");
- mModel.addContrastTile(contrastTile, contrastTile.getRefreshCallback());
- parent.addView(contrastTile);
-
// Color space adjustment tile
final SystemSettingTile colorSpaceTile = new SystemSettingTile(mContext);
colorSpaceTile.setUri(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
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 005b0d1..9b90d3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -95,12 +95,6 @@
}
static class InversionState extends State {
boolean toggled;
- int type;
- }
- static class ContrastState extends State {
- boolean toggled;
- float contrast;
- float brightness;
}
static class ColorSpaceState extends State {
boolean toggled;
@@ -237,38 +231,6 @@
cr.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_QUICK_SETTING_ENABLED),
false, this, mUserTracker.getCurrentUserId());
- cr.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION),
- false, this, mUserTracker.getCurrentUserId());
- }
- }
-
- /** ContentObserver to watch display contrast */
- private class DisplayContrastObserver extends ContentObserver {
- public DisplayContrastObserver(Handler handler) {
- super(handler);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- onContrastChanged();
- }
-
- public void startObserving() {
- final ContentResolver cr = mContext.getContentResolver();
- cr.unregisterContentObserver(this);
- cr.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED),
- false, this, mUserTracker.getCurrentUserId());
- cr.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_QUICK_SETTING_ENABLED),
- false, this, mUserTracker.getCurrentUserId());
- cr.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST),
- false, this, mUserTracker.getCurrentUserId());
- cr.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS),
- false, this, mUserTracker.getCurrentUserId());
}
}
@@ -348,7 +310,6 @@
private final BugreportObserver mBugreportObserver;
private final BrightnessObserver mBrightnessObserver;
private final DisplayInversionObserver mInversionObserver;
- private final DisplayContrastObserver mContrastObserver;
private final DisplayColorSpaceObserver mColorSpaceObserver;
private final ZenModeObserver mZenModeObserver;
@@ -417,10 +378,6 @@
private RefreshCallback mInversionCallback;
private InversionState mInversionState = new InversionState();
- private QuickSettingsTileView mContrastTile;
- private RefreshCallback mContrastCallback;
- private ContrastState mContrastState = new ContrastState();
-
private QuickSettingsTileView mColorSpaceTile;
private RefreshCallback mColorSpaceCallback;
private ColorSpaceState mColorSpaceState = new ColorSpaceState();
@@ -448,12 +405,10 @@
public void onUserSwitched(int newUserId) {
mBrightnessObserver.startObserving();
mInversionObserver.startObserving();
- mContrastObserver.startObserving();
mColorSpaceObserver.startObserving();
refreshRotationLockTile();
onBrightnessLevelChanged();
onInversionChanged();
- onContrastChanged();
onColorSpaceChanged();
onNextAlarmChanged();
onBugreportChanged();
@@ -470,8 +425,6 @@
mBrightnessObserver.startObserving();
mInversionObserver = new DisplayInversionObserver(mHandler);
mInversionObserver.startObserving();
- mContrastObserver = new DisplayContrastObserver(mHandler);
- mContrastObserver.startObserving();
mColorSpaceObserver = new DisplayColorSpaceObserver(mHandler);
mColorSpaceObserver.startObserving();
mZenModeObserver = new ZenModeObserver(mHandler);
@@ -1037,11 +990,8 @@
currentUserId) == 1;
final boolean enabled = Settings.Secure.getIntForUser(
cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, currentUserId) == 1;
- final int type = Settings.Secure.getIntForUser(
- cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION, 0, currentUserId);
mInversionState.enabled = quickSettingEnabled;
mInversionState.toggled = enabled;
- mInversionState.type = type;
// TODO: Add real icon assets.
mInversionState.iconId = enabled ? R.drawable.ic_qs_inversion_on
: R.drawable.ic_qs_inversion_off;
@@ -1049,36 +999,6 @@
mInversionCallback.refreshView(mInversionTile, mInversionState);
}
- // Contrast enhancement
- void addContrastTile(QuickSettingsTileView view, RefreshCallback cb) {
- mContrastTile = view;
- mContrastCallback = cb;
- onContrastChanged();
- }
- public void onContrastChanged() {
- final Resources res = mContext.getResources();
- final ContentResolver cr = mContext.getContentResolver();
- final int currentUserId = mUserTracker.getCurrentUserId();
- final boolean quickSettingEnabled = Settings.Secure.getIntForUser(
- cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_QUICK_SETTING_ENABLED, 0,
- currentUserId) == 1;
- final boolean enabled = Settings.Secure.getIntForUser(
- cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED, 0, currentUserId) == 1;
- final float contrast = Settings.Secure.getFloatForUser(
- cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST, 1, currentUserId);
- final float brightness = Settings.Secure.getFloatForUser(
- cr, Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS, 0, currentUserId);
- mContrastState.enabled = quickSettingEnabled;
- mContrastState.toggled = enabled;
- mContrastState.contrast = contrast;
- mContrastState.brightness = brightness;
- // TODO: Add real icon assets.
- mContrastState.iconId = enabled ? R.drawable.ic_qs_contrast_on
- : R.drawable.ic_qs_contrast_off;
- mContrastState.label = res.getString(R.string.quick_settings_contrast_label);
- mContrastCallback.refreshView(mContrastTile, mContrastState);
- }
-
// Color space adjustment
void addColorSpaceTile(QuickSettingsTileView view, RefreshCallback cb) {
mColorSpaceTile = view;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 1af9a6b..5527473 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -18,6 +18,8 @@
import android.content.Context;
import android.content.Intent;
+import android.graphics.Outline;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -59,6 +61,9 @@
private ActivityStarter mActivityStarter;
private BrightnessController mBrightnessController;
+ private final Rect mClipBounds = new Rect();
+ private final Outline mOutline = new Outline();
+
public StatusBarHeaderView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -193,6 +198,14 @@
} else {
mBackground.setTranslationY(0);
}
+ setClipping(height);
+ }
+
+ private void setClipping(float height) {
+ mClipBounds.set(getPaddingLeft(), 0, getWidth() - getPaddingRight(), (int) height);
+ setClipBounds(mClipBounds);
+ mOutline.setRect(mClipBounds);
+ setOutline(mOutline);
}
public View getBackgroundView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 4121a40..deab757 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -28,6 +28,8 @@
private int mScrollY;
private boolean mDimmed;
private View mActivatedChild;
+ private float mOverScrollTopAmount;
+ private float mOverScrollBottomAmount;
public int getScrollY() {
return mScrollY;
@@ -72,4 +74,16 @@
public View getActivatedChild() {
return mActivatedChild;
}
+
+ public void setOverScrollAmount(float amount, boolean onTop) {
+ if (onTop) {
+ mOverScrollTopAmount = amount;
+ } else {
+ mOverScrollBottomAmount = amount;
+ }
+ }
+
+ public float getOverScrollAmount(boolean top) {
+ return top ? mOverScrollTopAmount : mOverScrollBottomAmount;
+ }
}
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 27f66199..fbb6695 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -53,6 +53,7 @@
private static final String TAG = "NotificationStackScrollLayout";
private static final boolean DEBUG = false;
+ private static final float RUBBER_BAND_FACTOR = 0.35f;
/**
* Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
@@ -70,8 +71,8 @@
private int mTouchSlop;
private int mMinimumVelocity;
private int mMaximumVelocity;
- private int mOverscrollDistance;
private int mOverflingDistance;
+ private float mMaxOverScroll;
private boolean mIsBeingDragged;
private int mLastMotionY;
private int mActivePointerId;
@@ -80,9 +81,12 @@
private Paint mDebugPaint;
private int mContentHeight;
private int mCollapsedSize;
+ private int mBottomStackSlowDownHeight;
private int mBottomStackPeekSize;
private int mEmptyMarginBottom;
private int mPaddingBetweenElements;
+ private int mPaddingBetweenElementsDimmed;
+ private int mPaddingBetweenElementsNormal;
private int mTopPadding;
/**
@@ -104,6 +108,16 @@
private ArrayList<View> mSwipedOutViews = new ArrayList<View>();
private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
+ /**
+ * The raw amount of the overScroll on the top, which is not rubber-banded.
+ */
+ private float mOverScrolledTopPixels;
+
+ /**
+ * The raw amount of the overScroll on the bottom, which is not rubber-banded.
+ */
+ private float mOverScrolledBottomPixels;
+
private OnChildLocationsChangedListener mListener;
private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
private boolean mNeedsAnimation;
@@ -154,7 +168,7 @@
int y = mCollapsedSize;
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
y = (int) (getLayoutHeight() - mBottomStackPeekSize
- - mStackScrollAlgorithm.getBottomStackSlowDownLength());
+ - mBottomStackSlowDownHeight);
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
y = (int) (getLayoutHeight() - mBottomStackPeekSize);
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
@@ -172,7 +186,6 @@
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
- mOverscrollDistance = configuration.getScaledOverscrollDistance();
mOverflingDistance = configuration.getScaledOverflingDistance();
float densityScale = getResources().getDisplayMetrics().density;
float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
@@ -186,9 +199,20 @@
.getDimensionPixelSize(R.dimen.bottom_stack_peek_amount);
mEmptyMarginBottom = context.getResources().getDimensionPixelSize(
R.dimen.notification_stack_margin_bottom);
- mPaddingBetweenElements = context.getResources()
- .getDimensionPixelSize(R.dimen.notification_padding);
mStackScrollAlgorithm = new StackScrollAlgorithm(context);
+ mPaddingBetweenElementsDimmed = context.getResources()
+ .getDimensionPixelSize(R.dimen.notification_padding_dimmed);
+ mPaddingBetweenElementsNormal = context.getResources()
+ .getDimensionPixelSize(R.dimen.notification_padding);
+ updatePadding(false);
+ }
+
+ private void updatePadding(boolean dimmed) {
+ mPaddingBetweenElements = dimmed
+ ? mPaddingBetweenElementsDimmed
+ : mPaddingBetweenElementsNormal;
+ mBottomStackSlowDownHeight = mStackScrollAlgorithm.getBottomStackSlowDownLength();
+ updateContentHeight();
}
@Override
@@ -530,7 +554,7 @@
* will be false if being flinged.
*/
if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
+ mScroller.forceFinished(true);
}
// Remember where the motion event started
@@ -558,40 +582,23 @@
if (mIsBeingDragged) {
// Scroll to follow the motion event
mLastMotionY = y;
-
- final int oldX = mScrollX;
- final int oldY = mOwnScrollY;
final int range = getScrollRange();
- final int overscrollMode = getOverScrollMode();
- final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
- (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
+
+ float scrollAmount;
+ if (deltaY < 0) {
+ scrollAmount = overScrollDown(deltaY);
+ } else {
+ scrollAmount = overScrollUp(deltaY, range);
+ }
// Calling overScrollBy will call onOverScrolled, which
// calls onScrollChanged if applicable.
- if (overScrollBy(0, deltaY, 0, mOwnScrollY,
- 0, range, 0, mOverscrollDistance, true)) {
- // Break our velocity if we hit a scroll barrier.
- mVelocityTracker.clear();
+ if (scrollAmount != 0.0f) {
+ // The scrolling motion could not be compensated with the
+ // existing overScroll, we have to scroll the view
+ overScrollBy(0, (int) scrollAmount, 0, mOwnScrollY,
+ 0, range, 0, getHeight() / 2, true);
}
- // TODO: Overscroll
-// if (canOverscroll) {
-// final int pulledToY = oldY + deltaY;
-// if (pulledToY < 0) {
-// mEdgeGlowTop.onPull((float) deltaY / getHeight());
-// if (!mEdgeGlowBottom.isFinished()) {
-// mEdgeGlowBottom.onRelease();
-// }
-// } else if (pulledToY > range) {
-// mEdgeGlowBottom.onPull((float) deltaY / getHeight());
-// if (!mEdgeGlowTop.isFinished()) {
-// mEdgeGlowTop.onRelease();
-// }
-// }
-// if (mEdgeGlowTop != null
-// && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())){
-// postInvalidateOnAnimation();
-// }
-// }
}
break;
case MotionEvent.ACTION_UP:
@@ -638,6 +645,68 @@
return true;
}
+ /**
+ * Perform a scroll upwards and adapt the overscroll amounts accordingly
+ *
+ * @param deltaY The amount to scroll upwards, has to be positive.
+ * @return The amount of scrolling to be performed by the scroller,
+ * not handled by the overScroll amount.
+ */
+ private float overScrollUp(int deltaY, int range) {
+ deltaY = Math.max(deltaY, 0);
+ float currentTopAmount = getCurrentOverScrollAmount(true);
+ float newTopAmount = currentTopAmount - deltaY;
+ if (currentTopAmount > 0) {
+ setOverScrollAmount(newTopAmount, true /* onTop */,
+ false /* animate */);
+ }
+ // Top overScroll might not grab all scrolling motion,
+ // we have to scroll as well.
+ float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
+ float newScrollY = mOwnScrollY + scrollAmount;
+ if (newScrollY > range) {
+ float currentBottomPixels = getCurrentOverScrolledPixels(false);
+ // We overScroll on the top
+ setOverScrolledPixels(currentBottomPixels + newScrollY - range,
+ false /* onTop */,
+ false /* animate */);
+ mOwnScrollY = range;
+ scrollAmount = 0.0f;
+ }
+ return scrollAmount;
+ }
+
+ /**
+ * Perform a scroll downward and adapt the overscroll amounts accordingly
+ *
+ * @param deltaY The amount to scroll downwards, has to be negative.
+ * @return The amount of scrolling to be performed by the scroller,
+ * not handled by the overScroll amount.
+ */
+ private float overScrollDown(int deltaY) {
+ deltaY = Math.min(deltaY, 0);
+ float currentBottomAmount = getCurrentOverScrollAmount(false);
+ float newBottomAmount = currentBottomAmount + deltaY;
+ if (currentBottomAmount > 0) {
+ setOverScrollAmount(newBottomAmount, false /* onTop */,
+ false /* animate */);
+ }
+ // Bottom overScroll might not grab all scrolling motion,
+ // we have to scroll as well.
+ float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
+ float newScrollY = mOwnScrollY + scrollAmount;
+ if (newScrollY < 0) {
+ float currentTopPixels = getCurrentOverScrolledPixels(true);
+ // We overScroll on the top
+ setOverScrolledPixels(currentTopPixels - newScrollY,
+ true /* onTop */,
+ false /* animate */);
+ mOwnScrollY = 0;
+ scrollAmount = 0.0f;
+ }
+ return scrollAmount;
+ }
+
private void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
MotionEvent.ACTION_POINTER_INDEX_SHIFT;
@@ -687,23 +756,16 @@
if (oldX != x || oldY != y) {
final int range = getScrollRange();
- final int overscrollMode = getOverScrollMode();
- final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
- (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
+ if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
+ float currVelocity = mScroller.getCurrVelocity();
+ if (currVelocity >= mMinimumVelocity) {
+ mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
+ }
+ }
overScrollBy(x - oldX, y - oldY, oldX, oldY, 0, range,
- 0, mOverflingDistance, false);
+ 0, (int) (mMaxOverScroll), false);
onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
-
- if (canOverscroll) {
- // TODO: Overscroll
-// if (y < 0 && oldY >= 0) {
-// mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
-// } else if (y > range && oldY <= range) {
-// mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
-// }
- }
- updateChildren();
}
// Keep on drawing until the animation has finished.
@@ -711,6 +773,81 @@
}
}
+ @Override
+ protected int computeVerticalScrollRange() {
+ // needed for the overScroller
+ return mContentHeight;
+ }
+
+ /**
+ * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
+ * overscroll effect based on numPixels. By default this will also cancel animations on the
+ * same overScroll edge.
+ *
+ * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
+ * the rubber-banding logic.
+ * @param onTop Should the effect be applied on top of the scroller.
+ * @param animate Should an animation be performed.
+ */
+ public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
+ setOverScrollAmount(numPixels * RUBBER_BAND_FACTOR, onTop, animate, true);
+ }
+
+ /**
+ * Set the effective overScroll amount which will be directly reflected in the layout.
+ * By default this will also cancel animations on the same overScroll edge.
+ *
+ * @param amount The amount to overScroll by.
+ * @param onTop Should the effect be applied on top of the scroller.
+ * @param animate Should an animation be performed.
+ */
+ public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
+ setOverScrollAmount(amount, onTop, animate, true);
+ }
+
+ /**
+ * Set the effective overScroll amount which will be directly reflected in the layout.
+ *
+ * @param amount The amount to overScroll by.
+ * @param onTop Should the effect be applied on top of the scroller.
+ * @param animate Should an animation be performed.
+ * @param cancelAnimators Should running animations be cancelled.
+ */
+ public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
+ boolean cancelAnimators) {
+ if (cancelAnimators) {
+ mStateAnimator.cancelOverScrollAnimators(onTop);
+ }
+ setOverScrollAmountInternal(amount, onTop, animate);
+ }
+
+ private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate) {
+ amount = Math.max(0, amount);
+ if (animate) {
+ mStateAnimator.animateOverScrollToAmount(amount, onTop);
+ } else {
+ setOverScrolledPixels(amount / RUBBER_BAND_FACTOR, onTop);
+ mAmbientState.setOverScrollAmount(amount, onTop);
+ requestChildrenUpdate();
+ }
+ }
+
+ public float getCurrentOverScrollAmount(boolean top) {
+ return mAmbientState.getOverScrollAmount(top);
+ }
+
+ public float getCurrentOverScrolledPixels(boolean top) {
+ return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
+ }
+
+ private void setOverScrolledPixels(float amount, boolean onTop) {
+ if (onTop) {
+ mOverScrolledTopPixels = amount;
+ } else {
+ mOverScrolledBottomPixels = amount;
+ }
+ }
+
private void customScrollTo(int y) {
mOwnScrollY = y;
updateChildren();
@@ -724,33 +861,51 @@
final int oldY = mOwnScrollY;
mScrollX = scrollX;
mOwnScrollY = scrollY;
- invalidateParentIfNeeded();
- onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
if (clampedY) {
- mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange());
+ springBack();
+ } else {
+ onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
+ invalidateParentIfNeeded();
+ updateChildren();
}
- updateChildren();
} else {
customScrollTo(scrollY);
scrollTo(scrollX, mScrollY);
}
}
+ private void springBack() {
+ int scrollRange = getScrollRange();
+ boolean overScrolledTop = mOwnScrollY <= 0;
+ boolean overScrolledBottom = mOwnScrollY >= scrollRange;
+ if (overScrolledTop || overScrolledBottom) {
+ boolean onTop;
+ float newAmount;
+ if (overScrolledTop) {
+ onTop = true;
+ newAmount = -mOwnScrollY;
+ mOwnScrollY = 0;
+ } else {
+ onTop = false;
+ newAmount = mOwnScrollY - scrollRange;
+ mOwnScrollY = scrollRange;
+ }
+ setOverScrollAmount(newAmount, onTop, false);
+ setOverScrollAmount(0.0f, onTop, true);
+ mScroller.forceFinished(true);
+ }
+ }
+
private int getScrollRange() {
int scrollRange = 0;
ExpandableView firstChild = (ExpandableView) getFirstChildNotGone();
if (firstChild != null) {
int contentHeight = getContentHeight();
int firstChildMaxExpandHeight = getMaxExpandHeight(firstChild);
-
- scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize);
+ scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize
+ + mBottomStackSlowDownHeight);
if (scrollRange > 0) {
View lastChild = getLastChildNotGone();
- if (isViewExpanded(lastChild)) {
- // last child is expanded, so we have to ensure that it can exit the
- // bottom stack
- scrollRange += mCollapsedSize + mPaddingBetweenElements;
- }
// 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);
@@ -832,7 +987,23 @@
int height = (int) getLayoutHeight();
int bottom = getContentHeight();
- mScroller.fling(mScrollX, mOwnScrollY, 0, velocityY, 0, 0, 0,
+ float topAmount = getCurrentOverScrollAmount(true);
+ float bottomAmount = getCurrentOverScrollAmount(false);
+ if (velocityY < 0 && topAmount > 0) {
+ mOwnScrollY -= (int) topAmount;
+ setOverScrollAmount(0, true, false);
+ mMaxOverScroll = Math.abs(velocityY) / 1000f * RUBBER_BAND_FACTOR
+ * mOverflingDistance + topAmount;
+ } else if (velocityY > 0 && bottomAmount > 0) {
+ mOwnScrollY += bottomAmount;
+ setOverScrollAmount(0, false, false);
+ mMaxOverScroll = Math.abs(velocityY) / 1000f * RUBBER_BAND_FACTOR
+ * mOverflingDistance + bottomAmount;
+ } else {
+ // it will be set once we reach the boundary
+ mMaxOverScroll = 0.0f;
+ }
+ mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0,
Math.max(0, bottom - height), 0, height/2);
postInvalidateOnAnimation();
@@ -844,11 +1015,12 @@
recycleVelocityTracker();
- // TODO: Overscroll
-// if (mEdgeGlowTop != null) {
-// mEdgeGlowTop.onRelease();
-// mEdgeGlowBottom.onRelease();
-// }
+ if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
+ setOverScrollAmount(0, true /* onTop */, true /* animate */);
+ }
+ if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
+ setOverScrollAmount(0, false /* onTop */, true /* animate */);
+ }
}
@Override
@@ -1184,7 +1356,7 @@
public int getEmptyBottomMargin() {
int emptyMargin = mMaxLayoutHeight - mContentHeight;
if (needsHeightAdaption()) {
- emptyMargin = emptyMargin - mCollapsedSize - mBottomStackPeekSize;
+ emptyMargin = emptyMargin - mBottomStackSlowDownHeight - mBottomStackPeekSize;
}
return Math.max(emptyMargin, 0);
}
@@ -1231,6 +1403,7 @@
public void setDimmed(boolean dimmed, boolean animate) {
mStackScrollAlgorithm.setDimmed(dimmed);
mAmbientState.setDimmed(dimmed);
+ updatePadding(dimmed);
if (animate) {
mDimmedNeedsAnimation = true;
mNeedsAnimation = true;
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 bd9de82..d572ea5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -128,7 +128,10 @@
algorithmState.scrolledPixelsTop = 0;
algorithmState.itemsInBottomStack = 0.0f;
algorithmState.partialInBottom = 0.0f;
- algorithmState.scrollY = ambientState.getScrollY() + mCollapsedSize;
+ float topOverScroll = ambientState.getOverScrollAmount(true /* onTop */);
+ float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
+ algorithmState.scrollY = (int) (ambientState.getScrollY() + mCollapsedSize
+ + bottomOverScroll - topOverScroll);
updateVisibleChildren(resultState, algorithmState);
@@ -215,7 +218,6 @@
*
* @param resultState The result state to update if a change to the properties of a child occurs
* @param algorithmState The state in which the current pass of the algorithm is currently in
- * and which will be updated
*/
private void updatePositionsForState(StackScrollState resultState,
StackScrollAlgorithmState algorithmState) {
@@ -295,7 +297,7 @@
// The first card is always rendered.
if (i == 0) {
childViewState.alpha = 1.0f;
- childViewState.yTranslation = 0;
+ childViewState.yTranslation = Math.max(mCollapsedSize - algorithmState.scrollY, 0);
childViewState.location = StackScrollState.ViewState.LOCATION_FIRST_CARD;
}
if (childViewState.location == StackScrollState.ViewState.LOCATION_UNKNOWN) {
@@ -454,7 +456,6 @@
*
* @param resultState The result state to update if a height change of an child occurs
* @param algorithmState The state in which the current pass of the algorithm is currently in
- * and which will be updated
*/
private void findNumberOfItemsInTopStackAndUpdateState(StackScrollState resultState,
StackScrollAlgorithmState algorithmState) {
@@ -472,7 +473,7 @@
+ childHeight
+ mPaddingBetweenElements;
if (yPositionInScrollView < algorithmState.scrollY) {
- if (i == 0 && algorithmState.scrollY == mCollapsedSize) {
+ if (i == 0 && algorithmState.scrollY <= mCollapsedSize) {
// The starting position of the bottom stack peek
int bottomPeekStart = mInnerHeight - mBottomStackPeekSize;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index ca383aa..5ac51f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -18,7 +18,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
@@ -73,6 +72,9 @@
private AnimationFilter mAnimationFilter = new AnimationFilter();
private long mCurrentLength;
+ private ValueAnimator mTopOverScrollAnimator;
+ private ValueAnimator mBottomOverScrollAnimator;
+
public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
mHostLayout = hostLayout;
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(),
@@ -510,7 +512,7 @@
ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
StackScrollState finalState) {
mNewEvents.clear();
- for (NotificationStackScrollLayout.AnimationEvent event: animationEvents) {
+ for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
View changingView = event.changingView;
if (!mHandledEvents.contains(event)) {
if (event.animationType == NotificationStackScrollLayout.AnimationEvent
@@ -532,4 +534,34 @@
}
}
}
+
+ public void animateOverScrollToAmount(float targetAmount, final boolean onTop) {
+ final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
+ cancelOverScrollAnimators(onTop);
+ ValueAnimator overScrollAnimator = ValueAnimator.ofFloat(startOverScrollAmount,
+ targetAmount);
+ overScrollAnimator.setDuration(ANIMATION_DURATION_STANDARD);
+ overScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float currentOverScroll = (float) animation.getAnimatedValue();
+ mHostLayout.setOverScrollAmount(currentOverScroll, onTop, false /* animate */,
+ false /* cancelAnimators */);
+ }
+ });
+ overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator);
+ overScrollAnimator.start();
+ if (onTop) {
+ mTopOverScrollAnimator = overScrollAnimator;
+ } else {
+ mBottomOverScrollAnimator = overScrollAnimator;
+ }
+ }
+
+ public void cancelOverScrollAnimators(boolean onTop) {
+ ValueAnimator currentAnimator = onTop ? mTopOverScrollAnimator : mBottomOverScrollAnimator;
+ if (currentAnimator != null) {
+ currentAnimator.cancel();
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 4a59a8f..36b5cfb 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -3698,17 +3698,8 @@
private final Uri mEnhancedWebAccessibilityUri = Settings.Secure
.getUriFor(Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION);
- private final Uri mDisplayContrastEnabledUri = Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED);
- private final Uri mDisplayContrastUri = Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST);
- private final Uri mDisplayBrightnessUri = Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS);
-
private final Uri mDisplayInversionEnabledUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
- private final Uri mDisplayInversionUri = Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION);
private final Uri mDisplayDaltonizerEnabledUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
@@ -3734,16 +3725,8 @@
contentResolver.registerContentObserver(mEnhancedWebAccessibilityUri,
false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
- mDisplayContrastEnabledUri, false, this, UserHandle.USER_ALL);
- contentResolver.registerContentObserver(
- mDisplayContrastUri, false, this, UserHandle.USER_ALL);
- contentResolver.registerContentObserver(
- mDisplayBrightnessUri, false, this, UserHandle.USER_ALL);
- contentResolver.registerContentObserver(
mDisplayInversionEnabledUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
- mDisplayInversionUri, false, this, UserHandle.USER_ALL);
- contentResolver.registerContentObserver(
mDisplayDaltonizerEnabledUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
mDisplayDaltonizerUri, false, this, UserHandle.USER_ALL);
@@ -3823,12 +3806,8 @@
}
}
}
- } else if (mDisplayContrastEnabledUri.equals(uri)
- || mDisplayInversionEnabledUri.equals(uri)
+ } else if (mDisplayInversionEnabledUri.equals(uri)
|| mDisplayDaltonizerEnabledUri.equals(uri)
- || mDisplayContrastUri.equals(uri)
- || mDisplayBrightnessUri.equals(uri)
- || mDisplayInversionUri.equals(uri)
|| mDisplayDaltonizerUri.equals(uri)) {
synchronized (mLock) {
// Profiles share the accessibility state of the parent. Therefore,
diff --git a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
index 52bdeda..394c196 100644
--- a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
+++ b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
@@ -41,22 +41,6 @@
0, 0, 0, 1
};
- /** Matrix and offset used for standard display inversion. */
- private static final float[] INVERSION_MATRIX_STANDARD = new float[] {
- -1, 0, 0, 0,
- 0, -1, 0, 0,
- 0, 0, -1, 0,
- 1, 1, 1, 1
- };
-
- /** Matrix and offset used for hue-only display inversion. */
- private static final float[] INVERSION_MATRIX_HUE_ONLY = new float[] {
- 0, .5f, .5f, 0,
- .5f, 0, .5f, 0,
- .5f, .5f, 0, 0,
- 0, 0, 0, 1
- };
-
/** Matrix and offset used for value-only display inversion. */
private static final float[] INVERSION_MATRIX_VALUE_ONLY = new float[] {
0, -.5f, -.5f, 0,
@@ -65,15 +49,6 @@
1, 1, 1, 1
};
- /** Default contrast for display contrast enhancement. */
- private static final float DEFAULT_DISPLAY_CONTRAST = 2;
-
- /** Default brightness for display contrast enhancement. */
- private static final float DEFAULT_DISPLAY_BRIGHTNESS = 0;
-
- /** Default inversion mode for display color inversion. */
- private static final int DEFAULT_DISPLAY_INVERSION = AccessibilityManager.INVERSION_STANDARD;
-
/** Default inversion mode for display color correction. */
private static final int DEFAULT_DISPLAY_DALTONIZER =
AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY;
@@ -90,11 +65,6 @@
if (!hasColorTransform) {
hasColorTransform |= Settings.Secure.getIntForUser(
- cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED, 0, userId) == 1;
- }
-
- if (!hasColorTransform) {
- hasColorTransform |= Settings.Secure.getIntForUser(
cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) == 1;
}
@@ -115,21 +85,7 @@
final boolean inversionEnabled = Settings.Secure.getIntForUser(
cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) == 1;
if (inversionEnabled) {
- final int inversionMode = Settings.Secure.getIntForUser(cr,
- Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION, DEFAULT_DISPLAY_INVERSION,
- userId);
- final float[] inversionMatrix;
- switch (inversionMode) {
- case AccessibilityManager.INVERSION_HUE_ONLY:
- inversionMatrix = INVERSION_MATRIX_HUE_ONLY;
- break;
- case AccessibilityManager.INVERSION_VALUE_ONLY:
- inversionMatrix = INVERSION_MATRIX_VALUE_ONLY;
- break;
- default:
- inversionMatrix = INVERSION_MATRIX_STANDARD;
- }
-
+ final float[] inversionMatrix = INVERSION_MATRIX_VALUE_ONLY;
Matrix.multiplyMM(outputMatrix, 0, colorMatrix, 0, inversionMatrix, 0);
colorMatrix = outputMatrix;
@@ -138,31 +94,6 @@
hasColorTransform = true;
}
- final boolean contrastEnabled = Settings.Secure.getIntForUser(
- cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED, 0, userId) == 1;
- if (contrastEnabled) {
- final float contrast = Settings.Secure.getFloatForUser(cr,
- Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST, DEFAULT_DISPLAY_CONTRAST,
- userId);
- final float brightness = Settings.Secure.getFloatForUser(cr,
- Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS, DEFAULT_DISPLAY_BRIGHTNESS,
- userId);
- final float off = brightness * contrast - 0.5f * contrast + 0.5f;
- final float[] contrastMatrix = {
- contrast, 0, 0, 0,
- 0, contrast, 0, 0,
- 0, 0, contrast, 0,
- off, off, off, 1
- };
-
- Matrix.multiplyMM(outputMatrix, 0, colorMatrix, 0, contrastMatrix, 0);
-
- colorMatrix = outputMatrix;
- outputMatrix = colorMatrix;
-
- hasColorTransform = true;
- }
-
final boolean daltonizerEnabled = Settings.Secure.getIntForUser(
cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) != 0;
if (daltonizerEnabled) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index dfffa8a..ce2aefb 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -65,6 +65,7 @@
import android.net.LinkProperties.CompareResult;
import android.net.LinkQualityInfo;
import android.net.MobileDataStateTracker;
+import android.net.Network;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
@@ -165,6 +166,8 @@
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
+import static android.net.ConnectivityManager.INVALID_NET_ID;
+
/**
* @hide
*/
@@ -442,6 +445,10 @@
TelephonyManager mTelephonyManager;
+ private final static int MIN_NET_ID = 10; // some reserved marks
+ private final static int MAX_NET_ID = 65535;
+ private int mNextNetId = MIN_NET_ID;
+
public ConnectivityService(Context context, INetworkManagementService netd,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
// Currently, omitting a NetworkFactory will create one internally
@@ -706,6 +713,12 @@
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
}
+ private synchronized int nextNetId() {
+ int netId = mNextNetId;
+ if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
+ return netId;
+ }
+
/**
* Factory that creates {@link NetworkStateTracker} instances using given
* {@link NetworkConfig}.
@@ -1104,7 +1117,7 @@
if (tracker != null) {
final NetworkInfo info = getFilteredNetworkInfo(tracker, uid);
result.add(new NetworkState(
- info, tracker.getLinkProperties(), tracker.getLinkCapabilities()));
+ info, tracker.getLinkProperties(), tracker.getNetworkCapabilities()));
}
}
}
@@ -1116,7 +1129,7 @@
final NetworkStateTracker tracker = mNetTrackers[networkType];
if (tracker != null) {
return new NetworkState(tracker.getNetworkInfo(), tracker.getLinkProperties(),
- tracker.getLinkCapabilities());
+ tracker.getNetworkCapabilities());
}
}
return null;
@@ -1984,6 +1997,7 @@
int prevNetType = info.getType();
mNetTrackers[prevNetType].setTeardownRequested(false);
+ int thisNetId = mNetTrackers[prevNetType].getNetwork().netId;
// Remove idletimer previously setup in {@code handleConnect}
if (mNetConfigs[prevNetType].isDefault()) {
@@ -2069,6 +2083,13 @@
sendConnectedBroadcastDelayed(mNetTrackers[mActiveDefaultNetwork].getNetworkInfo(),
getConnectivityChangeDelay());
}
+ try {
+// mNetd.removeNetwork(thisNetId);
+ } catch (Exception e) {
+ loge("Exception removing network: " + e);
+ } finally {
+ mNetTrackers[prevNetType].setNetId(INVALID_NET_ID);
+ }
}
private void tryFailover(int prevNetType) {
@@ -2336,17 +2357,23 @@
if (mNetConfigs[newNetType].isDefault()) {
if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != newNetType) {
if (isNewNetTypePreferredOverCurrentNetType(newNetType)) {
- // tear down the other
- NetworkStateTracker otherNet =
- mNetTrackers[mActiveDefaultNetwork];
- if (DBG) {
- log("Policy requires " + otherNet.getNetworkInfo().getTypeName() +
- " teardown");
- }
- if (!teardown(otherNet)) {
- loge("Network declined teardown request");
- teardown(thisNet);
- return;
+ String teardownPolicy = SystemProperties.get("net.teardownPolicy");
+ if (TextUtils.equals(teardownPolicy, "keep") == false) {
+ // tear down the other
+ NetworkStateTracker otherNet =
+ mNetTrackers[mActiveDefaultNetwork];
+ if (DBG) {
+ log("Policy requires " + otherNet.getNetworkInfo().getTypeName() +
+ " teardown");
+ }
+ if (!teardown(otherNet)) {
+ loge("Network declined teardown request");
+ teardown(thisNet);
+ return;
+ }
+ } else {
+ //TODO - remove
+ loge("network teardown skipped due to net.teardownPolicy setting");
}
} else {
// don't accept this one
@@ -2358,6 +2385,15 @@
return;
}
}
+ int thisNetId = nextNetId();
+ thisNet.setNetId(thisNetId);
+ try {
+// mNetd.createNetwork(thisNetId, thisIface);
+ } catch (Exception e) {
+ loge("Exception creating network :" + e);
+ teardown(thisNet);
+ return;
+ }
setupDataActivityTracking(newNetType);
synchronized (ConnectivityService.this) {
// have a new default network, release the transition wakelock in a second
@@ -2380,6 +2416,16 @@
// Don't do this - if we never sign in stay, grey
//reportNetworkCondition(mActiveDefaultNetwork, 100);
updateNetworkSettings(thisNet);
+ } else {
+ int thisNetId = nextNetId();
+ thisNet.setNetId(thisNetId);
+ try {
+// mNetd.createNetwork(thisNetId, thisIface);
+ } catch (Exception e) {
+ loge("Exception creating network :" + e);
+ teardown(thisNet);
+ return;
+ }
}
thisNet.setTeardownRequested(false);
updateMtuSizeSettings(thisNet);
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 10315a7..0b9570d 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -1464,6 +1464,7 @@
private boolean needsToShowImeSwitchOngoingNotification() {
if (!mShowOngoingImeSwitcherForPhones) return false;
+ if (mSwitchingDialog != null) return false;
if (isScreenLocked()) return false;
synchronized (mMethodMap) {
List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
@@ -2812,6 +2813,7 @@
mSwitchingDialog.getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
mSwitchingDialog.getWindow().getAttributes().setTitle("Select input method");
+ updateImeWindowStatusLocked();
mSwitchingDialog.show();
}
}
@@ -2869,6 +2871,7 @@
mSwitchingDialog = null;
}
+ updateImeWindowStatusLocked();
mDialogBuilder = null;
mIms = null;
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index d6ecb46..e54e5d0 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -427,7 +427,7 @@
}
// bind to fused provider if supported
- if (FlpHardwareProvider.isSupported()) {
+ if (FlpHardwareProvider.getInstance(mContext).isSupported()) {
FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext);
FusedProxy fusedProxy = FusedProxy.createAndBind(
mContext,
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index 0d1e122..96f9ab0 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -50,6 +50,8 @@
final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
private static final boolean LOGD = false;
+ private final static boolean VDBG = false;
+
private final String TAG;
private String mSocket;
@@ -409,7 +411,7 @@
loge("timed-out waiting for response to " + logCmd);
throw new NativeDaemonFailureException(logCmd, event);
}
- log("RMV <- {" + event + "}");
+ if (VDBG) log("RMV <- {" + event + "}");
events.add(event);
} while (event.isClassContinue());
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 7ce45f7..b9c86dc 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -2029,4 +2029,24 @@
pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
}
+
+ public void createNetwork(int netId, String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ try {
+ mConnector.execute("network", "create", netId, iface);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ public void removeNetwork(int netId) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ try {
+ mConnector.execute("network", "destroy", netId);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index d4565b6..cfaf016 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -22,8 +22,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.net.LinkCapabilities;
import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -120,7 +120,7 @@
private LinkProperties mDataConnectionLinkProperties;
- private LinkCapabilities mDataConnectionLinkCapabilities;
+ private NetworkCapabilities mDataConnectionNetworkCapabilities;
private Bundle mCellLocation = new Bundle();
@@ -553,7 +553,7 @@
public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
String reason, String apn, String apnType, LinkProperties linkProperties,
- LinkCapabilities linkCapabilities, int networkType, boolean roaming) {
+ NetworkCapabilities networkCapabilities, int networkType, boolean roaming) {
if (!checkNotifyPermission("notifyDataConnection()" )) {
return;
}
@@ -587,7 +587,7 @@
mDataConnectionPossible = isDataConnectivityPossible;
mDataConnectionReason = reason;
mDataConnectionLinkProperties = linkProperties;
- mDataConnectionLinkCapabilities = linkCapabilities;
+ mDataConnectionNetworkCapabilities = networkCapabilities;
if (mDataConnectionNetworkType != networkType) {
mDataConnectionNetworkType = networkType;
// need to tell registered listeners about the new network type
@@ -624,7 +624,7 @@
handleRemoveListLocked();
}
broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
- apnType, linkProperties, linkCapabilities, roaming);
+ apnType, linkProperties, networkCapabilities, roaming);
broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn, reason,
linkProperties, "");
}
@@ -794,7 +794,8 @@
pw.println(" mDataConnectionReason=" + mDataConnectionReason);
pw.println(" mDataConnectionApn=" + mDataConnectionApn);
pw.println(" mDataConnectionLinkProperties=" + mDataConnectionLinkProperties);
- pw.println(" mDataConnectionLinkCapabilities=" + mDataConnectionLinkCapabilities);
+ pw.println(" mDataConnectionNetworkCapabilities=" +
+ mDataConnectionNetworkCapabilities);
pw.println(" mCellLocation=" + mCellLocation);
pw.println(" mCellInfo=" + mCellInfo);
pw.println(" mDcRtInfo=" + mDcRtInfo);
@@ -862,7 +863,7 @@
private void broadcastDataConnectionStateChanged(int state,
boolean isDataConnectivityPossible,
String reason, String apn, String apnType, LinkProperties linkProperties,
- LinkCapabilities linkCapabilities, boolean roaming) {
+ NetworkCapabilities networkCapabilities, boolean roaming) {
// Note: not reporting to the battery stats service here, because the
// status bar takes care of that after taking into account all of the
// required info.
@@ -882,8 +883,8 @@
intent.putExtra(PhoneConstants.DATA_IFACE_NAME_KEY, iface);
}
}
- if (linkCapabilities != null) {
- intent.putExtra(PhoneConstants.DATA_LINK_CAPABILITIES_KEY, linkCapabilities);
+ if (networkCapabilities != null) {
+ intent.putExtra(PhoneConstants.DATA_NETWORK_CAPABILITIES_KEY, networkCapabilities);
}
if (roaming) intent.putExtra(PhoneConstants.DATA_NETWORK_ROAMING_KEY, true);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index f587ccc..b2aaf74 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -34,6 +34,7 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentValues;
@@ -857,7 +858,7 @@
checkManageAccountsPermission();
UserHandle user = Binder.getCallingUserHandle();
UserAccounts accounts = getUserAccountsForCaller();
- if (!canUserModifyAccounts(Binder.getCallingUid())) {
+ if (!canUserModifyAccounts(Binder.getCallingUid(), account.type)) {
try {
response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
"User cannot modify accounts");
@@ -1512,7 +1513,7 @@
checkManageAccountsPermission();
// Is user disallowed from modifying accounts?
- if (!canUserModifyAccounts(Binder.getCallingUid())) {
+ if (!canUserModifyAccounts(Binder.getCallingUid(), accountType)) {
try {
response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
"User is not allowed to add an account!");
@@ -2758,7 +2759,7 @@
Manifest.permission.USE_CREDENTIALS);
}
- private boolean canUserModifyAccounts(int callingUid) {
+ private boolean canUserModifyAccounts(int callingUid, String accountType) {
if (callingUid != Process.myUid()) {
if (getUserManager().getUserRestrictions(
new UserHandle(UserHandle.getUserId(callingUid)))
@@ -2766,6 +2767,15 @@
return false;
}
}
+
+ DevicePolicyManager dpm = (DevicePolicyManager) mContext
+ .getSystemService(Context.DEVICE_POLICY_SERVICE);
+ String[] typesArray = dpm.getAccountTypesWithManagementDisabled();
+ for (String forbiddenType : typesArray) {
+ if (forbiddenType.equals(accountType)) {
+ return false;
+ }
+ }
return true;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5358c1a..344268a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1328,14 +1328,12 @@
String host = "";
String port = "";
String exclList = "";
- String pacFileUrl = "";
+ Uri pacFileUrl = Uri.EMPTY;
if (proxy != null) {
host = proxy.getHost();
port = Integer.toString(proxy.getPort());
exclList = proxy.getExclusionListAsString();
- if (proxy.getPacFileUrl() != null) {
- pacFileUrl = proxy.getPacFileUrl().toString();
- }
+ pacFileUrl = proxy.getPacFileUrl();
}
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 7c29d85..2e439ae 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1421,7 +1421,8 @@
final TaskRecord nextTask = next.task;
final TaskRecord prevTask = prev != null ? prev.task : null;
- if (prevTask != null && prevTask.mOnTopOfHome && prev.finishing && prev.frontOfTask) {
+ if (prevTask != null && prevTask.stack == this &&
+ prevTask.mOnTopOfHome && prev.finishing && prev.frontOfTask) {
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
if (prevTask == nextTask) {
prevTask.setFrontOfTask();
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index 0749f24..63178eb 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -25,6 +25,7 @@
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.net.ProxyInfo;
+import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -32,7 +33,6 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -71,7 +71,7 @@
public static final String KEY_PROXY = "keyProxy";
private String mCurrentPac;
@GuardedBy("mProxyLock")
- private String mPacUrl;
+ private Uri mPacUrl;
private AlarmManager mAlarmManager;
@GuardedBy("mProxyLock")
@@ -100,7 +100,7 @@
public void run() {
String file;
synchronized (mProxyLock) {
- if (mPacUrl == null) return;
+ if (Uri.EMPTY.equals(mPacUrl)) return;
try {
file = get(mPacUrl);
} catch (IOException ioe) {
@@ -158,13 +158,13 @@
* @return Returns true when the broadcast should not be sent
*/
public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
- if (proxy.getPacFileUrl() != null) {
+ if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
// Allow to send broadcast, nothing to do.
return false;
}
synchronized (mProxyLock) {
- mPacUrl = proxy.getPacFileUrl().toString();
+ mPacUrl = proxy.getPacFileUrl();
}
mCurrentDelay = DELAY_1;
mHasSentBroadcast = false;
@@ -196,8 +196,8 @@
*
* @throws IOException
*/
- private static String get(String urlString) throws IOException {
- URL url = new URL(urlString);
+ private static String get(Uri pacUri) throws IOException {
+ URL url = new URL(pacUri.toString());
URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY);
return new String(Streams.readFully(urlConnection.getInputStream()));
}
diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java
index 09f1c567..51ee93b 100644
--- a/services/core/java/com/android/server/location/FlpHardwareProvider.java
+++ b/services/core/java/com/android/server/location/FlpHardwareProvider.java
@@ -67,9 +67,9 @@
public static FlpHardwareProvider getInstance(Context context) {
if (sSingletonInstance == null) {
sSingletonInstance = new FlpHardwareProvider(context);
+ sSingletonInstance.nativeInit();
}
- nativeInit();
return sSingletonInstance;
}
@@ -96,8 +96,7 @@
Looper.myLooper());
}
- public static boolean isSupported() {
- nativeInit();
+ public boolean isSupported() {
return nativeIsSupported();
}
@@ -218,9 +217,9 @@
// Core members
private static native void nativeClassInit();
private static native boolean nativeIsSupported();
- private static native void nativeInit();
// FlpLocationInterface members
+ private native void nativeInit();
private native int nativeGetBatchSize();
private native void nativeStartBatching(int requestId, FusedBatchOptions options);
private native void nativeUpdateBatchingOptions(int requestId, FusedBatchOptions optionsObject);
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 015032b..41ab626 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -16,6 +16,7 @@
package com.android.server.media;
+import android.app.ActivityManager;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.routeprovider.RouteRequest;
@@ -42,6 +43,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -80,7 +82,9 @@
private final MessageHandler mHandler;
- private final int mPid;
+ private final int mOwnerPid;
+ private final int mOwnerUid;
+ private final int mUserId;
private final SessionInfo mSessionInfo;
private final String mTag;
private final ControllerStub mController;
@@ -110,10 +114,12 @@
private boolean mIsActive = false;
- public MediaSessionRecord(int pid, String packageName, ISessionCallback cb, String tag,
- MediaSessionService service, Handler handler) {
- mPid = pid;
- mSessionInfo = new SessionInfo(UUID.randomUUID().toString(), packageName);
+ public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
+ ISessionCallback cb, String tag, MediaSessionService service, Handler handler) {
+ mOwnerPid = ownerPid;
+ mOwnerUid = ownerUid;
+ mUserId = userId;
+ mSessionInfo = new SessionInfo(UUID.randomUUID().toString(), ownerPackageName);
mTag = tag;
mController = new ControllerStub();
mSession = new SessionStub();
@@ -187,6 +193,15 @@
}
/**
+ * Get the user id this session was created for.
+ *
+ * @return The user id for this session.
+ */
+ public int getUserId() {
+ return mUserId;
+ }
+
+ /**
* Check if this session has system priorty and should receive media buttons
* before any other sessions.
*
@@ -305,7 +320,8 @@
pw.println(prefix + mTag + " " + this);
final String indent = prefix + " ";
- pw.println(indent + "pid=" + mPid);
+ pw.println(indent + "ownerPid=" + mOwnerPid + ", ownerUid=" + mOwnerUid
+ + ", userId=" + mUserId);
pw.println(indent + "info=" + mSessionInfo.toString());
pw.println(indent + "published=" + mIsActive);
pw.println(indent + "flags=" + mFlags);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index fb858fc..008f9be 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -63,7 +63,6 @@
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();
private MediaSessionRecord mPrioritySession;
@@ -72,8 +71,8 @@
// session so we drop late callbacks properly.
private int mShowRoutesRequestId = 0;
- // TODO refactor to have per user state. See MediaRouterService for an
- // example
+ // TODO refactor to have per user state for providers. See
+ // MediaRouterService for an example
public MediaSessionService(Context context) {
super(context);
@@ -211,25 +210,42 @@
* <ul>
* <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
* permission</li>
- * <li>the caller's listener is one of the enabled notification listeners</li>
+ * <li>the caller's listener is one of the enabled notification listeners
+ * for the caller's user</li>
* </ul>
*/
- private void enforceMediaPermissions(ComponentName compName, int pid, int uid) {
+ private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
+ int resolvedUserId) {
if (getContext()
.checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
!= PackageManager.PERMISSION_GRANTED
- && !isEnabledNotificationListener(compName)) {
+ && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
+ resolvedUserId)) {
throw new SecurityException("Missing permission to control media.");
}
}
- private boolean isEnabledNotificationListener(ComponentName compName) {
+ /**
+ * This checks if the component is an enabled notification listener for the
+ * specified user. Enabled components may only operate on behalf of the user
+ * they're running as.
+ *
+ * @param compName The component that is enabled.
+ * @param userId The user id of the caller.
+ * @param forUserId The user id they're making the request on behalf of.
+ * @return True if the component is enabled, false otherwise
+ */
+ private boolean isEnabledNotificationListener(ComponentName compName, int userId,
+ int forUserId) {
+ if (userId != forUserId) {
+ // You may not access another user's content as an enabled listener.
+ return false;
+ }
if (compName != null) {
- final int currentUser = ActivityManager.getCurrentUser();
final String enabledNotifListeners = Settings.Secure.getStringForUser(
getContext().getContentResolver(),
Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
- currentUser);
+ userId);
if (enabledNotifListeners != null) {
final String[] components = enabledNotifListeners.split(":");
for (int i = 0; i < components.length; i++) {
@@ -248,23 +264,23 @@
}
if (DEBUG) {
Log.d(TAG, "not ok to get sessions, " + compName +
- " is not in list of ENABLED_NOTIFICATION_LISTENERS");
+ " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
}
}
return false;
}
- private MediaSessionRecord createSessionInternal(int pid, String packageName,
- ISessionCallback cb, String tag, boolean forCalls) {
+ private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
+ String callerPackageName, ISessionCallback cb, String tag) {
synchronized (mLock) {
- return createSessionLocked(pid, packageName, cb, tag);
+ return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
}
}
- private MediaSessionRecord createSessionLocked(int pid, String packageName,
- ISessionCallback cb, String tag) {
- final MediaSessionRecord session = new MediaSessionRecord(pid, packageName, cb, tag, this,
- mHandler);
+ private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
+ String callerPackageName, ISessionCallback cb, String tag) {
+ final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
+ callerPackageName, cb, tag, this, mHandler);
try {
cb.asBinder().linkToDeath(session, 0);
} catch (RemoteException e) {
@@ -273,7 +289,7 @@
mRecords.add(session);
mPriorityStack.addSession(session);
if (DEBUG) {
- Log.d(TAG, "Created session for package " + packageName + " with tag " + tag);
+ Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag);
}
return session;
}
@@ -358,41 +374,50 @@
// 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 {
+ public ISession createSession(String packageName, ISessionCallback cb, String tag,
+ int userId) throws RemoteException {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
enforcePackageName(packageName, uid);
+ int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+ false /* allowAll */, true /* requireFull */, "createSession", packageName);
if (cb == null) {
throw new IllegalArgumentException("Controller callback cannot be null");
}
- return createSessionInternal(pid, packageName, cb, tag, false).getSessionBinder();
+ return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
+ .getSessionBinder();
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
- public List<IBinder> getSessions(ComponentName componentName) {
+ public List<IBinder> getSessions(ComponentName componentName, int userId) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
+ String packageName = null;
if (componentName != null) {
// If they gave us a component name verify they own the
// package
- enforcePackageName(componentName.getPackageName(), uid);
+ packageName = componentName.getPackageName();
+ enforcePackageName(packageName, uid);
}
- // Then check if they have the permissions or their component is
- // allowed
- enforceMediaPermissions(componentName, pid, uid);
+ // Check that they can make calls on behalf of the user and
+ // get the final user id
+ int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+ true /* allowAll */, true /* requireFull */, "getSessions", packageName);
+ // Check if they have the permissions or their component is
+ // enabled for the user they're calling from.
+ enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
ArrayList<IBinder> binders = new ArrayList<IBinder>();
synchronized (mLock) {
ArrayList<MediaSessionRecord> records = mPriorityStack
- .getActiveSessions();
+ .getActiveSessions(resolvedUserId);
int size = records.size();
for (int i = 0; i < size; i++) {
binders.add(records.get(i).getControllerBinder().asBinder());
@@ -428,7 +453,7 @@
mRecords.get(i).dump(pw, "");
pw.println();
}
- mPriorityStack.dumpLocked(pw, "");
+ mPriorityStack.dump(pw, "");
pw.println("Providers:");
count = mProviders.size();
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index f9f004d..1e1818d 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -18,6 +18,7 @@
import android.media.session.PlaybackState;
import android.media.session.Session;
+import android.os.UserHandle;
import android.text.TextUtils;
import java.io.PrintWriter;
@@ -104,11 +105,12 @@
* Get the current priority sorted list of active sessions. The most
* important session is at index 0 and the least important at size - 1.
*
+ * @param userId The user to check.
* @return All the active sessions in priority order.
*/
- public ArrayList<MediaSessionRecord> getActiveSessions() {
+ public ArrayList<MediaSessionRecord> getActiveSessions(int userId) {
if (mCachedActiveList == null) {
- mCachedActiveList = getPriorityListLocked(true, 0);
+ mCachedActiveList = getPriorityListLocked(true, 0, userId);
}
return mCachedActiveList;
}
@@ -118,13 +120,14 @@
* transport controls. The most important session is at index 0 and the
* least important at size -1.
*
+ * @param userId The user to check.
* @return All the active sessions that handle transport controls in
* priority order.
*/
- public ArrayList<MediaSessionRecord> getTransportControlSessions() {
+ public ArrayList<MediaSessionRecord> getTransportControlSessions(int userId) {
if (mCachedTransportControlList == null) {
mCachedTransportControlList = getPriorityListLocked(true,
- Session.FLAG_HANDLES_TRANSPORT_CONTROLS);
+ Session.FLAG_HANDLES_TRANSPORT_CONTROLS, userId);
}
return mCachedTransportControlList;
}
@@ -132,13 +135,14 @@
/**
* Get the highest priority active session.
*
+ * @param userId The user to check.
* @return The current highest priority session or null.
*/
- public MediaSessionRecord getDefaultSession() {
+ public MediaSessionRecord getDefaultSession(int userId) {
if (mCachedDefault != null) {
return mCachedDefault;
}
- ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0);
+ ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId);
if (records.size() > 0) {
return records.get(0);
}
@@ -148,22 +152,24 @@
/**
* Get the highest priority session that can handle media buttons.
*
+ * @param userId The user to check.
* @return The default media button session or null.
*/
- public MediaSessionRecord getDefaultMediaButtonSession() {
+ public MediaSessionRecord getDefaultMediaButtonSession(int userId) {
if (mCachedButtonReceiver != null) {
return mCachedButtonReceiver;
}
ArrayList<MediaSessionRecord> records = getPriorityListLocked(true,
- Session.FLAG_HANDLES_MEDIA_BUTTONS);
+ Session.FLAG_HANDLES_MEDIA_BUTTONS, userId);
if (records.size() > 0) {
mCachedButtonReceiver = records.get(0);
}
return mCachedButtonReceiver;
}
- public void dumpLocked(PrintWriter pw, String prefix) {
- ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0);
+ public void dump(PrintWriter pw, String prefix) {
+ ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0,
+ UserHandle.USER_ALL);
int count = sortedSessions.size();
pw.println(prefix + "Sessions Stack - have " + count + " sessions:");
String indent = prefix + " ";
@@ -182,9 +188,12 @@
* all sessions.
* @param withFlags Only return sessions with all the specified flags set. 0
* returns all sessions.
+ * @param userId The user to get sessions for. {@link UserHandle#USER_ALL}
+ * will return sessions for all users.
* @return The priority sorted list of sessions.
*/
- private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags) {
+ private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags,
+ int userId) {
ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>();
int lastLocalIndex = 0;
int lastActiveIndex = 0;
@@ -194,7 +203,12 @@
for (int i = 0; i < size; i++) {
final MediaSessionRecord session = mSessions.get(i);
+ if (userId != UserHandle.USER_ALL && userId != session.getUserId()) {
+ // Filter out sessions for the wrong user
+ continue;
+ }
if ((session.getFlags() & withFlags) != withFlags) {
+ // Filter out sessions with the wrong flags
continue;
}
if (!session.isActive()) {
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
new file mode 100644
index 0000000..c8b1ba0
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -0,0 +1,44 @@
+/*
+ * 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 java.util.Comparator;
+
+/**
+ * Sorts notificaitons into attention-relelvant order.
+ */
+public class NotificationComparator
+ implements Comparator<NotificationManagerService.NotificationRecord> {
+
+ @Override
+ public int compare(NotificationManagerService.NotificationRecord lhs,
+ NotificationManagerService.NotificationRecord rhs) {
+ final int leftScore = lhs.sbn.getScore();
+ final int rightScore = rhs.sbn.getScore();
+ if (leftScore != rightScore) {
+ // by priority, high to low
+ return -1 * Integer.compare(leftScore, rightScore);
+ }
+ final float leftPeple = lhs.getContactAffinity();
+ final float rightPeople = rhs.getContactAffinity();
+ if (leftPeple != rightPeople) {
+ // by contact proximity, close to far
+ return -1 * Float.compare(leftPeple, rightPeople);
+ }
+ // then break ties by time, most recent first
+ return -1 * Long.compare(lhs.sbn.getPostTime(), rhs.sbn.getPostTime());
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4698587..7a4f951 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -49,8 +49,10 @@
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IInterface;
+import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
@@ -61,6 +63,7 @@
import android.service.notification.IConditionListener;
import android.service.notification.IConditionProvider;
import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationOrderUpdate;
import android.service.notification.StatusBarNotification;
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
@@ -76,7 +79,6 @@
import android.widget.Toast;
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.SystemService;
@@ -104,9 +106,12 @@
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
/** {@hide} */
public class NotificationManagerService extends SystemService {
@@ -118,6 +123,8 @@
// message codes
static final int MESSAGE_TIMEOUT = 2;
static final int MESSAGE_SAVE_POLICY_FILE = 3;
+ static final int MESSAGE_RECONSIDER_RANKING = 4;
+ static final int MESSAGE_SEND_RANKING_UPDATE = 5;
static final int LONG_DELAY = 3500; // 3.5 seconds
static final int SHORT_DELAY = 2000; // 2 seconds
@@ -147,6 +154,9 @@
final IBinder mForegroundToken = new Binder();
private WorkerHandler mHandler;
+ private final HandlerThread mRankingThread = new HandlerThread("ranker",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ private Handler mRankingHandler = null;
private Light mNotificationLight;
Light mAttentionLight;
@@ -171,6 +181,7 @@
// used as a mutex for access to all active notifications & listeners
final ArrayList<NotificationRecord> mNotificationList =
new ArrayList<NotificationRecord>();
+ final NotificationComparator mRankingComparator = new NotificationComparator();
final ArrayMap<String, NotificationRecord> mNotificationsByKey =
new ArrayMap<String, NotificationRecord>();
final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
@@ -193,7 +204,7 @@
private static final String TAG_PACKAGE = "package";
private static final String ATTR_NAME = "name";
- final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>();
+ final ArrayList<NotificationSignalExtractor> mSignalExtractors = new ArrayList<NotificationSignalExtractor>();
private final UserProfiles mUserProfiles = new UserProfiles();
private NotificationListeners mListeners;
@@ -446,6 +457,7 @@
final StatusBarNotification sbn;
SingleNotificationStats stats;
IBinder statusBarKey;
+ private float mContactAffinity;
NotificationRecord(StatusBarNotification sbn)
{
@@ -528,6 +540,14 @@
this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(),
this.sbn.getNotification());
}
+
+ public void setContactAffinity(float contactAffinity) {
+ mContactAffinity = contactAffinity;
+ }
+
+ public float getContactAffinity() {
+ return mContactAffinity;
+ }
}
private static final class ToastRecord
@@ -707,7 +727,7 @@
boolean queryRemove = false;
boolean packageChanged = false;
boolean cancelNotifications = true;
-
+
if (action.equals(Intent.ACTION_PACKAGE_ADDED)
|| (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
|| action.equals(Intent.ACTION_PACKAGE_RESTARTED)
@@ -849,6 +869,8 @@
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
mHandler = new WorkerHandler();
+ mRankingThread.start();
+ mRankingHandler = new RankingWorkerHandler(mRankingThread.getLooper());
mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
@Override
@@ -925,21 +947,22 @@
mSettingsObserver = new SettingsObserver(mHandler);
- // spin up NotificationScorers
- String[] notificationScorerNames = resources.getStringArray(
- R.array.config_notificationScorers);
- for (String scorerName : notificationScorerNames) {
+ // spin up NotificationSignalExtractors
+ String[] extractorNames = resources.getStringArray(
+ R.array.config_notificationSignalExtractors);
+ for (String extractorName : extractorNames) {
try {
- Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName);
- NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance();
- scorer.initialize(getContext());
- mScorers.add(scorer);
+ Class<?> extractorClass = getContext().getClassLoader().loadClass(extractorName);
+ NotificationSignalExtractor extractor =
+ (NotificationSignalExtractor) extractorClass.newInstance();
+ extractor.initialize(getContext());
+ mSignalExtractors.add(extractor);
} catch (ClassNotFoundException e) {
- Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e);
+ Slog.w(TAG, "Couldn't find extractor " + extractorName + ".", e);
} catch (InstantiationException e) {
- Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e);
+ Slog.w(TAG, "Couldn't instantiate extractor " + extractorName + ".", e);
} catch (IllegalAccessException e) {
- Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e);
+ Slog.w(TAG, "Problem accessing extractor " + extractorName + ".", e);
}
}
@@ -1150,6 +1173,7 @@
* System-only API for getting a list of current (i.e. not cleared) notifications.
*
* Requires ACCESS_NOTIFICATIONS which is signature|system.
+ * @returns A list of all the notifications, in natural order.
*/
@Override
public StatusBarNotification[] getActiveNotifications(String callingPkg) {
@@ -1306,6 +1330,9 @@
* should be used.
*
* @param token The binder for the listener, to check that the caller is allowed
+ * @param keys the notification keys to fetch, or null for all active notifications.
+ * @returns The return value will contain the notifications specified in keys, in that
+ * order, or if keys is null, all the notifications, in natural order.
*/
@Override
public StatusBarNotification[] getActiveNotificationsFromListener(
@@ -1337,7 +1364,7 @@
@Override
public String[] getActiveNotificationKeysFromListener(INotificationListener token) {
- return NotificationManagerService.this.getActiveNotificationKeysFromListener(token);
+ return NotificationManagerService.this.getActiveNotificationKeys(token);
}
@Override
@@ -1409,19 +1436,21 @@
}
};
- private String[] getActiveNotificationKeysFromListener(INotificationListener token) {
- synchronized (mNotificationList) {
- final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
- final ArrayList<String> keys = new ArrayList<String>();
- final int N = mNotificationList.size();
- for (int i=0; i<N; i++) {
- final StatusBarNotification sbn = mNotificationList.get(i).sbn;
- if (info.enabledAndUserMatches(sbn.getUserId())) {
- keys.add(sbn.getKey());
+ private String[] getActiveNotificationKeys(INotificationListener token) {
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ final ArrayList<String> keys = new ArrayList<String>();
+ if (info.isEnabledForCurrentProfiles()) {
+ synchronized (mNotificationList) {
+ final int N = mNotificationList.size();
+ for (int i = 0; i < N; i++) {
+ final StatusBarNotification sbn = mNotificationList.get(i).sbn;
+ if (info.enabledAndUserMatches(sbn.getUserId())) {
+ keys.add(sbn.getKey());
+ }
}
}
- return keys.toArray(new String[keys.size()]);
}
+ return keys.toArray(new String[keys.size()]);
}
void dumpImpl(PrintWriter pw) {
@@ -1578,26 +1607,23 @@
// 1. initial score: buckets of 10, around the app
int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
- // 2. Consult external heuristics (TBD)
-
- // 3. Apply local rules
-
- int initialScore = score;
- if (!mScorers.isEmpty()) {
- if (DBG) Slog.v(TAG, "Initial score is " + score + ".");
- for (NotificationScorer scorer : mScorers) {
+ // 2. extract ranking signals from the notification data
+ final StatusBarNotification n = new StatusBarNotification(
+ pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
+ user);
+ NotificationRecord r = new NotificationRecord(n);
+ if (!mSignalExtractors.isEmpty()) {
+ for (NotificationSignalExtractor extractor : mSignalExtractors) {
try {
- score = scorer.getScore(notification, score);
+ RankingFuture future = extractor.process(r);
+ scheduleRankingReconsideration(future);
} catch (Throwable t) {
- Slog.w(TAG, "Scorer threw on .getScore.", t);
+ Slog.w(TAG, "NotificationSignalExtractor failed.", t);
}
}
- if (DBG) Slog.v(TAG, "Final score is " + score + ".");
}
- // add extra to indicate score modified by NotificationScorer
- notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED,
- score != initialScore);
+ // 3. Apply local rules
// blocked apps
if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
@@ -1608,10 +1634,6 @@
}
}
- if (DBG) {
- Slog.v(TAG, "Assigned score=" + score + " to " + notification);
- }
-
if (score < SCORE_DISPLAY_THRESHOLD) {
// Notification will be blocked because the score is too low.
return;
@@ -1626,12 +1648,7 @@
if (DBG || intercept) Slog.v(TAG,
"pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept);
synchronized (mNotificationList) {
- final StatusBarNotification n = new StatusBarNotification(
- pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
- user);
- NotificationRecord r = new NotificationRecord(n);
NotificationRecord old = null;
-
int index = indexOfNotificationLocked(pkg, tag, id, userId);
if (index < 0) {
mNotificationList.add(r);
@@ -1651,6 +1668,8 @@
}
mNotificationsByKey.put(n.getKey(), r);
+ Collections.sort(mNotificationList, mRankingComparator);
+
// Ensure if this is a foreground service that the proper additional
// flags are set.
if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
@@ -1948,6 +1967,57 @@
}
}
+ private void scheduleRankingReconsideration(RankingFuture future) {
+ if (future != null) {
+ Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, future);
+ long delay = future.getDelay(TimeUnit.MILLISECONDS);
+ mRankingHandler.sendMessageDelayed(m, delay);
+ }
+ }
+
+ private void handleRankingReconsideration(Message message) {
+ if (!(message.obj instanceof RankingFuture)) return;
+
+ RankingFuture future = (RankingFuture) message.obj;
+ future.run();
+ try {
+ NotificationRecord record = future.get();
+ synchronized (mNotificationList) {
+ int before = mNotificationList.indexOf(record);
+ if (before != -1) {
+ Collections.sort(mNotificationList, mRankingComparator);
+ int after = mNotificationList.indexOf(record);
+
+ if (before != after) {
+ scheduleSendRankingUpdate();
+ }
+ }
+ }
+ } catch (InterruptedException e) {
+ // we're running the future explicitly, so this should never happen
+ } catch (ExecutionException e) {
+ // we're running the future explicitly, so this should never happen
+ }
+ }
+
+ private void scheduleSendRankingUpdate() {
+ mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
+ Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
+ mHandler.sendMessage(m);
+ }
+
+ private void handleSendRankingUpdate() {
+ synchronized (mNotificationList) {
+ final int N = mNotificationList.size();
+ ArrayList<StatusBarNotification> sbns =
+ new ArrayList<StatusBarNotification>(N);
+ for (int i = 0; i < N; i++ ) {
+ sbns.add(mNotificationList.get(i).sbn);
+ }
+ mListeners.notifyOrderUpdateLocked(sbns);
+ }
+ }
+
private final class WorkerHandler extends Handler
{
@Override
@@ -1961,11 +2031,30 @@
case MESSAGE_SAVE_POLICY_FILE:
handleSavePolicyFile();
break;
+ case MESSAGE_SEND_RANKING_UPDATE:
+ handleSendRankingUpdate();
+ break;
+ }
+ }
+
+ }
+
+ private final class RankingWorkerHandler extends Handler
+ {
+ public RankingWorkerHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_RECONSIDER_RANKING:
+ handleRankingReconsideration(msg);
+ break;
}
}
}
-
// Notifications
// ============================================================================
static int clamp(int x, int low, int high) {
@@ -2346,9 +2435,9 @@
@Override
public void onServiceAdded(ManagedServiceInfo info) {
final INotificationListener listener = (INotificationListener) info.service;
- final String[] keys = getActiveNotificationKeysFromListener(listener);
+ final String[] keys = getActiveNotificationKeys(listener);
try {
- listener.onListenerConnected(keys);
+ listener.onListenerConnected(new NotificationOrderUpdate(keys));
} catch (RemoteException e) {
// we tried
}
@@ -2361,12 +2450,18 @@
// make a copy in case changes are made to the underlying Notification object
final StatusBarNotification sbnClone = sbn.clone();
for (final ManagedServiceInfo info : mServices) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- notifyPostedIfUserMatch(info, sbnClone);
+ if (info.isEnabledForCurrentProfiles()) {
+ final INotificationListener listener = (INotificationListener) info.service;
+ final String[] keys = getActiveNotificationKeys(listener);
+ if (keys.length > 0) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ notifyPostedIfUserMatch(info, sbnClone, keys);
+ }
+ });
}
- });
+ }
}
}
@@ -2378,39 +2473,83 @@
// NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
// notification
final StatusBarNotification sbnLight = sbn.cloneLight();
- for (ManagedServiceInfo serviceInfo : mServices) {
- final ManagedServiceInfo info = (ManagedServiceInfo) serviceInfo;
+ for (final ManagedServiceInfo info : mServices) {
+ if (info.isEnabledForCurrentProfiles()) {
+ final INotificationListener listener = (INotificationListener) info.service;
+ final String[] keys = getActiveNotificationKeys(listener);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ notifyRemovedIfUserMatch(info, sbnLight, keys);
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * asynchronously notify all listeners about a reordering of notifications
+ * @param sbns an array of {@link StatusBarNotification}s to consider. This code
+ * must not rely on mutable members of these objects, such as the
+ * {@link Notification}.
+ */
+ public void notifyOrderUpdateLocked(final ArrayList<StatusBarNotification> sbns) {
+ for (final ManagedServiceInfo serviceInfo : mServices) {
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyRemovedIfUserMatch(info, sbnLight);
+ notifyOrderUpdateIfUserMatch(serviceInfo, sbns);
}
});
}
}
- private void notifyPostedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) {
+ private void notifyPostedIfUserMatch(final ManagedServiceInfo info,
+ final StatusBarNotification sbn, String[] keys) {
if (!info.enabledAndUserMatches(sbn.getUserId())) {
return;
}
final INotificationListener listener = (INotificationListener)info.service;
try {
- listener.onNotificationPosted(sbn);
+ listener.onNotificationPosted(sbn, new NotificationOrderUpdate(keys));
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
}
}
- private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) {
+ private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn,
+ String[] keys) {
if (!info.enabledAndUserMatches(sbn.getUserId())) {
return;
}
final INotificationListener listener = (INotificationListener)info.service;
try {
- listener.onNotificationRemoved(sbn);
+ listener.onNotificationRemoved(sbn, new NotificationOrderUpdate(keys));
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
}
}
+
+ /**
+ * @param sbns an array of {@link StatusBarNotification}s to consider. This code
+ * must not rely on mutable members of these objects, such as the
+ * {@link Notification}.
+ */
+ public void notifyOrderUpdateIfUserMatch(ManagedServiceInfo info,
+ ArrayList<StatusBarNotification> sbns) {
+ ArrayList<String> keys = new ArrayList<String>(sbns.size());
+ for (StatusBarNotification sbn: sbns) {
+ if (info.enabledAndUserMatches(sbn.getUserId())) {
+ keys.add(sbn.getKey());
+ }
+ }
+ final INotificationListener listener = (INotificationListener)info.service;
+ try {
+ listener.onNotificationOrderUpdate(
+ new NotificationOrderUpdate(keys.toArray(new String[keys.size()])));
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
new file mode 100644
index 0000000..a41fdfe
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
@@ -0,0 +1,41 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.android.server.notification;
+
+import android.content.Context;
+
+/**
+ * Extracts signals that will be useful to the {@link NotificationComparator} and caches them
+ * on the {@link NotificationManagerService.NotificationRecord} object. These annotations will
+ * not be passed on to {@link android.service.notification.NotificationListenerService}s.
+ */
+public interface NotificationSignalExtractor {
+
+ /** One-time initialization. */
+ public void initialize(Context context);
+
+ /**
+ * Called once per notification that is posted or updated.
+ *
+ * @return null if the work is done, or a future if there is more to do. The
+ * {@link RankingFuture} will be run on a worker thread, and if notifications are re-ordered
+ * by that execution, the {@link NotificationManagerService} may send order update
+ * events to the {@link android.service.notification.NotificationListenerService}s.
+ */
+ public RankingFuture process(NotificationManagerService.NotificationRecord notification);
+
+}
diff --git a/services/core/java/com/android/server/notification/RankingFuture.java b/services/core/java/com/android/server/notification/RankingFuture.java
new file mode 100644
index 0000000..33aad8d
--- /dev/null
+++ b/services/core/java/com/android/server/notification/RankingFuture.java
@@ -0,0 +1,118 @@
+/*
+ * 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 java.util.concurrent.Delayed;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public abstract class RankingFuture
+ implements ScheduledFuture<NotificationManagerService.NotificationRecord> {
+ private static final long IMMEDIATE = 0l;
+
+ private static final int START = 0;
+ private static final int RUNNING = 1;
+ private static final int DONE = 2;
+ private static final int CANCELLED = 3;
+
+ private int mState;
+ private long mDelay;
+ protected NotificationManagerService.NotificationRecord mRecord;
+
+ public RankingFuture(NotificationManagerService.NotificationRecord record) {
+ this(record, IMMEDIATE);
+ }
+
+ public RankingFuture(NotificationManagerService.NotificationRecord record, long delay) {
+ mDelay = delay;
+ mRecord = record;
+ mState = START;
+ }
+
+ public void run() {
+ if (mState == START) {
+ mState = RUNNING;
+
+ work();
+
+ mState = DONE;
+ synchronized (this) {
+ notifyAll();
+ }
+ }
+ }
+
+ @Override
+ public long getDelay(TimeUnit unit) {
+ return unit.convert(mDelay, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public int compareTo(Delayed another) {
+ return Long.compare(getDelay(TimeUnit.MICROSECONDS),
+ another.getDelay(TimeUnit.MICROSECONDS));
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ if (mState == START) { // can't cancel if running or done
+ mState = CANCELLED;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return mState == CANCELLED;
+ }
+
+ @Override
+ public boolean isDone() {
+ return mState == DONE;
+ }
+
+ @Override
+ public NotificationManagerService.NotificationRecord get()
+ throws InterruptedException, ExecutionException {
+ while (!isDone()) {
+ synchronized (this) {
+ this.wait();
+ }
+ }
+ return mRecord;
+ }
+
+ @Override
+ public NotificationManagerService.NotificationRecord get(long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ long timeoutMillis = unit.convert(timeout, TimeUnit.MILLISECONDS);
+ long start = System.currentTimeMillis();
+ long now = System.currentTimeMillis();
+ while (!isDone() && (now - start) < timeoutMillis) {
+ try {
+ wait(timeoutMillis - (now - start));
+ } catch (InterruptedException e) {
+ now = System.currentTimeMillis();
+ }
+ }
+ return mRecord;
+ }
+
+ public abstract void work();
+}
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
new file mode 100644
index 0000000..8cd2f9b2
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -0,0 +1,298 @@
+/*
+* 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.Notification;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.LruCache;
+import android.util.Slog;
+
+import com.android.server.notification.NotificationManagerService.NotificationRecord;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+/**
+ * This {@link NotificationSignalExtractor} attempts to validate
+ * people references. Also elevates the priority of real people.
+ */
+public class ValidateNotificationPeople implements NotificationSignalExtractor {
+ private static final String TAG = "ValidateNotificationPeople";
+ private static final boolean INFO = true;
+ private static final boolean DEBUG = false;
+
+ private static final boolean ENABLE_PEOPLE_VALIDATOR = true;
+ private static final String SETTING_ENABLE_PEOPLE_VALIDATOR =
+ "validate_notification_people_enabled";
+ private static final String[] LOOKUP_PROJECTION = { Contacts._ID };
+ private static final int MAX_PEOPLE = 10;
+ private static final int PEOPLE_CACHE_SIZE = 200;
+
+ private static final float NONE = 0f;
+ private static final float VALID_CONTACT = 0.5f;
+ // TODO private static final float STARRED_CONTACT = 1f;
+
+ protected boolean mEnabled;
+ private Context mContext;
+
+ // maps raw person handle to resolved person object
+ private LruCache<String, LookupResult> mPeopleCache;
+
+ private RankingFuture validatePeople(NotificationRecord record) {
+ float affinity = NONE;
+ Bundle extras = record.getNotification().extras;
+ if (extras == null) {
+ return null;
+ }
+
+ final String[] people = getExtraPeople(extras);
+ if (people == null || people.length == 0) {
+ return null;
+ }
+
+ if (INFO) Slog.i(TAG, "Validating: " + record.sbn.getKey());
+ final LinkedList<String> pendingLookups = new LinkedList<String>();
+ for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) {
+ final String handle = people[personIdx];
+ if (TextUtils.isEmpty(handle)) continue;
+
+ synchronized (mPeopleCache) {
+ LookupResult lookupResult = mPeopleCache.get(handle);
+ if (lookupResult == null || lookupResult.isExpired()) {
+ pendingLookups.add(handle);
+ } else {
+ if (DEBUG) Slog.d(TAG, "using cached lookupResult: " + lookupResult.mId);
+ }
+ if (lookupResult != null) {
+ affinity = Math.max(affinity, lookupResult.getAffinity());
+ }
+ }
+ }
+
+ // record the best available data, so far:
+ record.setContactAffinity(affinity);
+
+ if (pendingLookups.isEmpty()) {
+ if (INFO) Slog.i(TAG, "final affinity: " + affinity);
+ return null;
+ }
+
+ if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + record.sbn.getKey());
+ return new RankingFuture(record) {
+ @Override
+ public void work() {
+ if (INFO) Slog.i(TAG, "Executing: validation for: " + mRecord.sbn.getKey());
+ float affinity = NONE;
+ LookupResult lookupResult = null;
+ for (final String handle: pendingLookups) {
+ final Uri uri = Uri.parse(handle);
+ if ("tel".equals(uri.getScheme())) {
+ if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
+ lookupResult = resolvePhoneContact(handle, uri.getSchemeSpecificPart());
+ } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
+ if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
+ lookupResult = resolveContactsUri(handle, uri);
+ } else {
+ Slog.w(TAG, "unsupported URI " + handle);
+ }
+ }
+ if (lookupResult != null) {
+ affinity = Math.max(affinity, lookupResult.getAffinity());
+ }
+
+ float affinityBound = mRecord.getContactAffinity();
+ affinity = Math.max(affinity, affinityBound);
+ mRecord.setContactAffinity(affinity);
+ if (INFO) Slog.i(TAG, "final affinity: " + affinity);
+ }
+ };
+ }
+
+ private String[] getExtraPeople(Bundle extras) {
+ String[] people = extras.getStringArray(Notification.EXTRA_PEOPLE);
+ if (people != null) {
+ return people;
+ }
+
+ ArrayList<String> stringArray = extras.getStringArrayList(Notification.EXTRA_PEOPLE);
+ if (stringArray != null) {
+ return (String[]) stringArray.toArray();
+ }
+
+ String string = extras.getString(Notification.EXTRA_PEOPLE);
+ if (string != null) {
+ people = new String[1];
+ people[0] = string;
+ return people;
+ }
+ char[] charArray = extras.getCharArray(Notification.EXTRA_PEOPLE);
+ if (charArray != null) {
+ people = new String[1];
+ people[0] = new String(charArray);
+ return people;
+ }
+
+ CharSequence charSeq = extras.getCharSequence(Notification.EXTRA_PEOPLE);
+ if (charSeq != null) {
+ people = new String[1];
+ people[0] = charSeq.toString();
+ return people;
+ }
+
+ CharSequence[] charSeqArray = extras.getCharSequenceArray(Notification.EXTRA_PEOPLE);
+ if (charSeqArray != null) {
+ final int N = charSeqArray.length;
+ people = new String[N];
+ for (int i = 0; i < N; i++) {
+ people[i] = charSeqArray[i].toString();
+ }
+ return people;
+ }
+
+ ArrayList<CharSequence> charSeqList =
+ extras.getCharSequenceArrayList(Notification.EXTRA_PEOPLE);
+ if (charSeqList != null) {
+ final int N = charSeqList.size();
+ people = new String[N];
+ for (int i = 0; i < N; i++) {
+ people[i] = charSeqList.get(i).toString();
+ }
+ return people;
+ }
+ return null;
+ }
+
+ private LookupResult resolvePhoneContact(final String handle, final String number) {
+ LookupResult lookupResult = null;
+ Cursor c = null;
+ try {
+ Uri numberUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
+ Uri.encode(number));
+ c = mContext.getContentResolver().query(numberUri, LOOKUP_PROJECTION, null, null, null);
+ if (c != null && c.getCount() > 0) {
+ c.moveToFirst();
+ final int idIdx = c.getColumnIndex(Contacts._ID);
+ final int id = c.getInt(idIdx);
+ if (DEBUG) Slog.d(TAG, "is valid: " + id);
+ lookupResult = new LookupResult(id);
+ }
+ } catch(Throwable t) {
+ Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ if (lookupResult == null) {
+ lookupResult = new LookupResult(LookupResult.INVALID_ID);
+ }
+ synchronized (mPeopleCache) {
+ mPeopleCache.put(handle, lookupResult);
+ }
+ return lookupResult;
+ }
+
+ private LookupResult resolveContactsUri(String handle, final Uri personUri) {
+ LookupResult lookupResult = null;
+ Cursor c = null;
+ try {
+ c = mContext.getContentResolver().query(personUri, LOOKUP_PROJECTION, null, null, null);
+ if (c != null && c.getCount() > 0) {
+ c.moveToFirst();
+ final int idIdx = c.getColumnIndex(Contacts._ID);
+ final int id = c.getInt(idIdx);
+ if (DEBUG) Slog.d(TAG, "is valid: " + id);
+ lookupResult = new LookupResult(id);
+ }
+ } catch(Throwable t) {
+ Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ if (lookupResult == null) {
+ lookupResult = new LookupResult(LookupResult.INVALID_ID);
+ }
+ synchronized (mPeopleCache) {
+ mPeopleCache.put(handle, lookupResult);
+ }
+ return lookupResult;
+ }
+
+ public void initialize(Context context) {
+ if (DEBUG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
+ mContext = context;
+ mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE);
+ mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt(
+ mContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1);
+ }
+
+ public RankingFuture process(NotificationManagerService.NotificationRecord record) {
+ if (!mEnabled) {
+ if (INFO) Slog.i(TAG, "disabled");
+ return null;
+ }
+ if (record == null || record.getNotification() == null) {
+ if (INFO) Slog.i(TAG, "skipping empty notification");
+ return null;
+ }
+ return validatePeople(record);
+ }
+
+ private static class LookupResult {
+ private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr
+ public static final int INVALID_ID = -1;
+
+ private final long mExpireMillis;
+ private int mId;
+
+ public LookupResult(int id) {
+ mId = id;
+ mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS;
+ }
+
+ public boolean isExpired() {
+ return mExpireMillis < System.currentTimeMillis();
+ }
+
+ public boolean isInvalid() {
+ return mId == INVALID_ID || isExpired();
+ }
+
+ public float getAffinity() {
+ if (isInvalid()) {
+ return NONE;
+ } else {
+ return VALID_CONTACT; // TODO: finer grained result: stars
+ }
+ }
+
+ public LookupResult setId(int id) {
+ mId = id;
+ return this;
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a7f4b28..d1333b2 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -621,7 +621,7 @@
void write(boolean force) {
if (force) {
- write();
+ writeInternal();
return;
}
if (SystemClock.elapsedRealtime() - mLastWritten.get() < WRITE_INTERVAL
@@ -633,7 +633,7 @@
@Override
public void run() {
try {
- write(true);
+ writeInternal();
} finally {
mBackgroundWriteRunning.set(false);
}
@@ -642,7 +642,7 @@
}
}
- private void write() {
+ private void writeInternal() {
synchronized (mPackages) {
synchronized (mFileLock) {
AtomicFile file = getFile();
@@ -4554,7 +4554,7 @@
if (updateUsage) {
p.mLastPackageUsageTimeInMills = System.currentTimeMillis();
}
- mPackageUsage.write();
+ mPackageUsage.write(false);
if (!p.mDexOptNeeded) {
return false;
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 47a8b2e..a5eccb3 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -168,9 +168,9 @@
// Poll interval in milliseconds for watching boot animation finished.
private static final int BOOT_ANIMATION_POLL_INTERVAL = 200;
- //powerHint
+ // Used to send the hint to the PowerHAL indicating transitions
+ // from and to the low power mode.
private static final int POWER_HINT_LOW_POWER_MODE = 5;
- private static boolean mLowPowerModeEnabled;
private final Context mContext;
private LightsManager mLightsManager;
@@ -399,6 +399,9 @@
// Time when we last logged a warning about calling userActivity() without permission.
private long mLastWarningAboutUserActivityPermission = Long.MIN_VALUE;
+ // If true, the device is in low power mode.
+ private static boolean mLowPowerModeEnabled;
+
private native void nativeInit();
private static native void nativeAcquireSuspendBlocker(String name);
@@ -617,12 +620,11 @@
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT);
- boolean mIsEnabled = Settings.Global.getInt(resolver,
- Settings.Global.LOW_POWER_MODE, 0) != 0;
- if (mIsEnabled != mLowPowerModeEnabled) {
- BinderService bs = new BinderService();
- bs.powerHint(POWER_HINT_LOW_POWER_MODE, mIsEnabled ? 1 : 0);
- mLowPowerModeEnabled = mIsEnabled;
+ boolean lowPowerModeEnabled = Settings.Global.getInt(resolver,
+ Settings.Global.LOW_POWER_MODE, 0) != 0;
+ if (lowPowerModeEnabled != mLowPowerModeEnabled) {
+ powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0);
+ mLowPowerModeEnabled = lowPowerModeEnabled;
}
mDirty |= DIRTY_SETTINGS;
@@ -2020,6 +2022,10 @@
}
}
+ private void powerHintInternal(int hintId, int data) {
+ nativeSendPowerHint(hintId, data);
+ }
+
/**
* Low-level function turn the device off immediately, without trying
* to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
@@ -2529,7 +2535,7 @@
@Override // Binder call
public void powerHint(int hintId, int data) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
- nativeSendPowerHint(hintId, data);
+ powerHintInternal(hintId, data);
}
@Override // Binder call
diff --git a/services/core/java/com/android/server/task/StateChangedListener.java b/services/core/java/com/android/server/task/StateChangedListener.java
new file mode 100644
index 0000000..a87bf95
--- /dev/null
+++ b/services/core/java/com/android/server/task/StateChangedListener.java
@@ -0,0 +1,40 @@
+/*
+ * 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.task;
+
+import com.android.server.task.controllers.TaskStatus;
+
+/**
+ * Interface through which a {@link StateController} informs the
+ * {@link com.android.server.task.TaskManagerService} that there are some tasks potentially ready
+ * to be run.
+ */
+public interface StateChangedListener {
+ /**
+ * Called by the controller to notify the TaskManager that it should check on the state of a
+ * task.
+ * @param taskStatus The state of the task which has changed.
+ */
+ public void onTaskStateChanged(TaskStatus taskStatus);
+
+ /**
+ * Called by the controller to notify the TaskManager that regardless of the state of the task,
+ * it must be run immediately.
+ * @param taskStatus The state of the task which is to be run immediately.
+ */
+ public void onTaskDeadlineExpired(TaskStatus taskStatus);
+}
diff --git a/services/core/java/com/android/server/task/TaskList.java b/services/core/java/com/android/server/task/TaskList.java
new file mode 100644
index 0000000..d2b8440
--- /dev/null
+++ b/services/core/java/com/android/server/task/TaskList.java
@@ -0,0 +1,78 @@
+/*
+ * 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.task;
+
+import android.content.ComponentName;
+import android.content.Task;
+
+import com.android.server.task.controllers.TaskStatus;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Maintain a list of classes, and accessor methods/logic for these tasks.
+ * This class offers the following functionality:
+ * - When a task is added, it will determine if the task requirements have changed (update) and
+ * whether the controllers need to be updated.
+ * - Persists Tasks, figures out when to to rewrite the Task to disk.
+ * - Is threadsafe.
+ * - Handles rescheduling of tasks.
+ * - When a periodic task is executed and must be re-added.
+ * - When a task fails and the client requests that it be retried with backoff.
+ */
+public class TaskList {
+
+ final List<TaskStatus> mTasks;
+
+ TaskList() {
+ mTasks = intialiseTaskMapFromDisk();
+ }
+
+ /**
+ * Add a task to the master list, persisting it if necessary.
+ * @param task Task to add.
+ * @param persistable true if the TaskQueue should persist this task to the disk.
+ * @return true if this operation was successful. If false, this task was neither added nor
+ * persisted.
+ */
+ // TODO: implement this when i decide whether i want to key by TaskStatus
+ public boolean add(Task task, boolean persistable) {
+ return true;
+ }
+
+ /**
+ * Remove the provided task. Will also delete the task if it was persisted. Note that this
+ * function does not return the validity of the operation, as we assume a delete will always
+ * succeed.
+ * @param task Task to remove.
+ */
+ public void remove(Task task) {
+
+ }
+
+ /**
+ *
+ * @return
+ */
+ // TODO: Implement this.
+ private List<TaskStatus> intialiseTaskMapFromDisk() {
+ return new ArrayList<TaskStatus>();
+ }
+}
diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java
new file mode 100644
index 0000000..5df4b2a
--- /dev/null
+++ b/services/core/java/com/android/server/task/TaskManagerService.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.task;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.SparseArray;
+
+import com.android.server.task.controllers.TaskStatus;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Responsible for taking tasks representing work to be performed by a client app, and determining
+ * based on the criteria specified when that task should be run against the client application's
+ * endpoint.
+ * @hide
+ */
+public class TaskManagerService extends com.android.server.SystemService
+ implements StateChangedListener {
+
+ /** Master list of tasks. */
+ private final TaskList mTaskList;
+
+ /**
+ * Track Services that have currently active or pending tasks. The index is provided by
+ * {@link TaskStatus#getServiceToken()}
+ */
+ private final SparseArray<TaskServiceContext> mPendingTaskServices =
+ new SparseArray<TaskServiceContext>();
+
+ private final TaskHandler mHandler;
+
+ private class TaskHandler extends Handler {
+ /** Check the pending queue and start any tasks. */
+ static final int MSG_RUN_PENDING = 0;
+ /** Initiate the stop task flow. */
+ static final int MSG_STOP_TASK = 1;
+
+ public TaskHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_RUN_PENDING:
+
+ break;
+ case MSG_STOP_TASK:
+
+ break;
+ }
+ }
+
+ /**
+ * Helper to post a message to this handler that will run through the pending queue and
+ * start any tasks it can.
+ */
+ void sendRunPendingTasksMessage() {
+ Message m = Message.obtain(this, MSG_RUN_PENDING);
+ m.sendToTarget();
+ }
+
+ void sendOnStopMessage(TaskStatus taskStatus) {
+
+ }
+ }
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public TaskManagerService(Context context) {
+ super(context);
+ mTaskList = new TaskList();
+ mHandler = new TaskHandler(context.getMainLooper());
+ }
+
+ @Override
+ public void onStart() {
+
+ }
+
+ /**
+ * Offboard work to our handler thread as quickly as possible, b/c this call is probably being
+ * made on the main thread.
+ * @param taskStatus The state of the task which has changed.
+ */
+ @Override
+ public void onTaskStateChanged(TaskStatus taskStatus) {
+ if (taskStatus.isReady()) {
+
+ } else {
+ if (mPendingTaskServices.get(taskStatus.getServiceToken()) != null) {
+ // The task is either pending or being executed, which we have to cancel.
+ }
+ }
+
+ }
+
+ @Override
+ public void onTaskDeadlineExpired(TaskStatus taskStatus) {
+
+ }
+}
diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/task/TaskServiceContext.java
new file mode 100644
index 0000000..65c6fa5
--- /dev/null
+++ b/services/core/java/com/android/server/task/TaskServiceContext.java
@@ -0,0 +1,94 @@
+/*
+ * 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.task;
+
+import android.app.task.ITaskCallback;
+import android.app.task.ITaskService;
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.content.Task;
+import android.os.IBinder;
+
+import com.android.server.task.controllers.TaskStatus;
+
+/**
+ * Maintains information required to bind to a {@link android.app.task.TaskService}. This binding
+ * can then be reused to start concurrent tasks on the TaskService. Information here is unique
+ * within this service.
+ * Functionality provided by this class:
+ * - Managages wakelock for the service.
+ * - Sends onStartTask() and onStopTask() messages to client app, and handles callbacks.
+ * -
+ */
+public class TaskServiceContext extends ITaskCallback.Stub implements ServiceConnection {
+
+ final ComponentName component;
+ int uid;
+ ITaskService service;
+
+ /** Whether this service is actively bound. */
+ boolean mBound;
+
+ TaskServiceContext(Task task) {
+ this.component = task.getService();
+ }
+
+ public void stopTask() {
+
+ }
+
+ public void startTask(Task task) {
+
+ }
+
+ @Override
+ public void taskFinished(int taskId, boolean reschedule) {
+
+ }
+
+ @Override
+ public void acknowledgeStopMessage(int taskId) {
+
+ }
+
+ @Override
+ public void acknowledgeStartMessage(int taskId) {
+
+ }
+
+ /**
+ * @return true if this task is pending or active within this context.
+ */
+ public boolean hasTaskPending(TaskStatus taskStatus) {
+ return true;
+ }
+
+ public boolean isBound() {
+ return mBound;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+
+ mBound = true;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mBound = false;
+ }
+}
diff --git a/services/core/java/com/android/server/task/controllers/ConnectivityController.java b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
new file mode 100644
index 0000000..5cca77c
--- /dev/null
+++ b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
@@ -0,0 +1,119 @@
+/*
+ * 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.task.controllers;
+
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.server.task.TaskManagerService;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ */
+public class ConnectivityController extends StateController {
+ private static final String TAG = "TaskManager.Connectivity";
+
+ private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>();
+ private final BroadcastReceiver mConnectivityChangedReceiver =
+ new ConnectivityChangedReceiver();
+
+ public ConnectivityController(TaskManagerService service) {
+ super(service);
+ // Register connectivity changed BR.
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ mContext.registerReceiverAsUser(
+ mConnectivityChangedReceiver, UserHandle.ALL, intentFilter, null, null);
+ }
+
+ @Override
+ public void maybeTrackTaskState(TaskStatus taskStatus) {
+ if (taskStatus.hasConnectivityConstraint() || taskStatus.hasMeteredConstraint()) {
+ mTrackedTasks.add(taskStatus);
+ }
+ }
+
+ @Override
+ public void removeTaskStateIfTracked(TaskStatus taskStatus) {
+ mTrackedTasks.remove(taskStatus);
+ }
+
+ /**
+ * @param isConnected Whether the active network is connected for the given uid
+ * @param isMetered Whether the active network is metered for the given uid. This is
+ * necessarily false if <code>isConnected</code> is false.
+ * @param userId Id of the user for whom we are updating the connectivity state.
+ */
+ private void updateTrackedTasks(boolean isConnected, boolean isMetered, int userId) {
+ for (TaskStatus ts : mTrackedTasks) {
+ if (ts.userId != userId) {
+ continue;
+ }
+ boolean prevIsConnected = ts.connectivityConstraintSatisfied.getAndSet(isConnected);
+ boolean prevIsMetered = ts.meteredConstraintSatisfied.getAndSet(isMetered);
+ if (prevIsConnected != isConnected || prevIsMetered != isMetered) {
+ mStateChangedListener.onTaskStateChanged(ts);
+ }
+ }
+ }
+
+ class ConnectivityChangedReceiver extends BroadcastReceiver {
+ /**
+ * We'll receive connectivity changes for each user here, which we'll process independently.
+ * We are only interested in the active network here. We're only interested in the active
+ * network, b/c the end result of this will be for apps to try to hit the network.
+ * @param context The Context in which the receiver is running.
+ * @param intent The Intent being received.
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ final int networkType =
+ intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
+ ConnectivityManager.TYPE_NONE);
+ // Connectivity manager for THIS context - important!
+ final ConnectivityManager connManager = (ConnectivityManager)
+ context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo();
+ // This broadcast gets sent a lot, only update if the active network has changed.
+ if (activeNetwork.getType() == networkType) {
+ final int userid = context.getUserId();
+ boolean isMetered = false;
+ boolean isConnected =
+ !intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+ if (isConnected) { // No point making the call if we know there's no conn.
+ isMetered = connManager.isActiveNetworkMetered();
+ }
+ updateTrackedTasks(isConnected, isMetered, userid);
+ }
+ } else {
+ Log.w(TAG, "Unrecognised action in intent: " + action);
+ }
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/task/controllers/StateController.java b/services/core/java/com/android/server/task/controllers/StateController.java
new file mode 100644
index 0000000..e1cd662
--- /dev/null
+++ b/services/core/java/com/android/server/task/controllers/StateController.java
@@ -0,0 +1,51 @@
+/*
+ * 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.task.controllers;
+
+import android.content.Context;
+
+import com.android.server.task.StateChangedListener;
+import com.android.server.task.TaskManagerService;
+
+/**
+ * Incorporates shared controller logic between the various controllers of the TaskManager.
+ * These are solely responsible for tracking a list of tasks, and notifying the TM when these
+ * are ready to run, or whether they must be stopped.
+ */
+public abstract class StateController {
+
+ protected Context mContext;
+ protected StateChangedListener mStateChangedListener;
+
+ public StateController(TaskManagerService service) {
+ mStateChangedListener = service;
+ mContext = service.getContext();
+ }
+
+ /**
+ * Implement the logic here to decide whether a task should be tracked by this controller.
+ * This logic is put here so the TaskManger can be completely agnostic of Controller logic.
+ * Also called when updating a task, so implementing controllers have to be aware of
+ * preexisting tasks.
+ */
+ public abstract void maybeTrackTaskState(TaskStatus taskStatus);
+ /**
+ * Remove task - this will happen if the task is cancelled, completed, etc.
+ */
+ public abstract void removeTaskStateIfTracked(TaskStatus taskStatus);
+
+}
diff --git a/services/core/java/com/android/server/task/controllers/TaskStatus.java b/services/core/java/com/android/server/task/controllers/TaskStatus.java
new file mode 100644
index 0000000..230b049
--- /dev/null
+++ b/services/core/java/com/android/server/task/controllers/TaskStatus.java
@@ -0,0 +1,154 @@
+/*
+ * 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.task.controllers;
+
+import android.content.ComponentName;
+import android.content.Task;
+import android.os.SystemClock;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Uniquely identifies a task internally.
+ * Created from the public {@link android.content.Task} object when it lands on the scheduler.
+ * Contains current state of the requirements of the task, as well as a function to evaluate
+ * whether it's ready to run.
+ * This object is shared among the various controllers - hence why the different fields are atomic.
+ * This isn't strictly necessary because each controller is only interested in a specific field,
+ * and the receivers that are listening for global state change will all run on the main looper,
+ * but we don't enforce that so this is safer.
+ * @hide
+ */
+public class TaskStatus {
+ final int taskId;
+ final int userId;
+ ComponentName component;
+
+ final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
+ final AtomicBoolean timeConstraintSatisfied = new AtomicBoolean();
+ final AtomicBoolean idleConstraintSatisfied = new AtomicBoolean();
+ final AtomicBoolean meteredConstraintSatisfied = new AtomicBoolean();
+ final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean();
+
+ private final boolean hasChargingConstraint;
+ private final boolean hasTimingConstraint;
+ private final boolean hasIdleConstraint;
+ private final boolean hasMeteredConstraint;
+ private final boolean hasConnectivityConstraint;
+
+ private long earliestRunTimeElapsedMillis;
+ private long latestRunTimeElapsedMillis;
+
+ /** Provide a unique handle to the service that this task will be run on. */
+ public int getServiceToken() {
+ return component.hashCode() + userId;
+ }
+
+ /** Generate a TaskStatus object for a given task and uid. */
+ // TODO: reimplement this to reuse these objects instead of creating a new one each time?
+ static TaskStatus getForTaskAndUid(Task task, int uId) {
+ return new TaskStatus(task, uId);
+ }
+
+ /** Set up the state of a newly scheduled task. */
+ TaskStatus(Task task, int userId) {
+ this.taskId = task.getTaskId();
+ this.userId = userId;
+ this.component = task.getService();
+
+ hasChargingConstraint = task.isRequireCharging();
+ hasIdleConstraint = task.isRequireDeviceIdle();
+
+ // Timing constraints
+ if (task.isPeriodic()) {
+ long elapsedNow = SystemClock.elapsedRealtime();
+ earliestRunTimeElapsedMillis = elapsedNow;
+ latestRunTimeElapsedMillis = elapsedNow + task.getIntervalMillis();
+ hasTimingConstraint = true;
+ } else if (task.getMinLatencyMillis() != 0L || task.getMaxExecutionDelayMillis() != 0L) {
+ earliestRunTimeElapsedMillis = task.getMinLatencyMillis() > 0L ?
+ task.getMinLatencyMillis() : Long.MAX_VALUE;
+ latestRunTimeElapsedMillis = task.getMaxExecutionDelayMillis() > 0L ?
+ task.getMaxExecutionDelayMillis() : Long.MAX_VALUE;
+ hasTimingConstraint = true;
+ } else {
+ hasTimingConstraint = false;
+ }
+
+ // Networking constraints
+ hasMeteredConstraint = task.getNetworkCapabilities() == Task.NetworkType.UNMETERED;
+ hasConnectivityConstraint = task.getNetworkCapabilities() == Task.NetworkType.ANY;
+ }
+
+ boolean hasConnectivityConstraint() {
+ return hasConnectivityConstraint;
+ }
+
+ boolean hasMeteredConstraint() {
+ return hasMeteredConstraint;
+ }
+
+ boolean hasChargingConstraint() {
+ return hasChargingConstraint;
+ }
+
+ boolean hasTimingConstraint() {
+ return hasTimingConstraint;
+ }
+
+ boolean hasIdleConstraint() {
+ return hasIdleConstraint;
+ }
+
+ long getEarliestRunTime() {
+ return earliestRunTimeElapsedMillis;
+ }
+
+ long getLatestRunTime() {
+ return latestRunTimeElapsedMillis;
+ }
+
+ /**
+ * @return whether this task is ready to run, based on its requirements.
+ */
+ public synchronized boolean isReady() {
+ return (!hasChargingConstraint || chargingConstraintSatisfied.get())
+ && (!hasTimingConstraint || timeConstraintSatisfied.get())
+ && (!hasConnectivityConstraint || connectivityConstraintSatisfied.get())
+ && (!hasMeteredConstraint || meteredConstraintSatisfied.get())
+ && (!hasIdleConstraint || idleConstraintSatisfied.get());
+ }
+
+ @Override
+ public int hashCode() {
+ int result = component.hashCode();
+ result = 31 * result + taskId;
+ result = 31 * result + userId;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof TaskStatus)) return false;
+
+ TaskStatus that = (TaskStatus) o;
+ return ((taskId == that.taskId)
+ && (userId == that.userId)
+ && (component.equals(that.component)));
+ }
+}
diff --git a/services/core/java/com/android/server/task/controllers/TimeController.java b/services/core/java/com/android/server/task/controllers/TimeController.java
new file mode 100644
index 0000000..6d97a53
--- /dev/null
+++ b/services/core/java/com/android/server/task/controllers/TimeController.java
@@ -0,0 +1,230 @@
+/*
+ * 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.task.controllers;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.server.task.TaskManagerService;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * This class sets an alarm for the next expiring task, and determines whether a task's minimum
+ * delay has been satisfied.
+ */
+public class TimeController extends StateController {
+ private static final String TAG = "TaskManager.Time";
+ private static final String ACTION_TASK_EXPIRED =
+ "android.content.taskmanager.TASK_EXPIRED";
+ private static final String ACTION_TASK_DELAY_EXPIRED =
+ "android.content.taskmanager.TASK_DELAY_EXPIRED";
+
+ /** Set an alarm for the next task expiry. */
+ private final PendingIntent mTaskExpiredAlarmIntent;
+ /** Set an alarm for the next task delay expiry. This*/
+ private final PendingIntent mNextDelayExpiredAlarmIntent;
+
+ private long mNextTaskExpiredElapsedMillis;
+ private long mNextDelayExpiredElapsedMillis;
+
+ private AlarmManager mAlarmService = null;
+ /** List of tracked tasks, sorted asc. by deadline */
+ private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>();
+
+ public TimeController(TaskManagerService service) {
+ super(service);
+ mTaskExpiredAlarmIntent =
+ PendingIntent.getBroadcast(mContext, 0 /* ignored */,
+ new Intent(ACTION_TASK_EXPIRED), 0);
+ mNextDelayExpiredAlarmIntent =
+ PendingIntent.getBroadcast(mContext, 0 /* ignored */,
+ new Intent(ACTION_TASK_DELAY_EXPIRED), 0);
+
+ // Register BR for these intents.
+ IntentFilter intentFilter = new IntentFilter(ACTION_TASK_EXPIRED);
+ intentFilter.addAction(ACTION_TASK_DELAY_EXPIRED);
+ mContext.registerReceiver(mAlarmExpiredReceiver, intentFilter);
+ }
+
+ /**
+ * Check if the task has a timing constraint, and if so determine where to insert it in our
+ * list.
+ */
+ @Override
+ public synchronized void maybeTrackTaskState(TaskStatus task) {
+ if (task.hasTimingConstraint()) {
+ ListIterator<TaskStatus> it = mTrackedTasks.listIterator(mTrackedTasks.size());
+ while (it.hasPrevious()) {
+ TaskStatus ts = it.previous();
+ if (ts.equals(task)) {
+ // Update
+ it.remove();
+ it.add(task);
+ break;
+ } else if (ts.getLatestRunTime() < task.getLatestRunTime()) {
+ // Insert
+ it.add(task);
+ break;
+ }
+ }
+ maybeUpdateAlarms(task.getEarliestRunTime(), task.getLatestRunTime());
+ }
+ }
+
+ /**
+ * If the task passed in is being tracked, figure out if we need to update our alarms, and if
+ * so, update them.
+ */
+ @Override
+ public synchronized void removeTaskStateIfTracked(TaskStatus taskStatus) {
+ if (mTrackedTasks.remove(taskStatus)) {
+ if (mNextDelayExpiredElapsedMillis <= taskStatus.getEarliestRunTime()) {
+ handleTaskDelayExpired();
+ }
+ if (mNextTaskExpiredElapsedMillis <= taskStatus.getLatestRunTime()) {
+ handleTaskDeadlineExpired();
+ }
+ }
+ }
+
+ /**
+ * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a task's
+ * delay will expire.
+ * This alarm <b>will not</b> wake up the phone.
+ */
+ private void setDelayExpiredAlarm(long alarmTimeElapsedMillis) {
+ ensureAlarmService();
+ mAlarmService.set(AlarmManager.ELAPSED_REALTIME, alarmTimeElapsedMillis,
+ mNextDelayExpiredAlarmIntent);
+ }
+
+ /**
+ * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a task's
+ * deadline will expire.
+ * This alarm <b>will</b> wake up the phone.
+ */
+ private void setDeadlineExpiredAlarm(long alarmTimeElapsedMillis) {
+ ensureAlarmService();
+ mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTimeElapsedMillis,
+ mTaskExpiredAlarmIntent);
+ }
+
+ /**
+ * Determines whether this controller can stop tracking the given task.
+ * The controller is no longer interested in a task once its time constraint is satisfied, and
+ * the task's deadline is fulfilled - unlike other controllers a time constraint can't toggle
+ * back and forth.
+ */
+ private boolean canStopTrackingTask(TaskStatus taskStatus) {
+ final long elapsedNowMillis = SystemClock.elapsedRealtime();
+ return taskStatus.timeConstraintSatisfied.get() &&
+ (taskStatus.getLatestRunTime() == Long.MAX_VALUE ||
+ taskStatus.getLatestRunTime() < elapsedNowMillis);
+ }
+
+ private void maybeUpdateAlarms(long delayExpiredElapsed, long deadlineExpiredElapsed) {
+ if (delayExpiredElapsed < mNextDelayExpiredElapsedMillis) {
+ mNextDelayExpiredElapsedMillis = delayExpiredElapsed;
+ setDelayExpiredAlarm(mNextDelayExpiredElapsedMillis);
+ }
+ if (deadlineExpiredElapsed < mNextTaskExpiredElapsedMillis) {
+ mNextTaskExpiredElapsedMillis = deadlineExpiredElapsed;
+ setDeadlineExpiredAlarm(mNextTaskExpiredElapsedMillis);
+ }
+ }
+
+ private void ensureAlarmService() {
+ if (mAlarmService == null) {
+ mAlarmService = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ }
+ }
+
+ /**
+ * Handles alarm that notifies that a task has expired. When this function is called at least
+ * one task must be run.
+ */
+ private synchronized void handleTaskDeadlineExpired() {
+ long nextExpiryTime = Long.MAX_VALUE;
+ final long nowElapsedMillis = SystemClock.elapsedRealtime();
+
+ Iterator<TaskStatus> it = mTrackedTasks.iterator();
+ while (it.hasNext()) {
+ TaskStatus ts = it.next();
+ final long taskDeadline = ts.getLatestRunTime();
+
+ if (taskDeadline <= nowElapsedMillis) {
+ ts.timeConstraintSatisfied.set(true);
+ mStateChangedListener.onTaskDeadlineExpired(ts);
+ it.remove();
+ } else { // Sorted by expiry time, so take the next one and stop.
+ nextExpiryTime = taskDeadline;
+ break;
+ }
+ }
+ maybeUpdateAlarms(Long.MAX_VALUE, nextExpiryTime);
+ }
+
+ /**
+ * Handles alarm that notifies us that a task's delay has expired. Iterates through the list of
+ * tracked tasks and marks them as ready as appropriate.
+ */
+ private synchronized void handleTaskDelayExpired() {
+ final long nowElapsedMillis = SystemClock.elapsedRealtime();
+ long nextDelayTime = Long.MAX_VALUE;
+
+ Iterator<TaskStatus> it = mTrackedTasks.iterator();
+ while (it.hasNext()) {
+ final TaskStatus ts = it.next();
+ final long taskDelayTime = ts.getEarliestRunTime();
+ if (taskDelayTime < nowElapsedMillis) {
+ ts.timeConstraintSatisfied.set(true);
+ mStateChangedListener.onTaskStateChanged(ts);
+ if (canStopTrackingTask(ts)) {
+ it.remove();
+ }
+ } else { // Keep going through list to get next delay time.
+ if (nextDelayTime > taskDelayTime) {
+ nextDelayTime = taskDelayTime;
+ }
+ }
+ }
+ maybeUpdateAlarms(nextDelayTime, Long.MAX_VALUE);
+ }
+
+ private final BroadcastReceiver mAlarmExpiredReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // An task has just expired, so we run through the list of tasks that we have and
+ // notify our StateChangedListener.
+ if (ACTION_TASK_EXPIRED.equals(intent.getAction())) {
+ handleTaskDeadlineExpired();
+ } else if (ACTION_TASK_DELAY_EXPIRED.equals(intent.getAction())) {
+ handleTaskDelayExpired();
+ }
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 50dd27d..05f9947 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -134,7 +134,7 @@
private void buildTvInputListLocked(int userId) {
UserState userState = getUserStateLocked(userId);
- userState.inputList.clear();
+ userState.inputMap.clear();
if (DEBUG) Slog.d(TAG, "buildTvInputList");
PackageManager pm = mContext.getPackageManager();
@@ -149,7 +149,7 @@
}
TvInputInfo info = new TvInputInfo(ri);
if (DEBUG) Slog.d(TAG, "add " + info.getId());
- userState.inputList.add(info);
+ userState.inputMap.put(info.getId(), info);
}
}
@@ -179,9 +179,9 @@
}
// Release created sessions.
for (SessionState state : userState.sessionStateMap.values()) {
- if (state.session != null) {
+ if (state.mSession != null) {
try {
- state.session.release();
+ state.mSession.release();
} catch (RemoteException e) {
Slog.e(TAG, "error in release", e);
}
@@ -191,15 +191,15 @@
// Unregister all callbacks and unbind all services.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
- if (serviceState.callback != null) {
+ if (serviceState.mCallback != null) {
try {
- serviceState.service.unregisterCallback(serviceState.callback);
+ serviceState.mService.unregisterCallback(serviceState.mCallback);
} catch (RemoteException e) {
Slog.e(TAG, "error in unregisterCallback", e);
}
}
- serviceState.clients.clear();
- mContext.unbindService(serviceState.connection);
+ serviceState.mClients.clear();
+ mContext.unbindService(serviceState.mConnection);
}
userState.serviceStateMap.clear();
@@ -215,28 +215,33 @@
return userState;
}
- private ServiceState getServiceStateLocked(ComponentName name, int userId) {
+ private ServiceState getServiceStateLocked(String inputId, int userId) {
UserState userState = getUserStateLocked(userId);
- ServiceState serviceState = userState.serviceStateMap.get(name);
+ ServiceState serviceState = userState.serviceStateMap.get(inputId);
if (serviceState == null) {
- throw new IllegalStateException("Service state not found for " + name + " (userId="
+ throw new IllegalStateException("Service state not found for " + inputId + " (userId="
+ userId + ")");
}
return serviceState;
}
- private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
+ private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
UserState userState = getUserStateLocked(userId);
SessionState sessionState = userState.sessionStateMap.get(sessionToken);
if (sessionState == null) {
throw new IllegalArgumentException("Session state not found for token " + sessionToken);
}
// Only the application that requested this session or the system can access it.
- if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) {
+ if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) {
throw new SecurityException("Illegal access to the session with token " + sessionToken
+ " from uid " + callingUid);
}
- ITvInputSession session = sessionState.session;
+ return sessionState;
+ }
+
+ private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
+ ITvInputSession session = sessionState.mSession;
if (session == null) {
throw new IllegalStateException("Session not yet created for token " + sessionToken);
}
@@ -249,38 +254,47 @@
false, methodName, null);
}
- private void updateServiceConnectionLocked(ComponentName name, int userId) {
+ private void updateServiceConnectionLocked(String inputId, int userId) {
UserState userState = getUserStateLocked(userId);
- ServiceState serviceState = userState.serviceStateMap.get(name);
+ ServiceState serviceState = userState.serviceStateMap.get(inputId);
if (serviceState == null) {
return;
}
- boolean isStateEmpty = serviceState.clients.isEmpty()
- && serviceState.sessionTokens.isEmpty();
- if (serviceState.service == null && !isStateEmpty && userId == mCurrentUserId) {
+ if (serviceState.mReconnecting) {
+ if (!serviceState.mSessionTokens.isEmpty()) {
+ // wait until all the sessions are removed.
+ return;
+ }
+ serviceState.mReconnecting = false;
+ }
+ boolean isStateEmpty = serviceState.mClients.isEmpty()
+ && serviceState.mSessionTokens.isEmpty();
+ if (serviceState.mService == null && !isStateEmpty && userId == mCurrentUserId) {
// This means that the service is not yet connected but its state indicates that we
// have pending requests. Then, connect the service.
- if (serviceState.bound) {
+ if (serviceState.mBound) {
// We have already bound to the service so we don't try to bind again until after we
// unbind later on.
return;
}
if (DEBUG) {
- Slog.d(TAG, "bindServiceAsUser(name=" + name.getClassName() + ", userId=" + userId
+ Slog.d(TAG, "bindServiceAsUser(inputId=" + inputId + ", userId=" + userId
+ ")");
}
- Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(name);
- mContext.bindServiceAsUser(i, serviceState.connection, Context.BIND_AUTO_CREATE,
+
+ Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(
+ userState.inputMap.get(inputId).getComponent());
+ mContext.bindServiceAsUser(i, serviceState.mConnection, Context.BIND_AUTO_CREATE,
new UserHandle(userId));
- serviceState.bound = true;
- } else if (serviceState.service != null && isStateEmpty) {
+ serviceState.mBound = true;
+ } else if (serviceState.mService != null && isStateEmpty) {
// 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) {
- Slog.d(TAG, "unbindService(name=" + name.getClassName() + ")");
+ Slog.d(TAG, "unbindService(inputId=" + inputId + ")");
}
- mContext.unbindService(serviceState.connection);
- userState.serviceStateMap.remove(name);
+ mContext.unbindService(serviceState.mConnection);
+ userState.serviceStateMap.remove(inputId);
}
}
@@ -289,8 +303,7 @@
final SessionState sessionState =
getUserStateLocked(userId).sessionStateMap.get(sessionToken);
if (DEBUG) {
- Slog.d(TAG, "createSessionInternalLocked(name=" + sessionState.name.getClassName()
- + ")");
+ Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInputId + ")");
}
final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
@@ -300,17 +313,22 @@
@Override
public void onSessionCreated(ITvInputSession session) {
if (DEBUG) {
- Slog.d(TAG, "onSessionCreated(name=" + sessionState.name.getClassName() + ")");
+ Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInputId + ")");
}
synchronized (mLock) {
- sessionState.session = session;
+ sessionState.mSession = session;
if (session == null) {
removeSessionStateLocked(sessionToken, userId);
- sendSessionTokenToClientLocked(sessionState.client, sessionState.name, null,
- null, sessionState.seq, userId);
+ sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
+ null, null, sessionState.mSeq, userId);
} else {
- sendSessionTokenToClientLocked(sessionState.client, sessionState.name,
- sessionToken, channels[0], sessionState.seq, userId);
+ try {
+ session.asBinder().linkToDeath(sessionState, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Session is already died.");
+ }
+ sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
+ sessionToken, channels[0], sessionState.mSeq, userId);
}
channels[0].dispose();
}
@@ -323,24 +341,32 @@
} catch (RemoteException e) {
Slog.e(TAG, "error in createSession", e);
removeSessionStateLocked(sessionToken, userId);
- sendSessionTokenToClientLocked(sessionState.client, sessionState.name, null, null,
- sessionState.seq, userId);
+ sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, null, null,
+ sessionState.mSeq, userId);
}
channels[1].dispose();
}
- private void sendSessionTokenToClientLocked(ITvInputClient client, ComponentName name,
+ private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
IBinder sessionToken, InputChannel channel, int seq, int userId) {
try {
- client.onSessionCreated(name, sessionToken, channel, seq);
+ client.onSessionCreated(inputId, sessionToken, channel, seq);
} catch (RemoteException exception) {
Slog.e(TAG, "error in onSessionCreated", exception);
}
+ }
- if (sessionToken == null) {
- // This means that the session creation failed. We might want to disconnect the service.
- updateServiceConnectionLocked(name, userId);
+ private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
+ if (sessionState.mSession != null) {
+ try {
+ sessionState.mSession.release();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "session is already disapeared", e);
+ }
+ sessionState.mSession = null;
}
+ removeSessionStateLocked(sessionToken, userId);
}
private void removeSessionStateLocked(IBinder sessionToken, int userId) {
@@ -349,19 +375,31 @@
SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
// Close the open log entry, if any.
- if (sessionState.logUri != null) {
+ if (sessionState.mLogUri != null) {
SomeArgs args = SomeArgs.obtain();
- args.arg1 = sessionState.logUri;
+ args.arg1 = sessionState.mLogUri;
args.arg2 = System.currentTimeMillis();
mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget();
}
// Also remove the session token from the session token list of the current service.
- ServiceState serviceState = userState.serviceStateMap.get(sessionState.name);
+ ServiceState serviceState = userState.serviceStateMap.get(sessionState.mInputId);
if (serviceState != null) {
- serviceState.sessionTokens.remove(sessionToken);
+ serviceState.mSessionTokens.remove(sessionToken);
}
- updateServiceConnectionLocked(sessionState.name, userId);
+ updateServiceConnectionLocked(sessionState.mInputId, userId);
+ }
+
+ private void broadcastServiceAvailabilityChangedLocked(ServiceState serviceState) {
+ for (IBinder iBinder : serviceState.mClients) {
+ ITvInputClient client = ITvInputClient.Stub.asInterface(iBinder);
+ try {
+ client.onAvailabilityChanged(
+ serviceState.mTvInputInfo.getId(), serviceState.mAvailable);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onAvailabilityChanged", e);
+ }
+ }
}
private final class BinderService extends ITvInputManager.Stub {
@@ -373,7 +411,7 @@
try {
synchronized (mLock) {
UserState userState = getUserStateLocked(resolvedUserId);
- return new ArrayList<TvInputInfo>(userState.inputList);
+ return new ArrayList<TvInputInfo>(userState.inputMap.values());
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -381,7 +419,7 @@
}
@Override
- public boolean getAvailability(final ITvInputClient client, final ComponentName name,
+ public boolean getAvailability(final ITvInputClient client, final String inputId,
int userId) {
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
Binder.getCallingUid(), userId, "getAvailability");
@@ -389,11 +427,11 @@
try {
synchronized (mLock) {
UserState userState = getUserStateLocked(resolvedUserId);
- ServiceState serviceState = userState.serviceStateMap.get(name);
+ ServiceState serviceState = userState.serviceStateMap.get(inputId);
if (serviceState != null) {
// We already know the status of this input service. Return the cached
// status.
- return serviceState.available;
+ return serviceState.mAvailable;
}
}
} finally {
@@ -403,7 +441,7 @@
}
@Override
- public void registerCallback(final ITvInputClient client, final ComponentName name,
+ public void registerCallback(final ITvInputClient client, final String inputId,
int userId) {
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
Binder.getCallingUid(), userId, "registerCallback");
@@ -413,28 +451,29 @@
// Create a new service callback and add it to the callback map of the current
// service.
UserState userState = getUserStateLocked(resolvedUserId);
- ServiceState serviceState = userState.serviceStateMap.get(name);
+ ServiceState serviceState = userState.serviceStateMap.get(inputId);
if (serviceState == null) {
- serviceState = new ServiceState(resolvedUserId);
- userState.serviceStateMap.put(name, serviceState);
+ serviceState = new ServiceState(
+ userState.inputMap.get(inputId), resolvedUserId);
+ userState.serviceStateMap.put(inputId, serviceState);
}
IBinder iBinder = client.asBinder();
- if (!serviceState.clients.contains(iBinder)) {
- serviceState.clients.add(iBinder);
+ if (!serviceState.mClients.contains(iBinder)) {
+ serviceState.mClients.add(iBinder);
}
- if (serviceState.service != null) {
- if (serviceState.callback != null) {
+ if (serviceState.mService != null) {
+ if (serviceState.mCallback != null) {
// We already handled.
return;
}
- serviceState.callback = new ServiceCallback(resolvedUserId);
+ serviceState.mCallback = new ServiceCallback(resolvedUserId);
try {
- serviceState.service.registerCallback(serviceState.callback);
+ serviceState.mService.registerCallback(serviceState.mCallback);
} catch (RemoteException e) {
Slog.e(TAG, "error in registerCallback", e);
}
} else {
- updateServiceConnectionLocked(name, resolvedUserId);
+ updateServiceConnectionLocked(inputId, resolvedUserId);
}
}
} finally {
@@ -443,34 +482,34 @@
}
@Override
- public void unregisterCallback(ITvInputClient client, ComponentName name, int userId) {
+ public void unregisterCallback(ITvInputClient client, String inputId, int userId) {
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
Binder.getCallingUid(), userId, "unregisterCallback");
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
UserState userState = getUserStateLocked(resolvedUserId);
- ServiceState serviceState = userState.serviceStateMap.get(name);
+ ServiceState serviceState = userState.serviceStateMap.get(inputId);
if (serviceState == null) {
return;
}
// Remove this client from the client list and unregister the callback.
- serviceState.clients.remove(client.asBinder());
- if (!serviceState.clients.isEmpty()) {
+ serviceState.mClients.remove(client.asBinder());
+ if (!serviceState.mClients.isEmpty()) {
// We have other clients who want to keep the callback. Do this later.
return;
}
- if (serviceState.service == null || serviceState.callback == null) {
+ if (serviceState.mService == null || serviceState.mCallback == null) {
return;
}
try {
- serviceState.service.unregisterCallback(serviceState.callback);
+ serviceState.mService.unregisterCallback(serviceState.mCallback);
} catch (RemoteException e) {
Slog.e(TAG, "error in unregisterCallback", e);
} finally {
- serviceState.callback = null;
- updateServiceConnectionLocked(name, resolvedUserId);
+ serviceState.mCallback = null;
+ updateServiceConnectionLocked(inputId, resolvedUserId);
}
}
} finally {
@@ -479,7 +518,7 @@
}
@Override
- public void createSession(final ITvInputClient client, final ComponentName name,
+ public void createSession(final ITvInputClient client, final String inputId,
int seq, int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
@@ -487,28 +526,35 @@
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
+ UserState userState = getUserStateLocked(resolvedUserId);
+ ServiceState serviceState = userState.serviceStateMap.get(inputId);
+ if (serviceState == null) {
+ serviceState = new ServiceState(
+ userState.inputMap.get(inputId), resolvedUserId);
+ userState.serviceStateMap.put(inputId, serviceState);
+ }
+ // Send a null token immediately while reconnecting.
+ if (serviceState.mReconnecting == true) {
+ sendSessionTokenToClientLocked(client, inputId, null, null, seq, userId);
+ return;
+ }
+
// Create a new session token and a session state.
IBinder sessionToken = new Binder();
- SessionState sessionState = new SessionState(name, client, seq, callingUid);
- sessionState.session = null;
+ SessionState sessionState = new SessionState(
+ sessionToken, inputId, client, seq, callingUid, resolvedUserId);
// Add them to the global session state map of the current user.
- UserState userState = getUserStateLocked(resolvedUserId);
userState.sessionStateMap.put(sessionToken, sessionState);
// Also, add them to the session state map of the current service.
- ServiceState serviceState = userState.serviceStateMap.get(name);
- if (serviceState == null) {
- serviceState = new ServiceState(resolvedUserId);
- userState.serviceStateMap.put(name, serviceState);
- }
- serviceState.sessionTokens.add(sessionToken);
+ serviceState.mSessionTokens.add(sessionToken);
- if (serviceState.service != null) {
- createSessionInternalLocked(serviceState.service, sessionToken,
+ if (serviceState.mService != null) {
+ createSessionInternalLocked(serviceState.mService, sessionToken,
resolvedUserId);
} else {
- updateServiceConnectionLocked(name, resolvedUserId);
+ updateServiceConnectionLocked(inputId, resolvedUserId);
}
}
} finally {
@@ -524,14 +570,7 @@
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- // Release the session.
- try {
- getSessionLocked(sessionToken, callingUid, resolvedUserId).release();
- } catch (RemoteException e) {
- Slog.e(TAG, "error in release", e);
- }
-
- removeSessionStateLocked(sessionToken, resolvedUserId);
+ releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -599,9 +638,9 @@
// Close the open log entry first, if any.
UserState userState = getUserStateLocked(resolvedUserId);
SessionState sessionState = userState.sessionStateMap.get(sessionToken);
- if (sessionState.logUri != null) {
+ if (sessionState.mLogUri != null) {
SomeArgs args = SomeArgs.obtain();
- args.arg1 = sessionState.logUri;
+ args.arg1 = sessionState.mLogUri;
args.arg2 = currentTime;
mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args)
.sendToTarget();
@@ -614,10 +653,10 @@
values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, 0);
values.put(TvContract.WatchedPrograms.CHANNEL_ID, channelId);
- sessionState.logUri = mContentResolver.insert(
+ sessionState.mLogUri = mContentResolver.insert(
TvContract.WatchedPrograms.CONTENT_URI, values);
SomeArgs args = SomeArgs.obtain();
- args.arg1 = sessionState.logUri;
+ args.arg1 = sessionState.mLogUri;
args.arg2 = ContentUris.parseId(channelUri);
args.arg3 = currentTime;
mLogHandler.obtainMessage(LogHandler.MSG_OPEN_ENTRY, args).sendToTarget();
@@ -694,12 +733,12 @@
}
private static final class UserState {
- // A list of all known TV inputs on the system.
- private final List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
+ // A mapping from the TV input id to its TvInputInfo.
+ private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>();
// A mapping from the name of a TV input service to its state.
- private final Map<ComponentName, ServiceState> serviceStateMap =
- new HashMap<ComponentName, ServiceState>();
+ private final Map<String, ServiceState> serviceStateMap =
+ new HashMap<String, ServiceState>();
// A mapping from the token of a TV input session to its state.
private final Map<IBinder, SessionState> sessionStateMap =
@@ -707,66 +746,91 @@
}
private final class ServiceState {
- private final List<IBinder> clients = new ArrayList<IBinder>();
- private final List<IBinder> sessionTokens = new ArrayList<IBinder>();
- private final ServiceConnection connection;
+ // TODO: need to implement DeathRecipient for clients.
+ private final List<IBinder> mClients = new ArrayList<IBinder>();
+ private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
+ private final ServiceConnection mConnection;
+ private final TvInputInfo mTvInputInfo;
- private ITvInputService service;
- private ServiceCallback callback;
- private boolean bound;
- private boolean available;
+ private ITvInputService mService;
+ private ServiceCallback mCallback;
+ private boolean mBound;
+ private boolean mAvailable;
+ private boolean mReconnecting;
- private ServiceState(int userId) {
- this.connection = new InputServiceConnection(userId);
+ private ServiceState(TvInputInfo inputInfo, int userId) {
+ mTvInputInfo = inputInfo;
+ mConnection = new InputServiceConnection(inputInfo, userId);
}
}
- private static final class SessionState {
- private final ComponentName name;
- private final ITvInputClient client;
- private final int seq;
- private final int callingUid;
+ private final class SessionState implements IBinder.DeathRecipient {
+ private final String mInputId;
+ private final ITvInputClient mClient;
+ private final int mSeq;
+ private final int mCallingUid;
+ private final int mUserId;
+ private final IBinder mToken;
+ private ITvInputSession mSession;
+ private Uri mLogUri;
- private ITvInputSession session;
- private Uri logUri;
+ private SessionState(IBinder token, String inputId, ITvInputClient client, int seq,
+ int callingUid, int userId) {
+ mToken = token;
+ mInputId = inputId;
+ mClient = client;
+ mSeq = seq;
+ mCallingUid = callingUid;
+ mUserId = userId;
+ }
- private SessionState(ComponentName name, ITvInputClient client, int seq, int callingUid) {
- this.name = name;
- this.client = client;
- this.seq = seq;
- this.callingUid = callingUid;
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ mSession = null;
+ if (mClient != null) {
+ try {
+ mClient.onSessionReleased(mSeq);
+ } catch(RemoteException e) {
+ Slog.e(TAG, "error in onSessionReleased", e);
+ }
+ }
+ removeSessionStateLocked(mToken, mUserId);
+ }
}
}
private final class InputServiceConnection implements ServiceConnection {
+ private final TvInputInfo mTvInputInfo;
private final int mUserId;
- private InputServiceConnection(int userId) {
+ private InputServiceConnection(TvInputInfo inputInfo, int userId) {
mUserId = userId;
+ mTvInputInfo = inputInfo;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) {
- Slog.d(TAG, "onServiceConnected(name=" + name.getClassName() + ")");
+ Slog.d(TAG, "onServiceConnected(inputId=" + mTvInputInfo.getId() + ")");
}
synchronized (mLock) {
- ServiceState serviceState = getServiceStateLocked(name, mUserId);
- serviceState.service = ITvInputService.Stub.asInterface(service);
+ ServiceState serviceState = getServiceStateLocked(mTvInputInfo.getId(), mUserId);
+ serviceState.mService = ITvInputService.Stub.asInterface(service);
// Register a callback, if we need to.
- if (!serviceState.clients.isEmpty() && serviceState.callback == null) {
- serviceState.callback = new ServiceCallback(mUserId);
+ if (!serviceState.mClients.isEmpty() && serviceState.mCallback == null) {
+ serviceState.mCallback = new ServiceCallback(mUserId);
try {
- serviceState.service.registerCallback(serviceState.callback);
+ serviceState.mService.registerCallback(serviceState.mCallback);
} catch (RemoteException e) {
Slog.e(TAG, "error in registerCallback", e);
}
}
// And create sessions, if any.
- for (IBinder sessionToken : serviceState.sessionTokens) {
- createSessionInternalLocked(serviceState.service, sessionToken, mUserId);
+ for (IBinder sessionToken : serviceState.mSessionTokens) {
+ createSessionInternalLocked(serviceState.mService, sessionToken, mUserId);
}
}
}
@@ -774,7 +838,38 @@
@Override
public void onServiceDisconnected(ComponentName name) {
if (DEBUG) {
- Slog.d(TAG, "onServiceDisconnected(name=" + name.getClassName() + ")");
+ Slog.d(TAG, "onServiceDisconnected(inputId=" + mTvInputInfo.getId() + ")");
+ }
+ if (!mTvInputInfo.getComponent().equals(name)) {
+ throw new IllegalArgumentException("Mismatched ComponentName: "
+ + mTvInputInfo.getComponent() + " (expected), " + name + " (actual).");
+ }
+ synchronized (mLock) {
+ UserState userState = getUserStateLocked(mUserId);
+ ServiceState serviceState = userState.serviceStateMap.get(mTvInputInfo.getId());
+ if (serviceState != null) {
+ serviceState.mReconnecting = true;
+ serviceState.mBound = false;
+ serviceState.mService = null;
+ serviceState.mCallback = null;
+
+ // Send null tokens for not finishing create session events.
+ for (IBinder sessionToken : serviceState.mSessionTokens) {
+ SessionState sessionState = userState.sessionStateMap.get(sessionToken);
+ if (sessionState.mSession == null) {
+ removeSessionStateLocked(sessionToken, sessionState.mUserId);
+ sendSessionTokenToClientLocked(sessionState.mClient,
+ sessionState.mInputId, null, null, sessionState.mSeq,
+ sessionState.mUserId);
+ }
+ }
+
+ if (serviceState.mAvailable) {
+ serviceState.mAvailable = false;
+ broadcastServiceAvailabilityChangedLocked(serviceState);
+ }
+ updateServiceConnectionLocked(mTvInputInfo.getId(), mUserId);
+ }
}
}
}
@@ -787,18 +882,16 @@
}
@Override
- public void onAvailabilityChanged(ComponentName name, boolean isAvailable)
- throws RemoteException {
+ public void onAvailabilityChanged(String inputId, boolean isAvailable) {
if (DEBUG) {
- Slog.d(TAG, "onAvailabilityChanged(name=" + name.getClassName() + ", isAvailable="
+ Slog.d(TAG, "onAvailabilityChanged(inputId=" + inputId + ", isAvailable="
+ isAvailable + ")");
}
synchronized (mLock) {
- ServiceState serviceState = getServiceStateLocked(name, mUserId);
- serviceState.available = isAvailable;
- for (IBinder iBinder : serviceState.clients) {
- ITvInputClient client = ITvInputClient.Stub.asInterface(iBinder);
- client.onAvailabilityChanged(name, isAvailable);
+ ServiceState serviceState = getServiceStateLocked(inputId, mUserId);
+ if (serviceState.mAvailable != isAvailable) {
+ serviceState.mAvailable = isAvailable;
+ broadcastServiceAvailabilityChangedLocked(serviceState);
}
}
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 8ea9b0d..b104c11 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -18,7 +18,7 @@
import android.content.Intent;
import android.net.LinkProperties;
-import android.net.LinkCapabilities;
+import android.net.NetworkCapabilities;
import android.os.Bundle;
import android.telephony.CellInfo;
import android.telephony.DataConnectionRealTimeInfo;
@@ -37,7 +37,7 @@
void notifyDataActivity(int state);
void notifyDataConnection(int state, boolean isDataConnectivityPossible,
String reason, String apn, String apnType, in LinkProperties linkProperties,
- in LinkCapabilities linkCapabilities, int networkType, boolean roaming);
+ in NetworkCapabilities networkCapabilities, int networkType, boolean roaming);
void notifyDataConnectionFailed(String reason, String apnType);
void notifyCellLocation(in Bundle cellLocation);
void notifyOtaspChanged(in int otaspMode);
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 8c42d25..6ad57cf 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -78,7 +78,7 @@
public static final String DATA_APN_TYPE_KEY = "apnType";
public static final String DATA_APN_KEY = "apn";
public static final String DATA_LINK_PROPERTIES_KEY = "linkProperties";
- public static final String DATA_LINK_CAPABILITIES_KEY = "linkCapabilities";
+ public static final String DATA_NETWORK_CAPABILITIES_KEY = "networkCapabilities";
public static final String DATA_IFACE_NAME_KEY = "iface";
public static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable";
diff --git a/tests/Split/Android.mk b/tests/Split/Android.mk
new file mode 100644
index 0000000..7884d4d
--- /dev/null
+++ b/tests/Split/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_PACKAGE_NAME := Split
+
+LOCAL_AAPT_FLAGS := --split fr,de
+LOCAL_AAPT_FLAGS += -v
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/Split/AndroidManifest.xml b/tests/Split/AndroidManifest.xml
new file mode 100644
index 0000000..a4956a7
--- /dev/null
+++ b/tests/Split/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.example.split">
+ <application android:label="@string/app_title">
+ <activity android:name="ActivityMain">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/Split/assets/blah.txt b/tests/Split/assets/blah.txt
new file mode 100644
index 0000000..1b37e40
--- /dev/null
+++ b/tests/Split/assets/blah.txt
@@ -0,0 +1 @@
+This is some useful info.
diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml b/tests/Split/assets/statement.xml
similarity index 73%
copy from packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
copy to tests/Split/assets/statement.xml
index a018929..91750d1 100644
--- a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
+++ b/tests/Split/assets/statement.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,6 +14,6 @@
limitations under the License.
-->
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_qs_contrast_alpha"
- android:tint="@color/ic_qs_on" />
+<statement>
+ <value>Hello</value>
+</statement>
diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml b/tests/Split/res/layout-fr-sw600dp/main.xml
similarity index 64%
copy from packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
copy to tests/Split/res/layout-fr-sw600dp/main.xml
index a018929..2461c8c 100644
--- a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
+++ b/tests/Split/res/layout-fr-sw600dp/main.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,6 +14,10 @@
limitations under the License.
-->
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_qs_contrast_alpha"
- android:tint="@color/ic_qs_on" />
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+</FrameLayout>
diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml b/tests/Split/res/layout/main.xml
similarity index 73%
copy from packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
copy to tests/Split/res/layout/main.xml
index a018929..36992a2 100644
--- a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
+++ b/tests/Split/res/layout/main.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,6 +14,6 @@
limitations under the License.
-->
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_qs_contrast_alpha"
- android:tint="@color/ic_qs_on" />
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml b/tests/Split/res/values-de/values.xml
similarity index 73%
copy from packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
copy to tests/Split/res/values-de/values.xml
index a018929..26d0507 100644
--- a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
+++ b/tests/Split/res/values-de/values.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,6 +14,6 @@
limitations under the License.
-->
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_qs_contrast_alpha"
- android:tint="@color/ic_qs_on" />
+<resources>
+ <string name="test">Achtung!</string>
+</resources>
diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml b/tests/Split/res/values-fr/values.xml
similarity index 63%
copy from packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
copy to tests/Split/res/values-fr/values.xml
index a018929..16532da 100644
--- a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
+++ b/tests/Split/res/values-fr/values.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,6 +14,12 @@
limitations under the License.
-->
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_qs_contrast_alpha"
- android:tint="@color/ic_qs_on" />
+<resources>
+ <string name="app_title">APK Divisé</string>
+ <string name="test">Bonjour, Monde!</string>
+ <string name="blah">Bleh..</string>
+ <string-array name="lotsofstrings">
+ <item>Hé là</item>
+ <item>Au revoir</item>
+ </string-array>
+</resources>
diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml b/tests/Split/res/values-sw600dp/values.xml
similarity index 73%
rename from packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
rename to tests/Split/res/values-sw600dp/values.xml
index a018929..a8329bb 100644
--- a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
+++ b/tests/Split/res/values-sw600dp/values.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,6 +14,6 @@
limitations under the License.
-->
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_qs_contrast_alpha"
- android:tint="@color/ic_qs_on" />
+<resources>
+ <dimen name="width">230dp</dimen>
+</resources>
diff --git a/tests/Split/res/values/values.xml b/tests/Split/res/values/values.xml
new file mode 100644
index 0000000..68edc77
--- /dev/null
+++ b/tests/Split/res/values/values.xml
@@ -0,0 +1,47 @@
+<?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.
+-->
+
+<resources>
+ <string name="app_title">Split APK</string>
+ <string name="test">Hello, World!</string>
+ <string name="boom">Boom!</string>
+ <string name="blah">Blah...</string>
+ <string-array name="lotsofstrings">
+ <item>Hello there</item>
+ <item>Good bye</item>
+ </string-array>
+
+ <plurals name="plur">
+ <item quantity="zero">I no haz :(</item>
+ <item quantity="one">I haz 1!1! :)</item>
+ <item quantity="many">I haz ALL!</item>
+ </plurals>
+
+ <bool name="que">true</bool>
+ <color name="green">#00FF00</color>
+ <dimen name="width">23dp</dimen>
+ <item type="id" name="identifier" />
+ <integer name="number">123</integer>
+ <integer-array name="numList">
+ <item>1234</item>
+ </integer-array>
+
+ <array name="ary">
+ <item>@string/test</item>
+ <item>@string/boom</item>
+ <item>25dp</item>
+ </array>
+</resources>
diff --git a/tests/Split/src/java/com/android/example/split/ActivityMain.java b/tests/Split/src/java/com/android/example/split/ActivityMain.java
new file mode 100644
index 0000000..a15fb3c
--- /dev/null
+++ b/tests/Split/src/java/com/android/example/split/ActivityMain.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.example.split;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class ActivityMain extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ TextView text = new TextView(this);
+ text.setText(R.string.test);
+ setContentView(text);
+ }
+}
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
index d0f2a2d..118f258 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
@@ -24,13 +24,12 @@
android:viewportHeight="480"
android:viewportWidth="480" />
- <group>
- <path
- android:name="box1"
- android:pathData="m20,200l100,90l180,-180l-35,-35l-145,145l-60,-60l-40,40z"
- android:fill="?android:attr/colorControlActivated"
- android:stroke="?android:attr/colorControlActivated"
- android:strokeLineCap="round"
- android:strokeLineJoin="round" />
- </group>
-</vector>
+ <path
+ android:name="box1"
+ android:fill="?android:attr/colorControlActivated"
+ android:pathData="m20,200l100,90l180,-180l-35,-35l-145,145l-60,-60l-40,40z"
+ android:stroke="?android:attr/colorControlActivated"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml
index 728624a..034f7a0 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,23 +16,23 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" >
<size
- android:width="64dp"
- android:height="64dp"/>
+ android:height="64dp"
+ android:width="64dp" />
- <viewport android:viewportWidth="320"
- android:viewportHeight="320"/>
+ <viewport
+ android:viewportHeight="320"
+ android:viewportWidth="320" />
- <group>
- <path
- android:name="house"
- android:pathData="M 130,225 L 130,115 L 130,115 L 70,15 L 10,115 L 10,115 L 10,225 z"
- android:fill="#ff440000"
- android:stroke="#FF00FF00"
- android:strokeWidth="10"
- android:rotation="180"
- android:pivotX="70"
- android:pivotY="120"
- android:trimPathStart=".1"
- android:trimPathEnd=".9"/>
- </group>
-</vector>
+ <path
+ android:name="house"
+ android:fill="#ff440000"
+ android:pathData="M 130,225 L 130,115 L 130,115 L 70,15 L 10,115 L 10,115 L 10,225 z"
+ android:pivotX="70"
+ android:pivotY="120"
+ android:rotation="180"
+ android:stroke="#FF00FF00"
+ android:strokeWidth="10"
+ android:trimPathEnd=".9"
+ android:trimPathStart=".1" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
index 1792683..451b28e 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,51 +16,47 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" >
<size
- android:width="64dp"
- android:height="64dp"/>
+ android:height="64dp"
+ android:width="64dp" />
<viewport
- android:viewportWidth="7.30625"
- android:viewportHeight="12.25"/>
+ android:viewportHeight="12.25"
+ android:viewportWidth="7.30625" />
- <group>
-
- <path
- android:name="clip1"
- android:pathData="
+ <path
+ android:name="clip1"
+ android:clipToPath="true"
+ android:pathData="
M 0, 0
l 7.3, 0
l 0, 0
l -7.3, 0
z"
- android:clipToPath="true"
- android:rotation="-30"
- android:pivotX="3.65"
- android:pivotY="6.125"
- />
- <path
- android:name="one"
- android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
+ android:pivotX="3.65"
+ android:pivotY="6.125"
+ android:rotation="-30" />
+ <path
+ android:name="one"
+ android:fill="#ff88ff"
+ android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
- l -5.046875,0.0 0.0,-1.0Z"
- android:fill="#ff88ff"
- />
- <path
- android:name="clip2"
- android:pathData="
+ l -5.046875,0.0 0.0,-1.0Z" />
+ <path
+ android:name="clip2"
+ android:clipToPath="true"
+ android:pathData="
M 0, 0
l 7.3, 0
l 0, 12.25
l -7.3, 0
z"
- android:clipToPath="true"
- android:rotation="-30"
- android:pivotX="3.65"
- android:pivotY="6.125"
- />
- <path
- android:name="two"
- android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
+ android:pivotX="3.65"
+ android:pivotY="6.125"
+ android:rotation="-30" />
+ <path
+ android:name="two"
+ android:fill="#ff88ff"
+ android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625
q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625
q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875
@@ -67,8 +64,6 @@
q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875
q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
- q -0.78125024,0.8125 -2.2187502,2.265625Z"
- android:fill="#ff88ff"
- />
- </group>
-</vector>
+ q -0.78125024,0.8125 -2.2187502,2.265625Z" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
index 90694fb..6f9caa8 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -12,48 +13,44 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android">
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
<size
- android:width="64dp"
- android:height="64dp"/>
+ android:height="64dp"
+ android:width="64dp" />
<viewport
- android:viewportWidth="7.30625"
- android:viewportHeight="12.25"/>
+ android:viewportHeight="12.25"
+ android:viewportWidth="7.30625" />
- <group>
- <path
- android:name="clip1"
- android:pathData="
+ <path
+ android:name="clip1"
+ android:clipToPath="true"
+ android:fill="#112233"
+ android:pathData="
M 3.65, 6.125
m -.001, 0
a .001,.001 0 1,0 .002,0
- a .001,.001 0 1,0 -.002,0z"
- android:clipToPath="true"
- android:fill="#112233"
- />
-
- <path
- android:name="one"
- android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
+ a .001,.001 0 1,0 -.002,0z" />
+ <path
+ android:name="one"
+ android:fill="#ff88ff"
+ android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
- l -5.046875,0.0 0.0,-1.0Z"
- android:fill="#ff88ff"
- />
- <path
- android:name="clip2"
- android:pathData="
+ l -5.046875,0.0 0.0,-1.0Z" />
+ <path
+ android:name="clip2"
+ android:clipToPath="true"
+ android:fill="#112233"
+ android:pathData="
M 3.65, 6.125
m -6, 0
a 6,6 0 1,0 12,0
- a 6,6 0 1,0 -12,0z"
- android:clipToPath="true"
- android:fill="#112233"
- />
- <path
- android:name="two"
- android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
+ a 6,6 0 1,0 -12,0z" />
+ <path
+ android:name="two"
+ android:fill="#ff88ff"
+ android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625
q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625
q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875
@@ -61,8 +58,6 @@
q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875
q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
- q -0.78125024,0.8125 -2.2187502,2.265625Z"
- android:fill="#ff88ff"
- />
- </group>
-</vector>
+ q -0.78125024,0.8125 -2.2187502,2.265625Z" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
index c6595fa..e6c2557 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
@@ -23,18 +23,17 @@
android:viewportHeight="12.25"
android:viewportWidth="7.30625" />
- <group>
- <path
- android:name="one"
- android:fill="#ffff00"
- android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
+ <path
+ android:name="one"
+ android:fill="#ffff00"
+ android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
l -5.046875,0.0 0.0,-1.0Z" />
- <path
- android:name="two"
- android:fill="#ffff00"
- android:fillOpacity="0"
- android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
+ <path
+ android:name="two"
+ android:fill="#ffff00"
+ android:fillOpacity="0"
+ android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625
q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625
q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875
@@ -43,5 +42,5 @@
q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
q -0.78125024,0.8125 -2.2187502,2.265625Z" />
- </group>
+
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml
index 850de28..3f8cc09 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,34 +16,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" >
<size
- android:width="64dp"
- android:height="64dp"/>
+ android:height="64dp"
+ android:width="64dp" />
<viewport
- android:viewportWidth="700"
- android:viewportHeight="700"/>
+ android:viewportHeight="700"
+ android:viewportWidth="700" />
- <group>
- <path android:pathData="M 569.374 461.472L 569.374 160.658L 160.658 160.658L 160.658 461.472L 569.374 461.472z"
- android:name="path2451"
- android:stroke="#FF000000"
- android:strokeWidth="30.65500000000000"/>
- <path android:pathData="M 365.015 311.066"
- android:name="path2453"
- android:stroke="#FF000000"
- android:strokeWidth="30.655000000000001"/>
- <path android:pathData="M 164.46 164.49L 340.78 343.158C 353.849 356.328 377.63 356.172 390.423 343.278L 566.622 165.928"
- android:name="path2455"
- android:stroke="#FF000000"
- android:fill="#FFFFFFFF"
- android:strokeWidth="30.655000000000001"/>
- <path android:pathData="M 170.515 451.566L 305.61 313.46"
- android:name="path2457"
- android:stroke="#000000"
- android:strokeWidth="30.655000000000001"/>
- <path android:pathData="M 557.968 449.974L 426.515 315.375"
- android:name="path2459"
- android:stroke="#000000"
- android:strokeWidth="30.655000000000001"/>
- </group>
-</vector>
+ <path
+ android:name="path2451"
+ android:pathData="M 569.374 461.472L 569.374 160.658L 160.658 160.658L 160.658 461.472L 569.374 461.472z"
+ android:stroke="#FF000000"
+ android:strokeWidth="30.65500000000000" />
+ <path
+ android:name="path2453"
+ android:pathData="M 365.015 311.066"
+ android:stroke="#FF000000"
+ android:strokeWidth="30.655000000000001" />
+ <path
+ android:name="path2455"
+ android:fill="#FFFFFFFF"
+ android:pathData="M 164.46 164.49L 340.78 343.158C 353.849 356.328 377.63 356.172 390.423 343.278L 566.622 165.928"
+ android:stroke="#FF000000"
+ android:strokeWidth="30.655000000000001" />
+ <path
+ android:name="path2457"
+ android:pathData="M 170.515 451.566L 305.61 313.46"
+ android:stroke="#000000"
+ android:strokeWidth="30.655000000000001" />
+ <path
+ android:name="path2459"
+ android:pathData="M 557.968 449.974L 426.515 315.375"
+ android:stroke="#000000"
+ android:strokeWidth="30.655000000000001" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml
index 7c7e679..4db5090 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -12,21 +13,21 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
<size
- android:width="64dp"
- android:height="64dp"/>
+ android:height="64dp"
+ android:width="64dp" />
- <viewport android:viewportWidth="140"
- android:viewportHeight="110"/>
+ <viewport
+ android:viewportHeight="110"
+ android:viewportWidth="140" />
- <group>
- <path
- android:name="back"
- android:pathData="M 20,55 l 35.3,-35.3 7.07,7.07 -35.3,35.3 z
+ <path
+ android:name="back"
+ android:fill="#ffffffff"
+ android:pathData="M 20,55 l 35.3,-35.3 7.07,7.07 -35.3,35.3 z
M 27,50 l 97,0 0,10 -97,0 z
- M 20,55 l 7.07,-7.07 35.3,35.3 -7.07,7.07 z"
- android:fill="#ffffffff"
- />
- </group>
-</vector>
+ M 20,55 l 7.07,-7.07 35.3,35.3 -7.07,7.07 z" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml
index 59f7459..44ef979 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,20 +16,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" >
<size
- android:width="64dp"
- android:height="64dp"/>
+ android:height="64dp"
+ android:width="64dp" />
+ <viewport
+ android:viewportHeight="600"
+ android:viewportWidth="600" />
- <viewport android:viewportWidth="600"
- android:viewportHeight="600"/>
+ <path
+ android:name="pie1"
+ android:fill="#ffffcc00"
+ android:pathData="M535.441,412.339A280.868,280.868 0 1,1 536.186,161.733L284.493,286.29Z"
+ android:stroke="#FF00FF00"
+ android:strokeWidth="1" />
- <group>
- <path
- android:name="pie1"
- android:pathData="M535.441,412.339A280.868,280.868 0 1,1 536.186,161.733L284.493,286.29Z"
- android:fill="#ffffcc00"
- android:stroke="#FF00FF00"
- android:strokeWidth="1"/>
- </group>
-
-</vector>
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml
index 2e379d6..248a143 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml
@@ -23,14 +23,12 @@
android:viewportHeight="200"
android:viewportWidth="200" />
- <group>
- <path
- android:name="house"
- android:fill="#ffffffff"
- android:pathData="M 100,20 l 0,0 0,140 -80,0 z M 100,20 l 0,0 80,140 -80,0 z"
- android:pivotX="100"
- android:pivotY="100"
- android:rotation="90" />
- </group>
+ <path
+ android:name="house"
+ android:fill="#ffffffff"
+ android:pathData="M 100,20 l 0,0 0,140 -80,0 z M 100,20 l 0,0 80,140 -80,0 z"
+ android:pivotX="100"
+ android:pivotY="100"
+ android:rotation="90" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml
index 8484e9e..56c2972 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml
@@ -21,26 +21,24 @@
android:width="64dp" />
<viewport
- android:viewportWidth="200"
- android:viewportHeight="200"/>
+ android:viewportHeight="200"
+ android:viewportWidth="200" />
- <group>
- <path
- android:name="bar3"
- android:fill="#FFFFFFFF"
- android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" />
- <path
- android:name="bar2"
- android:fill="#FFFFFFFF"
- android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" />
- <path
- android:name="bar1"
- android:fill="#FF555555"
- android:pathData="M14.001,34.645 L21,41.716c15.464 -15.621,40.536 -15.621,56,0l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" />
- <path
- android:name="bar0"
- android:fill="#FF555555"
- android:pathData="M0,20.502l6.999,7.071 c23.196 -23.431,60.806 -23.431,84.002,0L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" />
- </group>
+ <path
+ android:name="bar3"
+ android:fill="#FFFFFFFF"
+ android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" />
+ <path
+ android:name="bar2"
+ android:fill="#FFFFFFFF"
+ android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" />
+ <path
+ android:name="bar1"
+ android:fill="#FF555555"
+ android:pathData="M14.001,34.645 L21,41.716c15.464 -15.621,40.536 -15.621,56,0l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" />
+ <path
+ android:name="bar0"
+ android:fill="#FF555555"
+ android:pathData="M0,20.502l6.999,7.071 c23.196 -23.431,60.806 -23.431,84.002,0L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml
index 2b6c5d3..16d8b48 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml
@@ -23,18 +23,16 @@
android:viewportHeight="80"
android:viewportWidth="40" />
- <group>
- <path
- android:name="battery"
- android:fill="#3388ff"
- android:pathData="M 20.28125,2.0000002 C 17.352748,2.0000002 15,4.3527485 15,7.2812502 L 15,8.0000002 L 13.15625,8.0000002 C 9.7507553,8.0000002 7,10.750759 7,14.15625 L 7,39.84375 C 7,43.24924 9.7507558,46 13.15625,46 L 33.84375,46 C 37.249245,46 39.999999,43.24924 40,39.84375 L 40,14.15625 C 40,10.75076 37.249243,8.0000002 33.84375,8.0000002 L 32,8.0000002 L 32,7.2812502 C 32,4.3527485 29.647252,2.0000002 26.71875,2.0000002 L 20.28125,2.0000002 z"
- android:rotation="0"
- android:stroke="#ff8833"
- android:strokeWidth="1" />
- <path
- android:name="spark"
- android:fill="#FFFF0000"
- android:pathData="M 30,18.031528 L 25.579581,23.421071 L 29.370621,26.765348 L 20.096792,37 L 21.156922,28.014053 L 17,24.902844 L 20.880632,18 L 30,18.031528 z" />
- </group>
+ <path
+ android:name="battery"
+ android:fill="#3388ff"
+ android:pathData="M 20.28125,2.0000002 C 17.352748,2.0000002 15,4.3527485 15,7.2812502 L 15,8.0000002 L 13.15625,8.0000002 C 9.7507553,8.0000002 7,10.750759 7,14.15625 L 7,39.84375 C 7,43.24924 9.7507558,46 13.15625,46 L 33.84375,46 C 37.249245,46 39.999999,43.24924 40,39.84375 L 40,14.15625 C 40,10.75076 37.249243,8.0000002 33.84375,8.0000002 L 32,8.0000002 L 32,7.2812502 C 32,4.3527485 29.647252,2.0000002 26.71875,2.0000002 L 20.28125,2.0000002 z"
+ android:rotation="0"
+ android:stroke="#ff8833"
+ android:strokeWidth="1" />
+ <path
+ android:name="spark"
+ android:fill="#FFFF0000"
+ android:pathData="M 30,18.031528 L 25.579581,23.421071 L 29.370621,26.765348 L 20.096792,37 L 21.156922,28.014053 L 17,24.902844 L 20.880632,18 L 30,18.031528 z" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
index 681eb4f..0a0407d 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
@@ -23,22 +23,20 @@
android:viewportHeight="600"
android:viewportWidth="600" />
- <group>
- <path
- android:name="pie1"
- android:pathData="M300,70 a230,230 0 1,0 1,0 z"
- android:stroke="#FF00FF00"
- android:strokeWidth="70"
- android:trimPathEnd=".75"
- android:trimPathOffset="0"
- android:trimPathStart="0" />
- <path
- android:name="v"
- android:fill="#FF00FF00"
- android:pathData="M300,70 l 0,-70 70,70 -70,70z"
- android:pivotX="300"
- android:pivotY="300"
- android:rotation="0" />
- </group>
+ <path
+ android:name="pie1"
+ android:pathData="M300,70 a230,230 0 1,0 1,0 z"
+ android:stroke="#FF00FF00"
+ android:strokeWidth="70"
+ android:trimPathEnd=".75"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
+ <path
+ android:name="v"
+ android:fill="#FF00FF00"
+ android:pathData="M300,70 l 0,-70 70,70 -70,70z"
+ android:pivotX="300"
+ android:pivotY="300"
+ android:rotation="0" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml
index ef1b8e4..385b1e9 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml
@@ -23,22 +23,20 @@
android:viewportHeight="400"
android:viewportWidth="600" />
- <group>
- <path
- android:name="pie1"
- android:fill="#ffffffff"
- android:pathData="M300,200 h-150 a150,150 0 1,0 150,-150 z"
- android:stroke="#FF00FF00"
- android:strokeWidth="1" />
- <path
- android:name="half"
- android:fill="#FFFF0000"
- android:pathData="M275,175 v-150 a150,150 0 0,0 -150,150 z"
- android:pivotX="300"
- android:pivotY="200"
- android:rotation="0"
- android:stroke="#FF0000FF"
- android:strokeWidth="5" />
- </group>
+ <path
+ android:name="pie1"
+ android:fill="#ffffffff"
+ android:pathData="M300,200 h-150 a150,150 0 1,0 150,-150 z"
+ android:stroke="#FF00FF00"
+ android:strokeWidth="1" />
+ <path
+ android:name="half"
+ android:fill="#FFFF0000"
+ android:pathData="M275,175 v-150 a150,150 0 0,0 -150,150 z"
+ android:pivotX="300"
+ android:pivotY="200"
+ android:rotation="0"
+ android:stroke="#FF0000FF"
+ android:strokeWidth="5" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml
index 77bf723..b701b35 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml
@@ -23,19 +23,17 @@
android:viewportHeight="500"
android:viewportWidth="800" />
- <group>
- <path
- android:name="pie2"
- android:pathData="M200,350 l 50,-25
+ <path
+ android:name="pie2"
+ android:pathData="M200,350 l 50,-25
a25,12 -30 0,1 100,-50 l 50,-25
a25,25 -30 0,1 100,-50 l 50,-25
a25,37 -30 0,1 100,-50 l 50,-25
a25,50 -30 0,1 100,-50 l 50,-25"
- android:pivotX="90"
- android:pivotY="100"
- android:rotation="20"
- android:stroke="#FF00FF00"
- android:strokeWidth="10" />
- </group>
+ android:pivotX="90"
+ android:pivotY="100"
+ android:rotation="20"
+ android:stroke="#FF00FF00"
+ android:strokeWidth="10" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml
index df5838c..8d773e1 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml
@@ -23,16 +23,14 @@
android:viewportHeight="400"
android:viewportWidth="500" />
- <group>
- <path
- android:name="house"
- android:fill="#ff440000"
- android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200"
- android:pivotX="250"
- android:pivotY="200"
- android:rotation="180"
- android:stroke="#FFFF0000"
- android:strokeWidth="10" />
- </group>
+ <path
+ android:name="house"
+ android:fill="#ff440000"
+ android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200"
+ android:pivotX="250"
+ android:pivotY="200"
+ android:rotation="180"
+ android:stroke="#FFFF0000"
+ android:strokeWidth="10" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml
index 0bdcda5..3b7926c 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml
@@ -23,15 +23,13 @@
android:viewportHeight="200"
android:viewportWidth="200" />
- <group>
- <path
- android:name="house"
- android:pathData="M 100,10 v 90 M 10,100 h 90"
- android:pivotX="100"
- android:pivotY="100"
- android:rotation="360"
- android:stroke="#FF00FF00"
- android:strokeWidth="10" />
- </group>
+ <path
+ android:name="house"
+ android:pathData="M 100,10 v 90 M 10,100 h 90"
+ android:pivotX="100"
+ android:pivotY="100"
+ android:rotation="360"
+ android:stroke="#FF00FF00"
+ android:strokeWidth="10" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml
index 4453ae4..1ec72be 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,21 +16,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" >
<size
- android:width="64dp"
- android:height="64dp"/>
+ android:height="64dp"
+ android:width="64dp" />
- <viewport android:viewportWidth="1200"
- android:viewportHeight="600"/>
+ <viewport
+ android:viewportHeight="600"
+ android:viewportWidth="1200" />
- <group>
- <path
- android:name="house"
- android:pathData="M200,300 Q400,50 600,300 T1000,300"
- android:stroke="#FFFF0000"
- android:strokeWidth="10"
- android:rotation="360"
- android:pivotX="600"
- android:pivotY="300"/>
- </group>
+ <path
+ android:name="house"
+ android:pathData="M200,300 Q400,50 600,300 T1000,300"
+ android:pivotX="600"
+ android:pivotY="300"
+ android:rotation="360"
+ android:stroke="#FFFF0000"
+ android:strokeWidth="10" />
-</vector>
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml
index dfae9ac..12d0e93 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml
@@ -23,15 +23,13 @@
android:viewportHeight="400"
android:viewportWidth="500" />
- <group>
- <path
- android:name="house"
- android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200"
- android:pivotX="250"
- android:pivotY="200"
- android:rotation="360"
- android:stroke="#FFFFFF00"
- android:strokeWidth="10" />
- </group>
+ <path
+ android:name="house"
+ android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200"
+ android:pivotX="250"
+ android:pivotY="200"
+ android:rotation="360"
+ android:stroke="#FFFFFF00"
+ android:strokeWidth="10" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml
index a890fd6..017e04c 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml
@@ -23,14 +23,12 @@
android:viewportHeight="800"
android:viewportWidth="1000" />
- <group>
- <path
- android:name="house"
- android:pathData="M10,300 Q400,550 600,300 T1000,300"
- android:pivotX="90"
- android:pivotY="100"
- android:stroke="#FFFF0000"
- android:strokeWidth="60" />
- </group>
+ <path
+ android:name="house"
+ android:pathData="M10,300 Q400,550 600,300 T1000,300"
+ android:pivotX="90"
+ android:pivotY="100"
+ android:stroke="#FFFF0000"
+ android:strokeWidth="60" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml
index b8af7e2..b7002a3 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml
@@ -23,16 +23,14 @@
android:viewportHeight="480"
android:viewportWidth="480" />
- <group>
- <path
- android:name="edit"
- android:fill="#FF00FFFF"
- android:pathData="M406.667,180c0,0 -100 -100 -113.334 -113.333
+ <path
+ android:name="edit"
+ android:fill="#FF00FFFF"
+ android:pathData="M406.667,180c0,0 -100 -100 -113.334 -113.333
c-13.333 -13.334 -33.333,0 -33.333,0l-160,160c0,0 -40,153.333 -40,173.333c0,13.333,13.333,13.333,13.333,13.333l173.334 -40
c0,0,146.666 -146.666,160 -160C420,200,406.667,180,406.667,180z M226.399,356.823L131.95,378.62l-38.516 -38.522
c7.848 -34.675,20.152 -82.52,23.538 -95.593l3.027,2.162l106.667,106.666L226.399,356.823z"
- android:stroke="#FF000000"
- android:strokeWidth="10" />
- </group>
+ android:stroke="#FF000000"
+ android:strokeWidth="10" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml
index 22ce795..cda213d 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml
@@ -23,10 +23,8 @@
android:viewportHeight="24"
android:viewportWidth="24" />
- <group>
- <path
- android:fill="#FF000000"
- android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.813995,9.936001l-3.75,-3.75L3.0,17.25zM20.707,7.0429993c0.391,-0.391 0.391,-1.023 0.0,-1.414l-2.336,-2.336c-0.391,-0.391 -1.023,-0.391 -1.414,0.0l-1.832,1.832l3.75,3.75L20.707,7.0429993z" />
- </group>
+ <path
+ android:fill="#FF000000"
+ android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.813995,9.936001l-3.75,-3.75L3.0,17.25zM20.707,7.0429993c0.391,-0.391 0.391,-1.023 0.0,-1.414l-2.336,-2.336c-0.391,-0.391 -1.023,-0.391 -1.414,0.0l-1.832,1.832l3.75,3.75L20.707,7.0429993z" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml
index 042173c..2cb6381 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml
@@ -23,10 +23,8 @@
android:viewportHeight="24"
android:viewportWidth="24" />
- <group>
- <path
- android:fill="#FF000000"
- android:pathData="M6.0,19.0c0.0,1.104 0.896,2.0 2.0,2.0l8.0,0.0c1.104,0.0 2.0,-0.896 2.0,-2.0l0.0,-12.0L6.0,7.0L6.0,19.0zM18.0,4.0l-2.5,0.0l-1.0,-1.0l-5.0,0.0l-1.0,1.0L6.0,4.0C5.4469986,4.0 5.0,4.4469986 5.0,5.0l0.0,1.0l14.0,0.0l0.0,-1.0C19.0,4.4469986 18.552002,4.0 18.0,4.0z" />
- </group>
+ <path
+ android:fill="#FF000000"
+ android:pathData="M6.0,19.0c0.0,1.104 0.896,2.0 2.0,2.0l8.0,0.0c1.104,0.0 2.0,-0.896 2.0,-2.0l0.0,-12.0L6.0,7.0L6.0,19.0zM18.0,4.0l-2.5,0.0l-1.0,-1.0l-5.0,0.0l-1.0,1.0L6.0,4.0C5.4469986,4.0 5.0,4.4469986 5.0,5.0l0.0,1.0l14.0,0.0l0.0,-1.0C19.0,4.4469986 18.552002,4.0 18.0,4.0z" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml
index 6b6f43d..d58942e 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml
@@ -23,10 +23,8 @@
android:viewportHeight="24"
android:viewportWidth="24" />
- <group>
- <path
- android:fill="#FF000000"
- android:pathData="M16.0,5.0c-1.955,0.0 -3.83,1.268 -4.5,3.0c-0.67,-1.732 -2.547,-3.0 -4.5,-3.0C4.4570007,5.0 2.5,6.931999 2.5,9.5c0.0,3.529 3.793,6.258 9.0,11.5c5.207,-5.242 9.0,-7.971 9.0,-11.5C20.5,6.931999 18.543,5.0 16.0,5.0z" />
- </group>
+ <path
+ android:fill="#FF000000"
+ android:pathData="M16.0,5.0c-1.955,0.0 -3.83,1.268 -4.5,3.0c-0.67,-1.732 -2.547,-3.0 -4.5,-3.0C4.4570007,5.0 2.5,6.931999 2.5,9.5c0.0,3.529 3.793,6.258 9.0,11.5c5.207,-5.242 9.0,-7.971 9.0,-11.5C20.5,6.931999 18.543,5.0 16.0,5.0z" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
index ba8ebca..4717be4 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
@@ -23,13 +23,11 @@
android:viewportHeight="24"
android:viewportWidth="24" />
- <group>
- <path
- android:fillOpacity="0.9"
- android:pathData="M11.994999,2.0C6.4679985,2.0 2.0,6.4780006 2.0,12.0s4.468,10.0 9.995,10.0S22.0,17.522 22.0,12.0S17.521,2.0 11.994999,2.0zM12.0,20.0c-4.42,0.0 -8.0,-3.582 -8.0,-8.0s3.58,-8.0 8.0,-8.0s8.0,3.582 8.0,8.0S16.419998,20.0 12.0,20.0z" />
- <path
- android:fillOpacity="0.9"
- android:pathData="M12.5,6.0l-1.5,0.0 0.0,7.0 5.3029995,3.1819992 0.75,-1.249999 -4.5529995,-2.7320004z" />
- </group>
+ <path
+ android:fillOpacity="0.9"
+ android:pathData="M11.994999,2.0C6.4679985,2.0 2.0,6.4780006 2.0,12.0s4.468,10.0 9.995,10.0S22.0,17.522 22.0,12.0S17.521,2.0 11.994999,2.0zM12.0,20.0c-4.42,0.0 -8.0,-3.582 -8.0,-8.0s3.58,-8.0 8.0,-8.0s8.0,3.582 8.0,8.0S16.419998,20.0 12.0,20.0z" />
+ <path
+ android:fillOpacity="0.9"
+ android:pathData="M12.5,6.0l-1.5,0.0 0.0,7.0 5.3029995,3.1819992 0.75,-1.249999 -4.5529995,-2.7320004z" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml
index 896a938..c626325 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml
@@ -23,10 +23,8 @@
android:viewportHeight="24"
android:viewportWidth="24" />
- <group>
- <path
- android:fill="#FF000000"
- android:pathData="M19.429,12.975998c0.042,-0.32 0.07,-0.645 0.07,-0.976s-0.029,-0.655 -0.07,-0.976l2.113,-1.654c0.188,-0.151 0.243,-0.422 0.118,-0.639l-2.0,-3.463c-0.125,-0.217 -0.386,-0.304 -0.612,-0.218l-2.49,1.004c-0.516,-0.396 -1.081,-0.731 -1.69,-0.984l-0.375,-2.648C14.456,2.1829987 14.25,2.0 14.0,2.0l-4.0,0.0C9.75,2.0 9.544,2.1829987 9.506,2.422001L9.131,5.0699997C8.521,5.322998 7.957,5.6570015 7.44,6.054001L4.952,5.0509987C4.726,4.965 4.464,5.052002 4.34,5.269001l-2.0,3.463C2.2150002,8.947998 2.27,9.219002 2.4580002,9.369999l2.112,1.653C4.528,11.344002 4.5,11.668999 4.5,12.0s0.029,0.656 0.071,0.977L2.4580002,14.630001c-0.188,0.151 -0.243,0.422 -0.118,0.639l2.0,3.463c0.125,0.217 0.386,0.304 0.612,0.218l2.489,-1.004c0.516,0.396 1.081,0.731 1.69,0.984l0.375,2.648C9.544,21.817001 9.75,22.0 10.0,22.0l4.0,0.0c0.25,0.0 0.456,-0.183 0.494,-0.422l0.375,-2.648c0.609,-0.253 1.174,-0.588 1.689,-0.984l2.49,1.004c0.226,0.086 0.487,-0.001 0.612,-0.218l2.0,-3.463c0.125,-0.217 0.07,-0.487 -0.118,-0.639L19.429,12.975998zM12.0,16.0c-2.21,0.0 -4.0,-1.791 -4.0,-4.0c0.0,-2.21 1.79,-4.0 4.0,-4.0c2.208,0.0 4.0,1.79 4.0,4.0C16.0,14.209 14.208,16.0 12.0,16.0z" />
- </group>
+ <path
+ android:fill="#FF000000"
+ android:pathData="M19.429,12.975998c0.042,-0.32 0.07,-0.645 0.07,-0.976s-0.029,-0.655 -0.07,-0.976l2.113,-1.654c0.188,-0.151 0.243,-0.422 0.118,-0.639l-2.0,-3.463c-0.125,-0.217 -0.386,-0.304 -0.612,-0.218l-2.49,1.004c-0.516,-0.396 -1.081,-0.731 -1.69,-0.984l-0.375,-2.648C14.456,2.1829987 14.25,2.0 14.0,2.0l-4.0,0.0C9.75,2.0 9.544,2.1829987 9.506,2.422001L9.131,5.0699997C8.521,5.322998 7.957,5.6570015 7.44,6.054001L4.952,5.0509987C4.726,4.965 4.464,5.052002 4.34,5.269001l-2.0,3.463C2.2150002,8.947998 2.27,9.219002 2.4580002,9.369999l2.112,1.653C4.528,11.344002 4.5,11.668999 4.5,12.0s0.029,0.656 0.071,0.977L2.4580002,14.630001c-0.188,0.151 -0.243,0.422 -0.118,0.639l2.0,3.463c0.125,0.217 0.386,0.304 0.612,0.218l2.489,-1.004c0.516,0.396 1.081,0.731 1.69,0.984l0.375,2.648C9.544,21.817001 9.75,22.0 10.0,22.0l4.0,0.0c0.25,0.0 0.456,-0.183 0.494,-0.422l0.375,-2.648c0.609,-0.253 1.174,-0.588 1.689,-0.984l2.49,1.004c0.226,0.086 0.487,-0.001 0.612,-0.218l2.0,-3.463c0.125,-0.217 0.07,-0.487 -0.118,-0.639L19.429,12.975998zM12.0,16.0c-2.21,0.0 -4.0,-1.791 -4.0,-4.0c0.0,-2.21 1.79,-4.0 4.0,-4.0c2.208,0.0 4.0,1.79 4.0,4.0C16.0,14.209 14.208,16.0 12.0,16.0z" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_test01.xml b/tests/VectorDrawableTest/res/drawable/vector_test01.xml
index a9091ab..bad5a46 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_test01.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_test01.xml
@@ -23,12 +23,10 @@
android:viewportHeight="512"
android:viewportWidth="512" />
- <group>
- <path
- android:name="002b"
- android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0t-200,299"
- android:stroke="#FF0000FF"
- android:strokeWidth="4" />
- </group>
+ <path
+ android:name="002b"
+ android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0t-200,299"
+ android:stroke="#FF0000FF"
+ android:strokeWidth="4" />
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_test02.xml b/tests/VectorDrawableTest/res/drawable/vector_test02.xml
index ab58c06..c92b6f4 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_test02.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_test02.xml
@@ -23,12 +23,10 @@
android:viewportHeight="512"
android:viewportWidth="512" />
- <group>
- <path
- android:name="002b"
- android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0T-200,299"
- android:stroke="#FF0000FF"
- android:strokeWidth="4" />
- </group>
+ <path
+ android:name="002b"
+ android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0T-200,299"
+ android:stroke="#FF0000FF"
+ android:strokeWidth="4" />
</vector>
\ No newline at end of file
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index e0dab78..12d5389 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -3,8 +3,10 @@
//
#include "AaptAssets.h"
-#include "ResourceFilter.h"
+#include "AaptConfig.h"
+#include "AaptUtil.h"
#include "Main.h"
+#include "ResourceFilter.h"
#include <utils/misc.h>
#include <utils/SortedVector.h>
@@ -14,7 +16,6 @@
#include <errno.h>
static const char* kDefaultLocale = "default";
-static const char* kWildcardName = "any";
static const char* kAssetDir = "assets";
static const char* kResourceDir = "res";
static const char* kValuesDir = "values";
@@ -149,24 +150,6 @@
// =========================================================================
// =========================================================================
-/* static */ void AaptLocaleValue::splitAndLowerCase(const char* const chars,
- Vector<String8>* parts, const char separator) {
- const char *p = chars;
- const char *q;
- while (NULL != (q = strchr(p, separator))) {
- String8 val(p, q - p);
- val.toLower();
- parts->add(val);
- p = q+1;
- }
-
- if (p < chars + strlen(chars)) {
- String8 val(p);
- val.toLower();
- parts->add(val);
- }
-}
-
/* static */
inline bool isAlpha(const String8& string) {
const size_t length = string.length();
@@ -230,8 +213,7 @@
bool AaptLocaleValue::initFromFilterString(const String8& str) {
// A locale (as specified in the filter) is an underscore separated name such
// as "en_US", "en_Latn_US", or "en_US_POSIX".
- Vector<String8> parts;
- splitAndLowerCase(str.string(), &parts, '_');
+ Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '_');
const int numTags = parts.size();
bool valid = false;
@@ -301,8 +283,7 @@
if (part[0] == 'b' && part[1] == '+') {
// This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags,
// except that the separator is "+" and not "-".
- Vector<String8> subtags;
- AaptLocaleValue::splitAndLowerCase(part.string(), &subtags, '+');
+ Vector<String8> subtags = AaptUtil::splitAndLowerCase(part, '+');
subtags.removeItemsAt(0);
if (subtags.size() == 1) {
setLanguage(subtags[0]);
@@ -438,1349 +419,46 @@
}
}
-
-/* static */ bool
-AaptGroupEntry::parseFilterNamePart(const String8& part, int* axis, AxisValue* value)
-{
- ResTable_config config;
- memset(&config, 0, sizeof(ResTable_config));
-
- // IMSI - MCC
- if (getMccName(part.string(), &config)) {
- *axis = AXIS_MCC;
- value->intValue = config.mcc;
- return true;
- }
-
- // IMSI - MNC
- if (getMncName(part.string(), &config)) {
- *axis = AXIS_MNC;
- value->intValue = config.mnc;
- return true;
- }
-
- // locale - language
- if (value->localeValue.initFromFilterString(part)) {
- *axis = AXIS_LOCALE;
- return true;
- }
-
- // layout direction
- if (getLayoutDirectionName(part.string(), &config)) {
- *axis = AXIS_LAYOUTDIR;
- value->intValue = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
- return true;
- }
-
- // smallest screen dp width
- if (getSmallestScreenWidthDpName(part.string(), &config)) {
- *axis = AXIS_SMALLESTSCREENWIDTHDP;
- value->intValue = config.smallestScreenWidthDp;
- return true;
- }
-
- // screen dp width
- if (getScreenWidthDpName(part.string(), &config)) {
- *axis = AXIS_SCREENWIDTHDP;
- value->intValue = config.screenWidthDp;
- return true;
- }
-
- // screen dp height
- if (getScreenHeightDpName(part.string(), &config)) {
- *axis = AXIS_SCREENHEIGHTDP;
- value->intValue = config.screenHeightDp;
- return true;
- }
-
- // screen layout size
- if (getScreenLayoutSizeName(part.string(), &config)) {
- *axis = AXIS_SCREENLAYOUTSIZE;
- value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
- return true;
- }
-
- // screen layout long
- if (getScreenLayoutLongName(part.string(), &config)) {
- *axis = AXIS_SCREENLAYOUTLONG;
- value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
- return true;
- }
-
- // orientation
- if (getOrientationName(part.string(), &config)) {
- *axis = AXIS_ORIENTATION;
- value->intValue = config.orientation;
- return true;
- }
-
- // ui mode type
- if (getUiModeTypeName(part.string(), &config)) {
- *axis = AXIS_UIMODETYPE;
- value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
- return true;
- }
-
- // ui mode night
- if (getUiModeNightName(part.string(), &config)) {
- *axis = AXIS_UIMODENIGHT;
- value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
- return true;
- }
-
- // density
- if (getDensityName(part.string(), &config)) {
- *axis = AXIS_DENSITY;
- value->intValue = config.density;
- return true;
- }
-
- // touchscreen
- if (getTouchscreenName(part.string(), &config)) {
- *axis = AXIS_TOUCHSCREEN;
- value->intValue = config.touchscreen;
- return true;
- }
-
- // keyboard hidden
- if (getKeysHiddenName(part.string(), &config)) {
- *axis = AXIS_KEYSHIDDEN;
- value->intValue = config.inputFlags;
- return true;
- }
-
- // keyboard
- if (getKeyboardName(part.string(), &config)) {
- *axis = AXIS_KEYBOARD;
- value->intValue = config.keyboard;
- return true;
- }
-
- // navigation hidden
- if (getNavHiddenName(part.string(), &config)) {
- *axis = AXIS_NAVHIDDEN;
- value->intValue = config.inputFlags;
- return 0;
- }
-
- // navigation
- if (getNavigationName(part.string(), &config)) {
- *axis = AXIS_NAVIGATION;
- value->intValue = config.navigation;
- return true;
- }
-
- // screen size
- if (getScreenSizeName(part.string(), &config)) {
- *axis = AXIS_SCREENSIZE;
- value->intValue = config.screenSize;
- return true;
- }
-
- // version
- if (getVersionName(part.string(), &config)) {
- *axis = AXIS_VERSION;
- value->intValue = config.version;
- return true;
- }
-
- return false;
-}
-
-AxisValue
-AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
-{
- AxisValue value;
- switch (axis) {
- case AXIS_MCC:
- value.intValue = config.mcc;
- break;
- case AXIS_MNC:
- value.intValue = config.mnc;
- break;
- case AXIS_LOCALE:
- value.localeValue.initFromResTable(config);
- break;
- case AXIS_LAYOUTDIR:
- value.intValue = config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
- break;
- case AXIS_SCREENLAYOUTSIZE:
- value.intValue = config.screenLayout&ResTable_config::MASK_SCREENSIZE;
- break;
- case AXIS_ORIENTATION:
- value.intValue = config.orientation;
- break;
- case AXIS_UIMODETYPE:
- value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
- break;
- case AXIS_UIMODENIGHT:
- value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
- break;
- case AXIS_DENSITY:
- value.intValue = config.density;
- break;
- case AXIS_TOUCHSCREEN:
- value.intValue = config.touchscreen;
- break;
- case AXIS_KEYSHIDDEN:
- value.intValue = config.inputFlags;
- break;
- case AXIS_KEYBOARD:
- value.intValue = config.keyboard;
- break;
- case AXIS_NAVIGATION:
- value.intValue = config.navigation;
- break;
- case AXIS_SCREENSIZE:
- value.intValue = config.screenSize;
- break;
- case AXIS_SMALLESTSCREENWIDTHDP:
- value.intValue = config.smallestScreenWidthDp;
- break;
- case AXIS_SCREENWIDTHDP:
- value.intValue = config.screenWidthDp;
- break;
- case AXIS_SCREENHEIGHTDP:
- value.intValue = config.screenHeightDp;
- break;
- case AXIS_VERSION:
- value.intValue = config.version;
- break;
- }
-
- return value;
-}
-
-bool
-AaptGroupEntry::configSameExcept(const ResTable_config& config,
- const ResTable_config& otherConfig, int axis)
-{
- for (int i=AXIS_START; i<=AXIS_END; i++) {
- if (i == axis) {
- continue;
- }
- if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) {
- return false;
- }
- }
- return true;
-}
-
bool
AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
{
- mParamsChanged = true;
+ const char* q = strchr(dir, '-');
+ size_t typeLen;
+ if (q != NULL) {
+ typeLen = q - dir;
+ } else {
+ typeLen = strlen(dir);
+ }
- Vector<String8> parts;
- AaptLocaleValue::splitAndLowerCase(dir, &parts, '-');
-
- String8 mcc, mnc, layoutsize, layoutlong, orient, den;
- String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
- String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
-
- AaptLocaleValue locale;
- int numLocaleComponents = 0;
-
- const int N = parts.size();
- int index = 0;
- String8 part = parts[index];
-
- // resource type
- if (!isValidResourceType(part)) {
+ String8 type(dir, typeLen);
+ if (!isValidResourceType(type)) {
return false;
}
- *resType = part;
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
-
- // imsi - mcc
- if (getMccName(part.string())) {
- mcc = part;
-
- index++;
- if (index == N) {
- goto success;
+ if (q != NULL) {
+ if (!AaptConfig::parse(String8(q + 1), &mParams)) {
+ return false;
}
- part = parts[index];
- } else {
- //printf("not mcc: %s\n", part.string());
}
- // imsi - mnc
- if (getMncName(part.string())) {
- mnc = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not mnc: %s\n", part.string());
- }
-
- index = locale.initFromDirName(parts, index);
- if (index == -1) {
- return false;
- }
- if (index >= N){
- goto success;
- }
-
- part = parts[index];
- if (getLayoutDirectionName(part.string())) {
- layoutDir = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not layout direction: %s\n", part.string());
- }
-
- if (getSmallestScreenWidthDpName(part.string())) {
- smallestwidthdp = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not smallest screen width dp: %s\n", part.string());
- }
-
- if (getScreenWidthDpName(part.string())) {
- widthdp = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not screen width dp: %s\n", part.string());
- }
-
- if (getScreenHeightDpName(part.string())) {
- heightdp = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not screen height dp: %s\n", part.string());
- }
-
- if (getScreenLayoutSizeName(part.string())) {
- layoutsize = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not screen layout size: %s\n", part.string());
- }
-
- if (getScreenLayoutLongName(part.string())) {
- layoutlong = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not screen layout long: %s\n", part.string());
- }
-
- // orientation
- if (getOrientationName(part.string())) {
- orient = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not orientation: %s\n", part.string());
- }
-
- // ui mode type
- if (getUiModeTypeName(part.string())) {
- uiModeType = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not ui mode type: %s\n", part.string());
- }
-
- // ui mode night
- if (getUiModeNightName(part.string())) {
- uiModeNight = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not ui mode night: %s\n", part.string());
- }
-
- // density
- if (getDensityName(part.string())) {
- den = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not density: %s\n", part.string());
- }
-
- // touchscreen
- if (getTouchscreenName(part.string())) {
- touch = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not touchscreen: %s\n", part.string());
- }
-
- // keyboard hidden
- if (getKeysHiddenName(part.string())) {
- keysHidden = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not keysHidden: %s\n", part.string());
- }
-
- // keyboard
- if (getKeyboardName(part.string())) {
- key = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not keyboard: %s\n", part.string());
- }
-
- // navigation hidden
- if (getNavHiddenName(part.string())) {
- navHidden = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not navHidden: %s\n", part.string());
- }
-
- if (getNavigationName(part.string())) {
- nav = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not navigation: %s\n", part.string());
- }
-
- if (getScreenSizeName(part.string())) {
- size = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not screen size: %s\n", part.string());
- }
-
- if (getVersionName(part.string())) {
- vers = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not version: %s\n", part.string());
- }
-
- // if there are extra parts, it doesn't match
- return false;
-
-success:
- this->mcc = mcc;
- this->mnc = mnc;
- this->locale = locale;
- this->screenLayoutSize = layoutsize;
- this->screenLayoutLong = layoutlong;
- this->smallestScreenWidthDp = smallestwidthdp;
- this->screenWidthDp = widthdp;
- this->screenHeightDp = heightdp;
- this->orientation = orient;
- this->uiModeType = uiModeType;
- this->uiModeNight = uiModeNight;
- this->density = den;
- this->touchscreen = touch;
- this->keysHidden = keysHidden;
- this->keyboard = key;
- this->navHidden = navHidden;
- this->navigation = nav;
- this->screenSize = size;
- this->layoutDirection = layoutDir;
- this->version = vers;
-
- // what is this anyway?
- this->vendor = "";
-
+ *resType = type;
return true;
}
String8
-AaptGroupEntry::toString() const
-{
- String8 s = this->mcc;
- s += ",";
- s += this->mnc;
- s += ",";
- s += locale.toDirName();
- s += ",";
- s += layoutDirection;
- s += ",";
- s += smallestScreenWidthDp;
- s += ",";
- s += screenWidthDp;
- s += ",";
- s += screenHeightDp;
- s += ",";
- s += screenLayoutSize;
- s += ",";
- s += screenLayoutLong;
- s += ",";
- s += this->orientation;
- s += ",";
- s += uiModeType;
- s += ",";
- s += uiModeNight;
- s += ",";
- s += density;
- s += ",";
- s += touchscreen;
- s += ",";
- s += keysHidden;
- s += ",";
- s += keyboard;
- s += ",";
- s += navHidden;
- s += ",";
- s += navigation;
- s += ",";
- s += screenSize;
- s += ",";
- s += version;
- return s;
-}
-
-String8
AaptGroupEntry::toDirName(const String8& resType) const
{
String8 s = resType;
- if (this->mcc != "") {
+ String8 params = mParams.toString();
+ if (params.length() > 0) {
if (s.length() > 0) {
s += "-";
}
- s += mcc;
+ s += params;
}
- if (this->mnc != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += mnc;
- }
-
- const String8 localeComponent = locale.toDirName();
- if (localeComponent != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += localeComponent;
- }
-
- if (this->layoutDirection != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += layoutDirection;
- }
- if (this->smallestScreenWidthDp != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += smallestScreenWidthDp;
- }
- if (this->screenWidthDp != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += screenWidthDp;
- }
- if (this->screenHeightDp != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += screenHeightDp;
- }
- if (this->screenLayoutSize != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += screenLayoutSize;
- }
- if (this->screenLayoutLong != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += screenLayoutLong;
- }
- if (this->orientation != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += orientation;
- }
- if (this->uiModeType != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += uiModeType;
- }
- if (this->uiModeNight != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += uiModeNight;
- }
- if (this->density != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += density;
- }
- if (this->touchscreen != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += touchscreen;
- }
- if (this->keysHidden != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += keysHidden;
- }
- if (this->keyboard != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += keyboard;
- }
- if (this->navHidden != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += navHidden;
- }
- if (this->navigation != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += navigation;
- }
- if (this->screenSize != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += screenSize;
- }
- if (this->version != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += version;
- }
-
return s;
}
-bool AaptGroupEntry::getMccName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->mcc = 0;
- return true;
- }
- const char* c = name;
- if (tolower(*c) != 'm') return false;
- c++;
- if (tolower(*c) != 'c') return false;
- c++;
- if (tolower(*c) != 'c') return false;
- c++;
-
- const char* val = c;
-
- while (*c >= '0' && *c <= '9') {
- c++;
- }
- if (*c != 0) return false;
- if (c-val != 3) return false;
-
- int d = atoi(val);
- if (d != 0) {
- if (out) out->mcc = d;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getMncName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->mcc = 0;
- return true;
- }
- const char* c = name;
- if (tolower(*c) != 'm') return false;
- c++;
- if (tolower(*c) != 'n') return false;
- c++;
- if (tolower(*c) != 'c') return false;
- c++;
-
- const char* val = c;
-
- while (*c >= '0' && *c <= '9') {
- c++;
- }
- if (*c != 0) return false;
- if (c-val == 0 || c-val > 3) return false;
-
- if (out) {
- out->mnc = atoi(val);
- if (out->mnc == 0) {
- out->mnc = ACONFIGURATION_MNC_ZERO;
- }
- }
-
- return true;
-}
-
-bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
- | ResTable_config::LAYOUTDIR_ANY;
- return true;
- } else if (strcmp(name, "ldltr") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
- | ResTable_config::LAYOUTDIR_LTR;
- return true;
- } else if (strcmp(name, "ldrtl") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
- | ResTable_config::LAYOUTDIR_RTL;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_ANY;
- return true;
- } else if (strcmp(name, "small") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_SMALL;
- return true;
- } else if (strcmp(name, "normal") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_NORMAL;
- return true;
- } else if (strcmp(name, "large") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_LARGE;
- return true;
- } else if (strcmp(name, "xlarge") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_XLARGE;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
- | ResTable_config::SCREENLONG_ANY;
- return true;
- } else if (strcmp(name, "long") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
- | ResTable_config::SCREENLONG_YES;
- return true;
- } else if (strcmp(name, "notlong") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
- | ResTable_config::SCREENLONG_NO;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getOrientationName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->orientation = out->ORIENTATION_ANY;
- return true;
- } else if (strcmp(name, "port") == 0) {
- if (out) out->orientation = out->ORIENTATION_PORT;
- return true;
- } else if (strcmp(name, "land") == 0) {
- if (out) out->orientation = out->ORIENTATION_LAND;
- return true;
- } else if (strcmp(name, "square") == 0) {
- if (out) out->orientation = out->ORIENTATION_SQUARE;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getUiModeTypeName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_ANY;
- return true;
- } else if (strcmp(name, "desk") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_DESK;
- return true;
- } else if (strcmp(name, "car") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_CAR;
- return true;
- } else if (strcmp(name, "television") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_TELEVISION;
- return true;
- } else if (strcmp(name, "appliance") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_APPLIANCE;
- return true;
- } else if (strcmp(name, "watch") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_WATCH;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getUiModeNightName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
- | ResTable_config::UI_MODE_NIGHT_ANY;
- return true;
- } else if (strcmp(name, "night") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
- | ResTable_config::UI_MODE_NIGHT_YES;
- return true;
- } else if (strcmp(name, "notnight") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
- | ResTable_config::UI_MODE_NIGHT_NO;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getDensityName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->density = ResTable_config::DENSITY_DEFAULT;
- return true;
- }
-
- if (strcmp(name, "nodpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_NONE;
- return true;
- }
-
- if (strcmp(name, "ldpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_LOW;
- return true;
- }
-
- if (strcmp(name, "mdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_MEDIUM;
- return true;
- }
-
- if (strcmp(name, "tvdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_TV;
- return true;
- }
-
- if (strcmp(name, "hdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_HIGH;
- return true;
- }
-
- if (strcmp(name, "xhdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_XHIGH;
- return true;
- }
-
- if (strcmp(name, "xxhdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_XXHIGH;
- return true;
- }
-
- if (strcmp(name, "xxxhdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
- return true;
- }
-
- char* c = (char*)name;
- while (*c >= '0' && *c <= '9') {
- c++;
- }
-
- // check that we have 'dpi' after the last digit.
- if (toupper(c[0]) != 'D' ||
- toupper(c[1]) != 'P' ||
- toupper(c[2]) != 'I' ||
- c[3] != 0) {
- return false;
- }
-
- // temporarily replace the first letter with \0 to
- // use atoi.
- char tmp = c[0];
- c[0] = '\0';
-
- int d = atoi(name);
- c[0] = tmp;
-
- if (d != 0) {
- if (out) out->density = d;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getTouchscreenName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
- return true;
- } else if (strcmp(name, "notouch") == 0) {
- if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
- return true;
- } else if (strcmp(name, "stylus") == 0) {
- if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
- return true;
- } else if (strcmp(name, "finger") == 0) {
- if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getKeysHiddenName(const char* name,
- ResTable_config* out)
-{
- uint8_t mask = 0;
- uint8_t value = 0;
- if (strcmp(name, kWildcardName) == 0) {
- mask = ResTable_config::MASK_KEYSHIDDEN;
- value = ResTable_config::KEYSHIDDEN_ANY;
- } else if (strcmp(name, "keysexposed") == 0) {
- mask = ResTable_config::MASK_KEYSHIDDEN;
- value = ResTable_config::KEYSHIDDEN_NO;
- } else if (strcmp(name, "keyshidden") == 0) {
- mask = ResTable_config::MASK_KEYSHIDDEN;
- value = ResTable_config::KEYSHIDDEN_YES;
- } else if (strcmp(name, "keyssoft") == 0) {
- mask = ResTable_config::MASK_KEYSHIDDEN;
- value = ResTable_config::KEYSHIDDEN_SOFT;
- }
-
- if (mask != 0) {
- if (out) out->inputFlags = (out->inputFlags&~mask) | value;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getKeyboardName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->keyboard = out->KEYBOARD_ANY;
- return true;
- } else if (strcmp(name, "nokeys") == 0) {
- if (out) out->keyboard = out->KEYBOARD_NOKEYS;
- return true;
- } else if (strcmp(name, "qwerty") == 0) {
- if (out) out->keyboard = out->KEYBOARD_QWERTY;
- return true;
- } else if (strcmp(name, "12key") == 0) {
- if (out) out->keyboard = out->KEYBOARD_12KEY;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getNavHiddenName(const char* name,
- ResTable_config* out)
-{
- uint8_t mask = 0;
- uint8_t value = 0;
- if (strcmp(name, kWildcardName) == 0) {
- mask = ResTable_config::MASK_NAVHIDDEN;
- value = ResTable_config::NAVHIDDEN_ANY;
- } else if (strcmp(name, "navexposed") == 0) {
- mask = ResTable_config::MASK_NAVHIDDEN;
- value = ResTable_config::NAVHIDDEN_NO;
- } else if (strcmp(name, "navhidden") == 0) {
- mask = ResTable_config::MASK_NAVHIDDEN;
- value = ResTable_config::NAVHIDDEN_YES;
- }
-
- if (mask != 0) {
- if (out) out->inputFlags = (out->inputFlags&~mask) | value;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getNavigationName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->navigation = out->NAVIGATION_ANY;
- return true;
- } else if (strcmp(name, "nonav") == 0) {
- if (out) out->navigation = out->NAVIGATION_NONAV;
- return true;
- } else if (strcmp(name, "dpad") == 0) {
- if (out) out->navigation = out->NAVIGATION_DPAD;
- return true;
- } else if (strcmp(name, "trackball") == 0) {
- if (out) out->navigation = out->NAVIGATION_TRACKBALL;
- return true;
- } else if (strcmp(name, "wheel") == 0) {
- if (out) out->navigation = out->NAVIGATION_WHEEL;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->screenWidth = out->SCREENWIDTH_ANY;
- out->screenHeight = out->SCREENHEIGHT_ANY;
- }
- return true;
- }
-
- const char* x = name;
- while (*x >= '0' && *x <= '9') x++;
- if (x == name || *x != 'x') return false;
- String8 xName(name, x-name);
- x++;
-
- const char* y = x;
- while (*y >= '0' && *y <= '9') y++;
- if (y == name || *y != 0) return false;
- String8 yName(x, y-x);
-
- uint16_t w = (uint16_t)atoi(xName.string());
- uint16_t h = (uint16_t)atoi(yName.string());
- if (w < h) {
- return false;
- }
-
- if (out) {
- out->screenWidth = w;
- out->screenHeight = h;
- }
-
- return true;
-}
-
-bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
- }
- return true;
- }
-
- if (*name != 's') return false;
- name++;
- if (*name != 'w') return false;
- name++;
- const char* x = name;
- while (*x >= '0' && *x <= '9') x++;
- if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
- String8 xName(name, x-name);
-
- if (out) {
- out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
- }
-
- return true;
-}
-
-bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->screenWidthDp = out->SCREENWIDTH_ANY;
- }
- return true;
- }
-
- if (*name != 'w') return false;
- name++;
- const char* x = name;
- while (*x >= '0' && *x <= '9') x++;
- if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
- String8 xName(name, x-name);
-
- if (out) {
- out->screenWidthDp = (uint16_t)atoi(xName.string());
- }
-
- return true;
-}
-
-bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->screenHeightDp = out->SCREENWIDTH_ANY;
- }
- return true;
- }
-
- if (*name != 'h') return false;
- name++;
- const char* x = name;
- while (*x >= '0' && *x <= '9') x++;
- if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
- String8 xName(name, x-name);
-
- if (out) {
- out->screenHeightDp = (uint16_t)atoi(xName.string());
- }
-
- return true;
-}
-
-bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->sdkVersion = out->SDKVERSION_ANY;
- out->minorVersion = out->MINORVERSION_ANY;
- }
- return true;
- }
-
- if (*name != 'v') {
- return false;
- }
-
- name++;
- const char* s = name;
- while (*s >= '0' && *s <= '9') s++;
- if (s == name || *s != 0) return false;
- String8 sdkName(name, s-name);
-
- if (out) {
- out->sdkVersion = (uint16_t)atoi(sdkName.string());
- out->minorVersion = 0;
- }
-
- return true;
-}
-
-int AaptGroupEntry::compare(const AaptGroupEntry& o) const
-{
- int v = mcc.compare(o.mcc);
- if (v == 0) v = mnc.compare(o.mnc);
- if (v == 0) v = locale.compare(o.locale);
- if (v == 0) v = layoutDirection.compare(o.layoutDirection);
- if (v == 0) v = vendor.compare(o.vendor);
- if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
- if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
- if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
- if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
- if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
- if (v == 0) v = orientation.compare(o.orientation);
- if (v == 0) v = uiModeType.compare(o.uiModeType);
- if (v == 0) v = uiModeNight.compare(o.uiModeNight);
- if (v == 0) v = density.compare(o.density);
- if (v == 0) v = touchscreen.compare(o.touchscreen);
- if (v == 0) v = keysHidden.compare(o.keysHidden);
- if (v == 0) v = keyboard.compare(o.keyboard);
- if (v == 0) v = navHidden.compare(o.navHidden);
- if (v == 0) v = navigation.compare(o.navigation);
- if (v == 0) v = screenSize.compare(o.screenSize);
- if (v == 0) v = version.compare(o.version);
- return v;
-}
-
-const ResTable_config AaptGroupEntry::toParams() const
-{
- if (!mParamsChanged) {
- return mParams;
- }
-
- mParamsChanged = false;
- ResTable_config& params = mParams;
- memset(¶ms, 0, sizeof(ResTable_config));
- getMccName(mcc.string(), ¶ms);
- getMncName(mnc.string(), ¶ms);
- locale.writeTo(¶ms);
- getLayoutDirectionName(layoutDirection.string(), ¶ms);
- getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), ¶ms);
- getScreenWidthDpName(screenWidthDp.string(), ¶ms);
- getScreenHeightDpName(screenHeightDp.string(), ¶ms);
- getScreenLayoutSizeName(screenLayoutSize.string(), ¶ms);
- getScreenLayoutLongName(screenLayoutLong.string(), ¶ms);
- getOrientationName(orientation.string(), ¶ms);
- getUiModeTypeName(uiModeType.string(), ¶ms);
- getUiModeNightName(uiModeNight.string(), ¶ms);
- getDensityName(density.string(), ¶ms);
- getTouchscreenName(touchscreen.string(), ¶ms);
- getKeysHiddenName(keysHidden.string(), ¶ms);
- getKeyboardName(keyboard.string(), ¶ms);
- getNavHiddenName(navHidden.string(), ¶ms);
- getNavigationName(navigation.string(), ¶ms);
- getScreenSizeName(screenSize.string(), ¶ms);
- getVersionName(version.string(), ¶ms);
-
- // Fix up version number based on specified parameters.
- int minSdk = 0;
- if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
- || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
- || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
- minSdk = SDK_HONEYCOMB_MR2;
- } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
- != ResTable_config::UI_MODE_TYPE_ANY
- || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
- != ResTable_config::UI_MODE_NIGHT_ANY) {
- minSdk = SDK_FROYO;
- } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
- != ResTable_config::SCREENSIZE_ANY
- || (params.screenLayout&ResTable_config::MASK_SCREENLONG)
- != ResTable_config::SCREENLONG_ANY
- || params.density != ResTable_config::DENSITY_DEFAULT) {
- minSdk = SDK_DONUT;
- }
-
- if (minSdk > params.sdkVersion) {
- params.sdkVersion = minSdk;
- }
-
- return params;
-}
// =========================================================================
// =========================================================================
@@ -2229,9 +907,7 @@
: AaptDir(String8(), String8()),
mHavePrivateSymbols(false),
mChanged(false), mHaveIncludedAssets(false),
- mRes(NULL)
-{
-}
+ mRes(NULL) {}
const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
if (mChanged) {
@@ -2506,7 +1182,7 @@
String8 resType;
bool b = group.initFromDirName(entry->d_name, &resType);
if (!b) {
- fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
+ fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.string(),
entry->d_name);
err = -1;
continue;
@@ -2654,30 +1330,35 @@
status_t AaptAssets::filter(Bundle* bundle)
{
- ResourceFilter reqFilter;
+ WeakResourceFilter reqFilter;
status_t err = reqFilter.parse(bundle->getConfigurations());
if (err != NO_ERROR) {
return err;
}
- ResourceFilter prefFilter;
- err = prefFilter.parse(bundle->getPreferredConfigurations());
- if (err != NO_ERROR) {
- return err;
+ uint32_t preferredDensity = 0;
+ if (bundle->getPreferredDensity().size() > 0) {
+ ResTable_config preferredConfig;
+ if (!AaptConfig::parseDensity(bundle->getPreferredDensity().string(), &preferredConfig)) {
+ fprintf(stderr, "Error parsing preferred density: %s\n",
+ bundle->getPreferredDensity().string());
+ return UNKNOWN_ERROR;
+ }
+ preferredDensity = preferredConfig.density;
}
- if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
+ if (reqFilter.isEmpty() && preferredDensity == 0) {
return NO_ERROR;
}
if (bundle->getVerbose()) {
if (!reqFilter.isEmpty()) {
printf("Applying required filter: %s\n",
- bundle->getConfigurations());
+ bundle->getConfigurations().string());
}
- if (!prefFilter.isEmpty()) {
- printf("Applying preferred filter: %s\n",
- bundle->getPreferredConfigurations());
+ if (preferredDensity > 0) {
+ printf("Applying preferred density filter: %s\n",
+ bundle->getPreferredDensity().string());
}
}
@@ -2734,89 +1415,71 @@
}
// Quick check: no preferred filters, nothing more to do.
- if (prefFilter.isEmpty()) {
+ if (preferredDensity == 0) {
continue;
}
// Get the preferred density if there is one. We do not match exactly for density.
// If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
// pick xhdpi.
- uint32_t preferredDensity = 0;
- const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
- if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
- preferredDensity = (*preferredConfigs)[0].intValue;
- }
+ for (size_t k=0; k<grp->getFiles().size(); k++) {
+ sp<AaptFile> file = grp->getFiles().valueAt(k);
+ if (k == 0 && grp->getFiles().size() == 1) {
+ // If this is the only file left, we need to keep it.
+ // Otherwise the resource IDs we are using will be inconsistent
+ // with what we get when not stripping. Sucky, but at least
+ // for now we can rely on the back-end doing another filtering
+ // pass to take this out and leave us with this resource name
+ // containing no entries.
+ continue;
+ }
+ if (file->getPath().getPathExtension() == ".xml") {
+ // We can't remove .xml files at this point, because when
+ // we parse them they may add identifier resources, so
+ // removing them can cause our resource identifiers to
+ // become inconsistent.
+ continue;
+ }
+ const ResTable_config& config(file->getGroupEntry().toParams());
+ if (config.density != 0 && config.density != preferredDensity) {
+ // This is a resource we would prefer not to have. Check
+ // to see if have a similar variation that we would like
+ // to have and, if so, we can drop it.
+ uint32_t bestDensity = config.density;
- // Now deal with preferred configurations.
- for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
- for (size_t k=0; k<grp->getFiles().size(); k++) {
- sp<AaptFile> file = grp->getFiles().valueAt(k);
- if (k == 0 && grp->getFiles().size() == 1) {
- // If this is the only file left, we need to keep it.
- // Otherwise the resource IDs we are using will be inconsistent
- // with what we get when not stripping. Sucky, but at least
- // for now we can rely on the back-end doing another filtering
- // pass to take this out and leave us with this resource name
- // containing no entries.
- continue;
- }
- if (file->getPath().getPathExtension() == ".xml") {
- // We can't remove .xml files at this point, because when
- // we parse them they may add identifier resources, so
- // removing them can cause our resource identifiers to
- // become inconsistent.
- continue;
- }
- const ResTable_config& config(file->getGroupEntry().toParams());
- if (!prefFilter.match(axis, config)) {
- // This is a resource we would prefer not to have. Check
- // to see if have a similar variation that we would like
- // to have and, if so, we can drop it.
-
- uint32_t bestDensity = config.density;
-
- for (size_t m=0; m<grp->getFiles().size(); m++) {
- if (m == k) continue;
- sp<AaptFile> mfile = grp->getFiles().valueAt(m);
- const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
- if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
- if (axis == AXIS_DENSITY && preferredDensity > 0) {
- // See if there is a better density resource
- if (mconfig.density < bestDensity &&
- mconfig.density > preferredDensity &&
- bestDensity > preferredDensity) {
- // This density is between our best density and
- // the preferred density, therefore it is better.
- bestDensity = mconfig.density;
- } else if (mconfig.density > bestDensity &&
- bestDensity < preferredDensity) {
- // This density is better than our best density and
- // our best density was smaller than our preferred
- // density, so it is better.
- bestDensity = mconfig.density;
- }
- } else if (prefFilter.match(axis, mconfig)) {
- if (bundle->getVerbose()) {
- printf("Pruning unneeded resource: %s\n",
- file->getPrintableSource().string());
- }
- grp->removeFile(k);
- k--;
- break;
- }
- }
+ for (size_t m=0; m<grp->getFiles().size(); m++) {
+ if (m == k) {
+ continue;
}
- if (axis == AXIS_DENSITY && preferredDensity > 0 &&
- bestDensity != config.density) {
- if (bundle->getVerbose()) {
- printf("Pruning unneeded resource: %s\n",
- file->getPrintableSource().string());
+ sp<AaptFile> mfile = grp->getFiles().valueAt(m);
+ const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
+ if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) {
+ // See if there is a better density resource
+ if (mconfig.density < bestDensity &&
+ mconfig.density > preferredDensity &&
+ bestDensity > preferredDensity) {
+ // This density is between our best density and
+ // the preferred density, therefore it is better.
+ bestDensity = mconfig.density;
+ } else if (mconfig.density > bestDensity &&
+ bestDensity < preferredDensity) {
+ // This density is better than our best density and
+ // our best density was smaller than our preferred
+ // density, so it is better.
+ bestDensity = mconfig.density;
}
- grp->removeFile(k);
- k--;
}
}
+
+ if (bestDensity != config.density) {
+ if (bundle->getVerbose()) {
+ printf("Pruning unneeded resource: %s\n",
+ file->getPrintableSource().string());
+ }
+ grp->removeFile(k);
+ k--;
+ }
}
}
}
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 82dda5f..0c2576a 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -6,22 +6,24 @@
#ifndef __AAPT_ASSETS_H
#define __AAPT_ASSETS_H
-#include <stdlib.h>
#include <androidfw/AssetManager.h>
#include <androidfw/ResourceTypes.h>
+#include <stdlib.h>
+#include <set>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
#include <utils/SortedVector.h>
#include <utils/String8.h>
#include <utils/Vector.h>
+
+#include "AaptConfig.h"
+#include "Bundle.h"
+#include "ConfigDescription.h"
+#include "SourcePos.h"
#include "ZipFile.h"
-#include "Bundle.h"
-#include "SourcePos.h"
-
using namespace android;
-
extern const char * const gDefaultIgnoreAssets;
extern const char * gUserIgnoreAssets;
@@ -82,9 +84,6 @@
return memcmp(this, &other, sizeof(AaptLocaleValue));
}
- static void splitAndLowerCase(const char* const chars, Vector<String8>* parts,
- const char separator);
-
inline bool operator<(const AaptLocaleValue& o) const { return compare(o) < 0; }
inline bool operator<=(const AaptLocaleValue& o) const { return compare(o) <= 0; }
inline bool operator==(const AaptLocaleValue& o) const { return compare(o) == 0; }
@@ -98,31 +97,6 @@
void setVariant(const char* variant);
};
-struct AxisValue {
- // Used for all axes except AXIS_LOCALE, which is represented
- // as a AaptLocaleValue value.
- int intValue;
- AaptLocaleValue localeValue;
-
- AxisValue() : intValue(0) {
- }
-
- inline int compare(const AxisValue &other) const {
- if (intValue != other.intValue) {
- return intValue - other.intValue;
- }
-
- return localeValue.compare(other.localeValue);
- }
-
- inline bool operator<(const AxisValue& o) const { return compare(o) < 0; }
- inline bool operator<=(const AxisValue& o) const { return compare(o) <= 0; }
- inline bool operator==(const AxisValue& o) const { return compare(o) == 0; }
- inline bool operator!=(const AxisValue& o) const { return compare(o) != 0; }
- inline bool operator>=(const AxisValue& o) const { return compare(o) >= 0; }
- inline bool operator>(const AxisValue& o) const { return compare(o) > 0; }
-};
-
/**
* This structure contains a specific variation of a single file out
* of all the variations it can have that we can have.
@@ -130,23 +104,11 @@
struct AaptGroupEntry
{
public:
- AaptGroupEntry() : mParamsChanged(true) {
- memset(&mParams, 0, sizeof(ResTable_config));
- }
-
bool initFromDirName(const char* dir, String8* resType);
- static bool parseFilterNamePart(const String8& part, int* axis, AxisValue* value);
+ inline const ConfigDescription& toParams() const { return mParams; }
- static AxisValue getConfigValueForAxis(const ResTable_config& config, int axis);
-
- static bool configSameExcept(const ResTable_config& config,
- const ResTable_config& otherConfig, int axis);
-
- int compare(const AaptGroupEntry& o) const;
-
- const ResTable_config toParams() const;
-
+ inline int compare(const AaptGroupEntry& o) const { return mParams.compareLogical(o.mParams); }
inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; }
inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; }
inline bool operator==(const AaptGroupEntry& o) const { return compare(o) == 0; }
@@ -154,56 +116,13 @@
inline bool operator>=(const AaptGroupEntry& o) const { return compare(o) >= 0; }
inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; }
- String8 toString() const;
+ String8 toString() const { return mParams.toString(); }
String8 toDirName(const String8& resType) const;
- const String8& getVersionString() const { return version; }
+ const String8 getVersionString() const { return AaptConfig::getVersion(mParams); }
private:
- static bool getMccName(const char* name, ResTable_config* out = NULL);
- static bool getMncName(const char* name, ResTable_config* out = NULL);
- static bool getScreenLayoutSizeName(const char* name, ResTable_config* out = NULL);
- static bool getScreenLayoutLongName(const char* name, ResTable_config* out = NULL);
- static bool getOrientationName(const char* name, ResTable_config* out = NULL);
- static bool getUiModeTypeName(const char* name, ResTable_config* out = NULL);
- static bool getUiModeNightName(const char* name, ResTable_config* out = NULL);
- static bool getDensityName(const char* name, ResTable_config* out = NULL);
- static bool getTouchscreenName(const char* name, ResTable_config* out = NULL);
- static bool getKeysHiddenName(const char* name, ResTable_config* out = NULL);
- static bool getKeyboardName(const char* name, ResTable_config* out = NULL);
- static bool getNavigationName(const char* name, ResTable_config* out = NULL);
- static bool getNavHiddenName(const char* name, ResTable_config* out = NULL);
- static bool getScreenSizeName(const char* name, ResTable_config* out = NULL);
- static bool getSmallestScreenWidthDpName(const char* name, ResTable_config* out = NULL);
- static bool getScreenWidthDpName(const char* name, ResTable_config* out = NULL);
- static bool getScreenHeightDpName(const char* name, ResTable_config* out = NULL);
- static bool getLayoutDirectionName(const char* name, ResTable_config* out = NULL);
- static bool getVersionName(const char* name, ResTable_config* out = NULL);
-
- String8 mcc;
- String8 mnc;
- AaptLocaleValue locale;
- String8 vendor;
- String8 smallestScreenWidthDp;
- String8 screenWidthDp;
- String8 screenHeightDp;
- String8 screenLayoutSize;
- String8 screenLayoutLong;
- String8 orientation;
- String8 uiModeType;
- String8 uiModeNight;
- String8 density;
- String8 touchscreen;
- String8 keysHidden;
- String8 keyboard;
- String8 navHidden;
- String8 navigation;
- String8 screenSize;
- String8 layoutDirection;
- String8 version;
-
- mutable bool mParamsChanged;
- mutable ResTable_config mParams;
+ ConfigDescription mParams;
};
inline int compare_type(const AaptGroupEntry& lhs, const AaptGroupEntry& rhs)
diff --git a/tools/aapt/AaptConfig.cpp b/tools/aapt/AaptConfig.cpp
new file mode 100644
index 0000000..69a9c7f
--- /dev/null
+++ b/tools/aapt/AaptConfig.cpp
@@ -0,0 +1,790 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <androidfw/ResourceTypes.h>
+#include <ctype.h>
+
+#include "AaptConfig.h"
+#include "AaptAssets.h"
+#include "AaptUtil.h"
+#include "ResourceFilter.h"
+
+using android::String8;
+using android::Vector;
+using android::ResTable_config;
+
+namespace AaptConfig {
+
+static const char* kWildcardName = "any";
+
+bool parse(const String8& str, ConfigDescription* out) {
+ Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '-');
+
+ ConfigDescription config;
+ AaptLocaleValue locale;
+ ssize_t index = 0;
+ ssize_t localeIndex = 0;
+ const ssize_t N = parts.size();
+ const char* part = parts[index].string();
+
+ if (str.length() == 0) {
+ goto success;
+ }
+
+ if (parseMcc(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseMnc(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ // Locale spans a few '-' separators, so we let it
+ // control the index.
+ localeIndex = locale.initFromDirName(parts, index);
+ if (localeIndex < 0) {
+ return false;
+ } else if (localeIndex > index) {
+ locale.writeTo(&config);
+ index = localeIndex;
+ if (index >= N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseLayoutDirection(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseSmallestScreenWidthDp(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseScreenWidthDp(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseScreenHeightDp(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseScreenLayoutSize(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseScreenLayoutLong(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseOrientation(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseUiModeType(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseUiModeNight(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseDensity(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseTouchscreen(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseKeysHidden(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseKeyboard(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseNavHidden(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseNavigation(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseScreenSize(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseVersion(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ // Unrecognized.
+ return false;
+
+success:
+ if (out != NULL) {
+ applyVersionForCompatibility(&config);
+ *out = config;
+ }
+ return true;
+}
+
+bool parseCommaSeparatedList(const String8& str, std::set<ConfigDescription>* outSet) {
+ Vector<String8> parts = AaptUtil::splitAndLowerCase(str, ',');
+ const size_t N = parts.size();
+ for (size_t i = 0; i < N; i++) {
+ ConfigDescription config;
+ if (!parse(parts[i], &config)) {
+ return false;
+ }
+ outSet->insert(config);
+ }
+ return true;
+}
+
+void applyVersionForCompatibility(ConfigDescription* config) {
+ if (config == NULL) {
+ return;
+ }
+
+ uint16_t minSdk = 0;
+ if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
+ || config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY
+ || config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
+ minSdk = SDK_HONEYCOMB_MR2;
+ } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE)
+ != ResTable_config::UI_MODE_TYPE_ANY
+ || (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT)
+ != ResTable_config::UI_MODE_NIGHT_ANY) {
+ minSdk = SDK_FROYO;
+ } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE)
+ != ResTable_config::SCREENSIZE_ANY
+ || (config->screenLayout & ResTable_config::MASK_SCREENLONG)
+ != ResTable_config::SCREENLONG_ANY
+ || config->density != ResTable_config::DENSITY_DEFAULT) {
+ minSdk = SDK_DONUT;
+ }
+
+ if (minSdk > config->sdkVersion) {
+ config->sdkVersion = minSdk;
+ }
+}
+
+bool parseMcc(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->mcc = 0;
+ return true;
+ }
+ const char* c = name;
+ if (tolower(*c) != 'm') return false;
+ c++;
+ if (tolower(*c) != 'c') return false;
+ c++;
+ if (tolower(*c) != 'c') return false;
+ c++;
+
+ const char* val = c;
+
+ while (*c >= '0' && *c <= '9') {
+ c++;
+ }
+ if (*c != 0) return false;
+ if (c-val != 3) return false;
+
+ int d = atoi(val);
+ if (d != 0) {
+ if (out) out->mcc = d;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseMnc(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->mcc = 0;
+ return true;
+ }
+ const char* c = name;
+ if (tolower(*c) != 'm') return false;
+ c++;
+ if (tolower(*c) != 'n') return false;
+ c++;
+ if (tolower(*c) != 'c') return false;
+ c++;
+
+ const char* val = c;
+
+ while (*c >= '0' && *c <= '9') {
+ c++;
+ }
+ if (*c != 0) return false;
+ if (c-val == 0 || c-val > 3) return false;
+
+ if (out) {
+ out->mnc = atoi(val);
+ if (out->mnc == 0) {
+ out->mnc = ACONFIGURATION_MNC_ZERO;
+ }
+ }
+
+ return true;
+}
+
+bool parseLayoutDirection(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+ | ResTable_config::LAYOUTDIR_ANY;
+ return true;
+ } else if (strcmp(name, "ldltr") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+ | ResTable_config::LAYOUTDIR_LTR;
+ return true;
+ } else if (strcmp(name, "ldrtl") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+ | ResTable_config::LAYOUTDIR_RTL;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseScreenLayoutSize(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+ | ResTable_config::SCREENSIZE_ANY;
+ return true;
+ } else if (strcmp(name, "small") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+ | ResTable_config::SCREENSIZE_SMALL;
+ return true;
+ } else if (strcmp(name, "normal") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+ | ResTable_config::SCREENSIZE_NORMAL;
+ return true;
+ } else if (strcmp(name, "large") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+ | ResTable_config::SCREENSIZE_LARGE;
+ return true;
+ } else if (strcmp(name, "xlarge") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+ | ResTable_config::SCREENSIZE_XLARGE;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseScreenLayoutLong(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
+ | ResTable_config::SCREENLONG_ANY;
+ return true;
+ } else if (strcmp(name, "long") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
+ | ResTable_config::SCREENLONG_YES;
+ return true;
+ } else if (strcmp(name, "notlong") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
+ | ResTable_config::SCREENLONG_NO;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseOrientation(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->orientation = out->ORIENTATION_ANY;
+ return true;
+ } else if (strcmp(name, "port") == 0) {
+ if (out) out->orientation = out->ORIENTATION_PORT;
+ return true;
+ } else if (strcmp(name, "land") == 0) {
+ if (out) out->orientation = out->ORIENTATION_LAND;
+ return true;
+ } else if (strcmp(name, "square") == 0) {
+ if (out) out->orientation = out->ORIENTATION_SQUARE;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseUiModeType(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | ResTable_config::UI_MODE_TYPE_ANY;
+ return true;
+ } else if (strcmp(name, "desk") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | ResTable_config::UI_MODE_TYPE_DESK;
+ return true;
+ } else if (strcmp(name, "car") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | ResTable_config::UI_MODE_TYPE_CAR;
+ return true;
+ } else if (strcmp(name, "television") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | ResTable_config::UI_MODE_TYPE_TELEVISION;
+ return true;
+ } else if (strcmp(name, "appliance") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | ResTable_config::UI_MODE_TYPE_APPLIANCE;
+ return true;
+ } else if (strcmp(name, "watch") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | ResTable_config::UI_MODE_TYPE_WATCH;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseUiModeNight(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+ | ResTable_config::UI_MODE_NIGHT_ANY;
+ return true;
+ } else if (strcmp(name, "night") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+ | ResTable_config::UI_MODE_NIGHT_YES;
+ return true;
+ } else if (strcmp(name, "notnight") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+ | ResTable_config::UI_MODE_NIGHT_NO;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseDensity(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->density = ResTable_config::DENSITY_DEFAULT;
+ return true;
+ }
+
+ if (strcmp(name, "nodpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_NONE;
+ return true;
+ }
+
+ if (strcmp(name, "ldpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_LOW;
+ return true;
+ }
+
+ if (strcmp(name, "mdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_MEDIUM;
+ return true;
+ }
+
+ if (strcmp(name, "tvdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_TV;
+ return true;
+ }
+
+ if (strcmp(name, "hdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_HIGH;
+ return true;
+ }
+
+ if (strcmp(name, "xhdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_XHIGH;
+ return true;
+ }
+
+ if (strcmp(name, "xxhdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_XXHIGH;
+ return true;
+ }
+
+ if (strcmp(name, "xxxhdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
+ return true;
+ }
+
+ char* c = (char*)name;
+ while (*c >= '0' && *c <= '9') {
+ c++;
+ }
+
+ // check that we have 'dpi' after the last digit.
+ if (toupper(c[0]) != 'D' ||
+ toupper(c[1]) != 'P' ||
+ toupper(c[2]) != 'I' ||
+ c[3] != 0) {
+ return false;
+ }
+
+ // temporarily replace the first letter with \0 to
+ // use atoi.
+ char tmp = c[0];
+ c[0] = '\0';
+
+ int d = atoi(name);
+ c[0] = tmp;
+
+ if (d != 0) {
+ if (out) out->density = d;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseTouchscreen(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
+ return true;
+ } else if (strcmp(name, "notouch") == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
+ return true;
+ } else if (strcmp(name, "stylus") == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
+ return true;
+ } else if (strcmp(name, "finger") == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseKeysHidden(const char* name, ResTable_config* out) {
+ uint8_t mask = 0;
+ uint8_t value = 0;
+ if (strcmp(name, kWildcardName) == 0) {
+ mask = ResTable_config::MASK_KEYSHIDDEN;
+ value = ResTable_config::KEYSHIDDEN_ANY;
+ } else if (strcmp(name, "keysexposed") == 0) {
+ mask = ResTable_config::MASK_KEYSHIDDEN;
+ value = ResTable_config::KEYSHIDDEN_NO;
+ } else if (strcmp(name, "keyshidden") == 0) {
+ mask = ResTable_config::MASK_KEYSHIDDEN;
+ value = ResTable_config::KEYSHIDDEN_YES;
+ } else if (strcmp(name, "keyssoft") == 0) {
+ mask = ResTable_config::MASK_KEYSHIDDEN;
+ value = ResTable_config::KEYSHIDDEN_SOFT;
+ }
+
+ if (mask != 0) {
+ if (out) out->inputFlags = (out->inputFlags&~mask) | value;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseKeyboard(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->keyboard = out->KEYBOARD_ANY;
+ return true;
+ } else if (strcmp(name, "nokeys") == 0) {
+ if (out) out->keyboard = out->KEYBOARD_NOKEYS;
+ return true;
+ } else if (strcmp(name, "qwerty") == 0) {
+ if (out) out->keyboard = out->KEYBOARD_QWERTY;
+ return true;
+ } else if (strcmp(name, "12key") == 0) {
+ if (out) out->keyboard = out->KEYBOARD_12KEY;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseNavHidden(const char* name, ResTable_config* out) {
+ uint8_t mask = 0;
+ uint8_t value = 0;
+ if (strcmp(name, kWildcardName) == 0) {
+ mask = ResTable_config::MASK_NAVHIDDEN;
+ value = ResTable_config::NAVHIDDEN_ANY;
+ } else if (strcmp(name, "navexposed") == 0) {
+ mask = ResTable_config::MASK_NAVHIDDEN;
+ value = ResTable_config::NAVHIDDEN_NO;
+ } else if (strcmp(name, "navhidden") == 0) {
+ mask = ResTable_config::MASK_NAVHIDDEN;
+ value = ResTable_config::NAVHIDDEN_YES;
+ }
+
+ if (mask != 0) {
+ if (out) out->inputFlags = (out->inputFlags&~mask) | value;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseNavigation(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->navigation = out->NAVIGATION_ANY;
+ return true;
+ } else if (strcmp(name, "nonav") == 0) {
+ if (out) out->navigation = out->NAVIGATION_NONAV;
+ return true;
+ } else if (strcmp(name, "dpad") == 0) {
+ if (out) out->navigation = out->NAVIGATION_DPAD;
+ return true;
+ } else if (strcmp(name, "trackball") == 0) {
+ if (out) out->navigation = out->NAVIGATION_TRACKBALL;
+ return true;
+ } else if (strcmp(name, "wheel") == 0) {
+ if (out) out->navigation = out->NAVIGATION_WHEEL;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseScreenSize(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) {
+ out->screenWidth = out->SCREENWIDTH_ANY;
+ out->screenHeight = out->SCREENHEIGHT_ANY;
+ }
+ return true;
+ }
+
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || *x != 'x') return false;
+ String8 xName(name, x-name);
+ x++;
+
+ const char* y = x;
+ while (*y >= '0' && *y <= '9') y++;
+ if (y == name || *y != 0) return false;
+ String8 yName(x, y-x);
+
+ uint16_t w = (uint16_t)atoi(xName.string());
+ uint16_t h = (uint16_t)atoi(yName.string());
+ if (w < h) {
+ return false;
+ }
+
+ if (out) {
+ out->screenWidth = w;
+ out->screenHeight = h;
+ }
+
+ return true;
+}
+
+bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) {
+ out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
+ }
+ return true;
+ }
+
+ if (*name != 's') return false;
+ name++;
+ if (*name != 'w') return false;
+ name++;
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+ String8 xName(name, x-name);
+
+ if (out) {
+ out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
+ }
+
+ return true;
+}
+
+bool parseScreenWidthDp(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) {
+ out->screenWidthDp = out->SCREENWIDTH_ANY;
+ }
+ return true;
+ }
+
+ if (*name != 'w') return false;
+ name++;
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+ String8 xName(name, x-name);
+
+ if (out) {
+ out->screenWidthDp = (uint16_t)atoi(xName.string());
+ }
+
+ return true;
+}
+
+bool parseScreenHeightDp(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) {
+ out->screenHeightDp = out->SCREENWIDTH_ANY;
+ }
+ return true;
+ }
+
+ if (*name != 'h') return false;
+ name++;
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+ String8 xName(name, x-name);
+
+ if (out) {
+ out->screenHeightDp = (uint16_t)atoi(xName.string());
+ }
+
+ return true;
+}
+
+bool parseVersion(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) {
+ out->sdkVersion = out->SDKVERSION_ANY;
+ out->minorVersion = out->MINORVERSION_ANY;
+ }
+ return true;
+ }
+
+ if (*name != 'v') {
+ return false;
+ }
+
+ name++;
+ const char* s = name;
+ while (*s >= '0' && *s <= '9') s++;
+ if (s == name || *s != 0) return false;
+ String8 sdkName(name, s-name);
+
+ if (out) {
+ out->sdkVersion = (uint16_t)atoi(sdkName.string());
+ out->minorVersion = 0;
+ }
+
+ return true;
+}
+
+String8 getVersion(const ResTable_config& config) {
+ return String8::format("v%u", config.sdkVersion);
+}
+
+bool isSameExcept(const ResTable_config& a, const ResTable_config& b, int axisMask) {
+ return a.diff(b) == axisMask;
+}
+
+} // namespace AaptConfig
diff --git a/tools/aapt/AaptConfig.h b/tools/aapt/AaptConfig.h
new file mode 100644
index 0000000..2963539
--- /dev/null
+++ b/tools/aapt/AaptConfig.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AAPT_CONFIG_H
+#define __AAPT_CONFIG_H
+
+#include <set>
+#include <utils/String8.h>
+
+#include "ConfigDescription.h"
+
+/**
+ * Utility methods for dealing with configurations.
+ */
+namespace AaptConfig {
+
+/**
+ * Parse a string of the form 'fr-sw600dp-land' and fill in the
+ * given ResTable_config with resulting configuration parameters.
+ *
+ * The resulting configuration has the appropriate sdkVersion defined
+ * for backwards compatibility.
+ */
+bool parse(const android::String8& str, ConfigDescription* out = NULL);
+
+/**
+ * Parse a comma separated list of configuration strings. Duplicate configurations
+ * will be removed.
+ *
+ * Example input: "fr,de-land,fr-sw600dp-land"
+ */
+bool parseCommaSeparatedList(const android::String8& str, std::set<ConfigDescription>* outSet);
+
+/**
+ * If the configuration uses an axis that was added after
+ * the original Android release, make sure the SDK version
+ * is set accordingly.
+ */
+void applyVersionForCompatibility(ConfigDescription* config);
+
+// Individual axis
+bool parseMcc(const char* str, android::ResTable_config* out = NULL);
+bool parseMnc(const char* str, android::ResTable_config* out = NULL);
+bool parseLayoutDirection(const char* str, android::ResTable_config* out = NULL);
+bool parseSmallestScreenWidthDp(const char* str, android::ResTable_config* out = NULL);
+bool parseScreenWidthDp(const char* str, android::ResTable_config* out = NULL);
+bool parseScreenHeightDp(const char* str, android::ResTable_config* out = NULL);
+bool parseScreenLayoutSize(const char* str, android::ResTable_config* out = NULL);
+bool parseScreenLayoutLong(const char* str, android::ResTable_config* out = NULL);
+bool parseOrientation(const char* str, android::ResTable_config* out = NULL);
+bool parseUiModeType(const char* str, android::ResTable_config* out = NULL);
+bool parseUiModeNight(const char* str, android::ResTable_config* out = NULL);
+bool parseDensity(const char* str, android::ResTable_config* out = NULL);
+bool parseTouchscreen(const char* str, android::ResTable_config* out = NULL);
+bool parseKeysHidden(const char* str, android::ResTable_config* out = NULL);
+bool parseKeyboard(const char* str, android::ResTable_config* out = NULL);
+bool parseNavHidden(const char* str, android::ResTable_config* out = NULL);
+bool parseNavigation(const char* str, android::ResTable_config* out = NULL);
+bool parseScreenSize(const char* str, android::ResTable_config* out = NULL);
+bool parseVersion(const char* str, android::ResTable_config* out = NULL);
+
+android::String8 getVersion(const android::ResTable_config& config);
+
+/**
+ * Returns true if the two configurations only differ by the specified axis.
+ * The axis mask is a bitmask of CONFIG_* constants.
+ */
+bool isSameExcept(const android::ResTable_config& a, const android::ResTable_config& b, int configMask);
+
+} // namespace AaptConfig
+
+#endif // __AAPT_CONFIG_H
diff --git a/tools/aapt/AaptUtil.cpp b/tools/aapt/AaptUtil.cpp
new file mode 100644
index 0000000..293e144
--- /dev/null
+++ b/tools/aapt/AaptUtil.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AaptUtil.h"
+
+using android::Vector;
+using android::String8;
+
+namespace AaptUtil {
+
+Vector<String8> split(const String8& str, const char sep) {
+ Vector<String8> parts;
+ const char* p = str.string();
+ const char* q;
+
+ while (true) {
+ q = strchr(p, sep);
+ if (q == NULL) {
+ parts.add(String8(p, strlen(p)));
+ return parts;
+ }
+
+ parts.add(String8(p, q-p));
+ p = q + 1;
+ }
+ return parts;
+}
+
+Vector<String8> splitAndLowerCase(const String8& str, const char sep) {
+ Vector<String8> parts;
+ const char* p = str.string();
+ const char* q;
+
+ while (true) {
+ q = strchr(p, sep);
+ if (q == NULL) {
+ String8 val(p, strlen(p));
+ val.toLower();
+ parts.add(val);
+ return parts;
+ }
+
+ String8 val(p, q-p);
+ val.toLower();
+ parts.add(val);
+ p = q + 1;
+ }
+ return parts;
+}
+
+} // namespace AaptUtil
diff --git a/tools/aapt/AaptUtil.h b/tools/aapt/AaptUtil.h
new file mode 100644
index 0000000..47a704a
--- /dev/null
+++ b/tools/aapt/AaptUtil.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AAPT_UTIL_H
+#define __AAPT_UTIL_H
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+namespace AaptUtil {
+
+android::Vector<android::String8> split(const android::String8& str, const char sep);
+android::Vector<android::String8> splitAndLowerCase(const android::String8& str, const char sep);
+
+} // namespace AaptUtil
+
+#endif // __AAPT_UTIL_H
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 806f8ff..700afa1 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -1,104 +1,168 @@
-#
-# Copyright 2006 The Android Open Source Project
#
-# Android Asset Packaging Tool
+# 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.
#
# This tool is prebuilt if we're doing an app-only build.
ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
+# ==========================================================
+# Setup some common variables for the different build
+# targets here.
+# ==========================================================
+LOCAL_PATH:= $(call my-dir)
-aapt_src_files := \
- AaptAssets.cpp \
- Command.cpp \
- CrunchCache.cpp \
- FileFinder.cpp \
- Main.cpp \
- Package.cpp \
- StringPool.cpp \
- XMLNode.cpp \
- ResourceFilter.cpp \
- ResourceIdCache.cpp \
- ResourceTable.cpp \
- Images.cpp \
- Resource.cpp \
+aaptMain := Main.cpp
+aaptSources := \
+ AaptAssets.cpp \
+ AaptConfig.cpp \
+ AaptUtil.cpp \
+ ApkBuilder.cpp \
+ Command.cpp \
+ CrunchCache.cpp \
+ FileFinder.cpp \
+ Package.cpp \
+ StringPool.cpp \
+ XMLNode.cpp \
+ ResourceFilter.cpp \
+ ResourceIdCache.cpp \
+ ResourceTable.cpp \
+ Images.cpp \
+ Resource.cpp \
pseudolocalize.cpp \
SourcePos.cpp \
- WorkQueue.cpp \
+ WorkQueue.cpp \
ZipEntry.cpp \
ZipFile.cpp \
- qsort_r_compat.c
+ qsort_r_compat.c
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
+aaptTests := \
+ tests/AaptConfig_test.cpp \
+ tests/AaptGroupEntry_test.cpp \
+ tests/ResourceFilter_test.cpp
-LOCAL_SRC_FILES := $(aapt_src_files)
+aaptCIncludes := \
+ external/libpng \
+ external/zlib
-LOCAL_CFLAGS += -Wno-format-y2k
-ifeq (darwin,$(HOST_OS))
-LOCAL_CFLAGS += -D_DARWIN_UNLIMITED_STREAMS
-endif
-
-LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
-
-LOCAL_C_INCLUDES += external/libpng
-LOCAL_C_INCLUDES += external/zlib
-
-LOCAL_STATIC_LIBRARIES := \
- libandroidfw \
- libutils \
- libcutils \
- libexpat \
- libpng \
- liblog \
- libziparchive-host
+aaptHostLdLibs :=
+aaptHostStaticLibs := \
+ libandroidfw \
+ libpng \
+ liblog \
+ libutils \
+ libcutils \
+ libexpat \
+ libziparchive-host
ifeq ($(HOST_OS),linux)
-LOCAL_LDLIBS += -lrt -ldl -lpthread
+ aaptHostLdLibs += -lrt -ldl -lpthread
endif
# Statically link libz for MinGW (Win SDK under Linux),
# and dynamically link for all others.
ifneq ($(strip $(USE_MINGW)),)
- LOCAL_STATIC_LIBRARIES += libz
+ aaptHostStaticLibs += libz
else
- LOCAL_LDLIBS += -lz
+ aaptHostLdLibs += -lz
endif
+
+# ==========================================================
+# Build the host static library: libaapt
+# ==========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libaapt
+
+LOCAL_SRC_FILES := $(aaptSources)
+LOCAL_C_INCLUDES += $(aaptCIncludes)
+
+LOCAL_CFLAGS += -Wno-format-y2k
+LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
+ifeq (darwin,$(HOST_OS))
+LOCAL_CFLAGS += -D_DARWIN_UNLIMITED_STREAMS
+endif
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+# ==========================================================
+# Build the host executable: aapt
+# ==========================================================
+include $(CLEAR_VARS)
+
LOCAL_MODULE := aapt
+LOCAL_SRC_FILES := $(aaptMain)
+
+LOCAL_STATIC_LIBRARIES += \
+ libaapt \
+ $(aaptHostStaticLibs)
+LOCAL_LDLIBS += $(aaptHostLdLibs)
+
include $(BUILD_HOST_EXECUTABLE)
-# aapt for running on the device
-# =========================================================
+
+# ==========================================================
+# Build the host tests: libaapt_tests
+# ==========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libaapt_tests
+
+LOCAL_SRC_FILES += $(aaptTests)
+LOCAL_C_INCLUDES += $(LOCAL_PATH)
+
+LOCAL_STATIC_LIBRARIES += \
+ libaapt \
+ $(aaptHostStaticLibs)
+LOCAL_LDLIBS += $(aaptHostLdLibs)
+
+include $(BUILD_HOST_NATIVE_TEST)
+
+
+# ==========================================================
+# Build the device executable: aapt
+# ==========================================================
ifneq ($(SDK_ONLY),true)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(aapt_src_files)
-
LOCAL_MODULE := aapt
-LOCAL_C_INCLUDES += bionic
-LOCAL_C_INCLUDES += bionic/libstdc++/include
-LOCAL_C_INCLUDES += external/stlport/stlport
-LOCAL_C_INCLUDES += external/libpng
-LOCAL_C_INCLUDES += external/zlib
-
-LOCAL_CFLAGS += -Wno-non-virtual-dtor
+LOCAL_SRC_FILES := $(aaptSources) $(aaptMain)
+LOCAL_C_INCLUDES += \
+ $(aaptCIncludes) \
+ bionic \
+ external/stlport/stlport
LOCAL_SHARED_LIBRARIES := \
- libandroidfw \
- libutils \
- libcutils \
- libpng \
- liblog \
- libz
+ libandroidfw \
+ libutils \
+ libcutils \
+ libpng \
+ liblog \
+ libz
LOCAL_STATIC_LIBRARIES := \
- libstlport_static \
- libexpat_static
+ libstlport_static \
+ libexpat_static
+
+LOCAL_CPPFLAGS += -Wno-non-virtual-dtor
include $(BUILD_EXECUTABLE)
-endif
+
+endif # Not SDK_ONLY
endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK
diff --git a/tools/aapt/ApkBuilder.cpp b/tools/aapt/ApkBuilder.cpp
new file mode 100644
index 0000000..12f6040
--- /dev/null
+++ b/tools/aapt/ApkBuilder.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AaptAssets.h"
+#include "ApkBuilder.h"
+
+using namespace android;
+
+ApkBuilder::ApkBuilder(const sp<WeakResourceFilter>& configFilter)
+ : mConfigFilter(configFilter)
+ , mDefaultFilter(new AndResourceFilter()) {
+ // Add the default split, which is present for all APKs.
+ mDefaultFilter->addFilter(mConfigFilter);
+ mSplits.add(new ApkSplit(std::set<ConfigDescription>(), mDefaultFilter, true));
+}
+
+status_t ApkBuilder::createSplitForConfigs(const std::set<ConfigDescription>& configs) {
+ const size_t N = mSplits.size();
+ for (size_t i = 0; i < N; i++) {
+ const std::set<ConfigDescription>& splitConfigs = mSplits[i]->getConfigs();
+ std::set<ConfigDescription>::const_iterator iter = configs.begin();
+ for (; iter != configs.end(); iter++) {
+ if (splitConfigs.count(*iter) > 0) {
+ // Can't have overlapping configurations.
+ fprintf(stderr, "ERROR: Split configuration '%s' is already defined "
+ "in another split.\n", iter->toString().string());
+ return ALREADY_EXISTS;
+ }
+ }
+ }
+
+ sp<StrongResourceFilter> splitFilter = new StrongResourceFilter(configs);
+
+ // Add the inverse filter of this split filter to the base apk filter so it will
+ // omit resources that belong in this split.
+ mDefaultFilter->addFilter(new InverseResourceFilter(splitFilter));
+
+ // Now add the apk-wide config filter to our split filter.
+ sp<AndResourceFilter> filter = new AndResourceFilter();
+ filter->addFilter(splitFilter);
+ filter->addFilter(mConfigFilter);
+ mSplits.add(new ApkSplit(configs, filter));
+ return NO_ERROR;
+}
+
+status_t ApkBuilder::addEntry(const String8& path, const sp<AaptFile>& file) {
+ const size_t N = mSplits.size();
+ for (size_t i = 0; i < N; i++) {
+ if (mSplits[i]->matches(file)) {
+ return mSplits.editItemAt(i)->addEntry(path, file);
+ }
+ }
+ // Entry can be dropped if it doesn't match any split. This will only happen
+ // if the enry doesn't mConfigFilter.
+ return NO_ERROR;
+}
+
+void ApkBuilder::print() const {
+ fprintf(stderr, "APK Builder\n");
+ fprintf(stderr, "-----------\n");
+ const size_t N = mSplits.size();
+ for (size_t i = 0; i < N; i++) {
+ mSplits[i]->print();
+ fprintf(stderr, "\n");
+ }
+}
+
+ApkSplit::ApkSplit(const std::set<ConfigDescription>& configs, const sp<ResourceFilter>& filter, bool isBase)
+ : mConfigs(configs), mFilter(filter), mIsBase(isBase) {
+ std::set<ConfigDescription>::const_iterator iter = configs.begin();
+ for (; iter != configs.end(); iter++) {
+ if (mName.size() > 0) {
+ mName.append(",");
+ mDirName.append("_");
+ }
+
+ String8 configStr = iter->toString();
+ mName.append(configStr);
+ mDirName.append(configStr);
+ }
+}
+
+status_t ApkSplit::addEntry(const String8& path, const sp<AaptFile>& file) {
+ if (!mFiles.insert(OutputEntry(path, file)).second) {
+ // Duplicate file.
+ return ALREADY_EXISTS;
+ }
+ return NO_ERROR;
+}
+
+void ApkSplit::print() const {
+ fprintf(stderr, "APK Split '%s'\n", mName.string());
+
+ std::set<OutputEntry>::const_iterator iter = mFiles.begin();
+ for (; iter != mFiles.end(); iter++) {
+ fprintf(stderr, " %s (%s)\n", iter->getPath().string(), iter->getFile()->getSourceFile().string());
+ }
+}
diff --git a/tools/aapt/ApkBuilder.h b/tools/aapt/ApkBuilder.h
new file mode 100644
index 0000000..a4b7d4a
--- /dev/null
+++ b/tools/aapt/ApkBuilder.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __APK_BUILDER_H
+#define __APK_BUILDER_H
+
+#include <set>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
+#include "ConfigDescription.h"
+#include "OutputSet.h"
+#include "ResourceFilter.h"
+
+class ApkSplit;
+class AaptFile;
+
+class ApkBuilder : public android::RefBase {
+public:
+ ApkBuilder(const sp<WeakResourceFilter>& configFilter);
+
+ /**
+ * Tells the builder to generate a separate APK for resources that
+ * match the configurations specified. Split APKs can not have
+ * overlapping resources.
+ *
+ * NOTE: All splits should be set up before any files are added.
+ */
+ android::status_t createSplitForConfigs(const std::set<ConfigDescription>& configs);
+
+ /**
+ * Adds a file to be written to the final APK. It's name must not collide
+ * with that of any files previously added. When a Split APK is being
+ * generated, duplicates can exist as long as they are in different splits
+ * (resources.arsc, AndroidManifest.xml).
+ */
+ android::status_t addEntry(const String8& path, const android::sp<AaptFile>& file);
+
+ android::Vector<sp<ApkSplit> >& getSplits() {
+ return mSplits;
+ }
+
+ void print() const;
+
+private:
+ android::sp<ResourceFilter> mConfigFilter;
+ android::sp<AndResourceFilter> mDefaultFilter;
+ android::Vector<sp<ApkSplit> > mSplits;
+};
+
+class ApkSplit : public OutputSet {
+public:
+ android::status_t addEntry(const String8& path, const android::sp<AaptFile>& file);
+
+ const std::set<OutputEntry>& getEntries() const {
+ return mFiles;
+ }
+
+ const std::set<ConfigDescription>& getConfigs() const {
+ return mConfigs;
+ }
+
+ bool matches(const sp<AaptFile>& file) const {
+ return mFilter->match(file->getGroupEntry().toParams());
+ }
+
+ sp<ResourceFilter> getResourceFilter() const {
+ return mFilter;
+ }
+
+ const android::String8& getPrintableName() const {
+ return mName;
+ }
+
+ const android::String8& getDirectorySafeName() const {
+ return mDirName;
+ }
+
+ bool isBase() const {
+ return mIsBase;
+ }
+
+ void print() const;
+
+private:
+ friend class ApkBuilder;
+
+ ApkSplit(const std::set<ConfigDescription>& configs, const android::sp<ResourceFilter>& filter, bool isBase=false);
+
+ std::set<ConfigDescription> mConfigs;
+ const sp<ResourceFilter> mFilter;
+ const bool mIsBase;
+ String8 mName;
+ String8 mDirName;
+ std::set<OutputEntry> mFiles;
+};
+
+#endif // __APK_BUILDER_H
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index ebe1bed..ceb52a0 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -151,10 +151,12 @@
void setPublicOutputFile(const char* file) { mPublicOutputFile = file; }
const char* getRClassDir() const { return mRClassDir; }
void setRClassDir(const char* dir) { mRClassDir = dir; }
- const char* getConfigurations() const { return mConfigurations.size() > 0 ? mConfigurations.string() : NULL; }
+ const android::String8& getConfigurations() const { return mConfigurations; }
void addConfigurations(const char* val) { if (mConfigurations.size() > 0) { mConfigurations.append(","); mConfigurations.append(val); } else { mConfigurations = val; } }
- const char* getPreferredConfigurations() const { return mPreferredConfigurations.size() > 0 ? mPreferredConfigurations.string() : NULL; }
- void addPreferredConfigurations(const char* val) { if (mPreferredConfigurations.size() > 0) { mPreferredConfigurations.append(","); mPreferredConfigurations.append(val); } else { mPreferredConfigurations = val; } }
+ const android::String8& getPreferredDensity() const { return mPreferredDensity; }
+ void setPreferredDensity(const char* val) { mPreferredDensity = val; }
+ void addSplitConfigurations(const char* val) { mPartialConfigurations.add(android::String8(val)); }
+ const android::Vector<android::String8>& getSplitConfigurations() const { return mPartialConfigurations; }
const char* getResourceIntermediatesDir() const { return mResourceIntermediatesDir; }
void setResourceIntermediatesDir(const char* dir) { mResourceIntermediatesDir = dir; }
const android::Vector<const char*>& getPackageIncludes() const { return mPackageIncludes; }
@@ -286,7 +288,8 @@
const char* mRClassDir;
const char* mResourceIntermediatesDir;
android::String8 mConfigurations;
- android::String8 mPreferredConfigurations;
+ android::String8 mPreferredDensity;
+ android::Vector<android::String8> mPartialConfigurations;
android::Vector<const char*> mPackageIncludes;
android::Vector<const char*> mJarFiles;
android::Vector<const char*> mNoCompressExtensions;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 0af1ce1..0360200 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -3,6 +3,7 @@
//
// Android Asset Packaging Tool main entry point.
//
+#include "ApkBuilder.h"
#include "Main.h"
#include "Bundle.h"
#include "ResourceFilter.h"
@@ -2034,6 +2035,47 @@
return (result != NO_ERROR);
}
+static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder) {
+ const size_t numDirs = dir->getDirs().size();
+ for (size_t i = 0; i < numDirs; i++) {
+ status_t err = addResourcesToBuilder(dir->getDirs().valueAt(i), builder);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ const size_t numFiles = dir->getFiles().size();
+ for (size_t i = 0; i < numFiles; i++) {
+ sp<AaptGroup> gp = dir->getFiles().valueAt(i);
+ const size_t numConfigs = gp->getFiles().size();
+ for (size_t j = 0; j < numConfigs; j++) {
+ status_t err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
+ if (err != NO_ERROR) {
+ fprintf(stderr, "Failed to add %s (%s) to builder.\n",
+ gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
+ return err;
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
+ if (split->isBase()) {
+ return original;
+ }
+
+ String8 ext(original.getPathExtension());
+ if (ext == String8(".apk")) {
+ return String8::format("%s_%s%s",
+ original.getBasePath().string(),
+ split->getDirectorySafeName().string(),
+ ext.string());
+ }
+
+ return String8::format("%s_%s", original.string(),
+ split->getDirectorySafeName().string());
+}
/*
* Package up an asset directory and associated application files.
@@ -2047,17 +2089,18 @@
int N;
FILE* fp;
String8 dependencyFile;
+ sp<ApkBuilder> builder;
// -c en_XA or/and ar_XB means do pseudolocalization
- ResourceFilter filter;
- err = filter.parse(bundle->getConfigurations());
+ sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
+ err = configFilter->parse(bundle->getConfigurations());
if (err != NO_ERROR) {
goto bail;
}
- if (filter.containsPseudo()) {
+ if (configFilter->containsPseudo()) {
bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
}
- if (filter.containsPseudoBidi()) {
+ if (configFilter->containsPseudoBidi()) {
bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
}
@@ -2105,9 +2148,32 @@
assets->print(String8());
}
+ // Create the ApkBuilder, which will collect the compiled files
+ // to write to the final APK (or sets of APKs if we are building
+ // a Split APK.
+ builder = new ApkBuilder(configFilter);
+
+ // If we are generating a Split APK, find out which configurations to split on.
+ if (bundle->getSplitConfigurations().size() > 0) {
+ const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
+ const size_t numSplits = splitStrs.size();
+ for (size_t i = 0; i < numSplits; i++) {
+ std::set<ConfigDescription> configs;
+ if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
+ fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
+ goto bail;
+ }
+
+ err = builder->createSplitForConfigs(configs);
+ if (err != NO_ERROR) {
+ goto bail;
+ }
+ }
+ }
+
// If they asked for any fileAs that need to be compiled, do so.
if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
- err = buildResources(bundle, assets);
+ err = buildResources(bundle, assets, builder);
if (err != 0) {
goto bail;
}
@@ -2194,11 +2260,24 @@
// Write the apk
if (outputAPKFile) {
- err = writeAPK(bundle, assets, String8(outputAPKFile));
+ // Gather all resources and add them to the APK Builder. The builder will then
+ // figure out which Split they belong in.
+ err = addResourcesToBuilder(assets, builder);
if (err != NO_ERROR) {
- fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
goto bail;
}
+
+ const Vector<sp<ApkSplit> >& splits = builder->getSplits();
+ const size_t numSplits = splits.size();
+ for (size_t i = 0; i < numSplits; i++) {
+ const sp<ApkSplit>& split = splits[i];
+ String8 outputPath = buildApkName(String8(outputAPKFile), split);
+ err = writeAPK(bundle, outputPath, split);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
+ goto bail;
+ }
+ }
}
// If we've been asked to generate a dependency file, we need to finish up here.
diff --git a/tools/aapt/ConfigDescription.h b/tools/aapt/ConfigDescription.h
new file mode 100644
index 0000000..779c423
--- /dev/null
+++ b/tools/aapt/ConfigDescription.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONFIG_DESCRIPTION_H
+#define __CONFIG_DESCRIPTION_H
+
+#include <androidfw/ResourceTypes.h>
+
+/**
+ * Subclass of ResTable_config that adds convenient
+ * initialization and comparison methods.
+ */
+struct ConfigDescription : public android::ResTable_config {
+ ConfigDescription() {
+ memset(this, 0, sizeof(*this));
+ size = sizeof(android::ResTable_config);
+ }
+ ConfigDescription(const android::ResTable_config&o) {
+ *static_cast<android::ResTable_config*>(this) = o;
+ size = sizeof(android::ResTable_config);
+ }
+ ConfigDescription(const ConfigDescription&o) {
+ *static_cast<android::ResTable_config*>(this) = o;
+ }
+
+ ConfigDescription& operator=(const android::ResTable_config& o) {
+ *static_cast<android::ResTable_config*>(this) = o;
+ size = sizeof(android::ResTable_config);
+ return *this;
+ }
+ ConfigDescription& operator=(const ConfigDescription& o) {
+ *static_cast<android::ResTable_config*>(this) = o;
+ return *this;
+ }
+
+ inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; }
+ inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; }
+ inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; }
+ inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; }
+ inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; }
+ inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; }
+};
+
+#endif // __CONFIG_DESCRIPTION_H
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 1cf4783..5a60014 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -70,6 +70,7 @@
" [-F apk-file] [-J R-file-dir] \\\n"
" [--product product1,product2,...] \\\n"
" [-c CONFIGS] [--preferred-configurations CONFIGS] \\\n"
+ " [--split CONFIGS [--split CONFIGS]] \\\n"
" [raw-files-dir [raw-files-dir] ...] \\\n"
" [--output-text-symbols DIR]\n"
"\n"
@@ -166,10 +167,12 @@
" generate dependency files in the same directories for R.java and resource package\n"
" --auto-add-overlay\n"
" Automatically add resources that are only in overlays.\n"
- " --preferred-configurations\n"
- " Like the -c option for filtering out unneeded configurations, but\n"
- " only expresses a preference. If there is no resource available with\n"
- " the preferred configuration then it will not be stripped.\n"
+ " --preferred-density\n"
+ " Specifies a preference for a particular density. Resources that do not\n"
+ " match this density and have variants that are a closer match are removed.\n"
+ " --split\n"
+ " Builds a separate split APK for the configurations listed. This can\n"
+ " be loaded alongside the base APK at runtime.\n"
" --rename-manifest-package\n"
" Rewrite the manifest so that its package name is the package name\n"
" given here. Relative class names (for example .Foo) will be\n"
@@ -568,15 +571,24 @@
bundle.setGenDependencies(true);
} else if (strcmp(cp, "-utf16") == 0) {
bundle.setWantUTF16(true);
- } else if (strcmp(cp, "-preferred-configurations") == 0) {
+ } else if (strcmp(cp, "-preferred-density") == 0) {
argc--;
argv++;
if (!argc) {
- fprintf(stderr, "ERROR: No argument supplied for '--preferred-configurations' option\n");
+ fprintf(stderr, "ERROR: No argument supplied for '--preferred-density' option\n");
wantUsage = true;
goto bail;
}
- bundle.addPreferredConfigurations(argv[0]);
+ bundle.setPreferredDensity(argv[0]);
+ } else if (strcmp(cp, "-split") == 0) {
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '--split' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ bundle.addSplitConfigurations(argv[0]);
} else if (strcmp(cp, "-rename-manifest-package") == 0) {
argc--;
argv++;
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index a6b39ac..34c4496 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -10,8 +10,12 @@
#include <utils/threads.h>
#include <utils/List.h>
#include <utils/Errors.h>
-#include "Bundle.h"
+#include <utils/StrongPointer.h>
+
#include "AaptAssets.h"
+#include "ApkBuilder.h"
+#include "Bundle.h"
+#include "ResourceFilter.h"
#include "ZipFile.h"
@@ -22,6 +26,8 @@
#include <time.h>
#endif /* BENCHMARK */
+class OutputSet;
+
extern int doVersion(Bundle* bundle);
extern int doList(Bundle* bundle);
extern int doDump(Bundle* bundle);
@@ -34,13 +40,13 @@
extern int calcPercent(long uncompressedLen, long compressedLen);
extern android::status_t writeAPK(Bundle* bundle,
- const sp<AaptAssets>& assets,
- const android::String8& outputFile);
+ const android::String8& outputFile,
+ const android::sp<OutputSet>& outputSet);
extern android::status_t updatePreProcessedCache(Bundle* bundle);
extern android::status_t buildResources(Bundle* bundle,
- const sp<AaptAssets>& assets);
+ const sp<AaptAssets>& assets, sp<ApkBuilder>& builder);
extern android::status_t writeResourceSymbols(Bundle* bundle,
const sp<AaptAssets>& assets, const String8& pkgName, bool includePrivate);
@@ -49,8 +55,6 @@
extern bool isValidResourceType(const String8& type);
-ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets);
-
extern status_t filterResources(Bundle* bundle, const sp<AaptAssets>& assets);
int dumpResources(Bundle* bundle);
diff --git a/tools/aapt/OutputSet.h b/tools/aapt/OutputSet.h
new file mode 100644
index 0000000..ea9ef70
--- /dev/null
+++ b/tools/aapt/OutputSet.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OUTPUT_SET_H
+#define __OUTPUT_SET_H
+
+#include <set>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+
+class AaptFile;
+
+class OutputEntry {
+public:
+ OutputEntry() {}
+ OutputEntry(const android::String8& path, const android::sp<const AaptFile>& file)
+ : mPath(path), mFile(file) {}
+
+ inline const android::sp<const AaptFile>& getFile() const {
+ return mFile;
+ }
+
+ inline const android::String8& getPath() const {
+ return mPath;
+ }
+
+ bool operator<(const OutputEntry& o) const { return getPath() < o.mPath; }
+ bool operator==(const OutputEntry& o) const { return getPath() == o.mPath; }
+
+private:
+ android::String8 mPath;
+ android::sp<const AaptFile> mFile;
+};
+
+class OutputSet : public virtual android::RefBase {
+public:
+ virtual const std::set<OutputEntry>& getEntries() const = 0;
+
+ virtual ~OutputSet() {}
+};
+
+#endif // __OUTPUT_SET_H
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index 872d95c..dc16e35 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -5,6 +5,7 @@
//
#include "Main.h"
#include "AaptAssets.h"
+#include "OutputSet.h"
#include "ResourceTable.h"
#include "ResourceFilter.h"
@@ -36,11 +37,8 @@
};
/* fwd decls, so I can write this downward */
-ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets);
-ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
- const AaptGroupEntry& ge, const ResourceFilter* filter);
-bool processFile(Bundle* bundle, ZipFile* zip,
- const sp<AaptGroup>& group, const sp<AaptFile>& file);
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet);
+bool processFile(Bundle* bundle, ZipFile* zip, String8 storageName, const sp<const AaptFile>& file);
bool okayToCompress(Bundle* bundle, const String8& pathName);
ssize_t processJarFiles(Bundle* bundle, ZipFile* zip);
@@ -51,8 +49,7 @@
* On success, "bundle->numPackages" will be the number of Zip packages
* we created.
*/
-status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets,
- const String8& outputFile)
+status_t writeAPK(Bundle* bundle, const String8& outputFile, const sp<OutputSet>& outputSet)
{
#if BENCHMARK
fprintf(stdout, "BENCHMARK: Starting APK Bundling \n");
@@ -112,7 +109,7 @@
printf("Writing all files...\n");
}
- count = processAssets(bundle, zip, assets);
+ count = processAssets(bundle, zip, outputSet);
if (count < 0) {
fprintf(stderr, "ERROR: unable to process assets while packaging '%s'\n",
outputFile.string());
@@ -218,72 +215,24 @@
return result;
}
-ssize_t processAssets(Bundle* bundle, ZipFile* zip,
- const sp<AaptAssets>& assets)
-{
- ResourceFilter filter;
- status_t status = filter.parse(bundle->getConfigurations());
- if (status != NO_ERROR) {
- return -1;
- }
-
- ssize_t count = 0;
-
- const size_t N = assets->getGroupEntries().size();
- for (size_t i=0; i<N; i++) {
- const AaptGroupEntry& ge = assets->getGroupEntries()[i];
-
- ssize_t res = processAssets(bundle, zip, assets, ge, &filter);
- if (res < 0) {
- return res;
- }
-
- count += res;
- }
-
- return count;
-}
-
-ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
- const AaptGroupEntry& ge, const ResourceFilter* filter)
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet)
{
ssize_t count = 0;
-
- const size_t ND = dir->getDirs().size();
- size_t i;
- for (i=0; i<ND; i++) {
- const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
-
- const bool filterable = filter != NULL && subDir->getLeaf().find("mipmap-") != 0;
-
- if (filterable && subDir->getLeaf() != subDir->getPath() && !filter->match(ge.toParams())) {
- continue;
- }
-
- ssize_t res = processAssets(bundle, zip, subDir, ge, filterable ? filter : NULL);
- if (res < 0) {
- return res;
- }
- count += res;
- }
-
- if (filter != NULL && !filter->match(ge.toParams())) {
- return count;
- }
-
- const size_t NF = dir->getFiles().size();
- for (i=0; i<NF; i++) {
- sp<AaptGroup> gp = dir->getFiles().valueAt(i);
- ssize_t fi = gp->getFiles().indexOfKey(ge);
- if (fi >= 0) {
- sp<AaptFile> fl = gp->getFiles().valueAt(fi);
- if (!processFile(bundle, zip, gp, fl)) {
+ const std::set<OutputEntry>& entries = outputSet->getEntries();
+ std::set<OutputEntry>::const_iterator iter = entries.begin();
+ for (; iter != entries.end(); iter++) {
+ const OutputEntry& entry = *iter;
+ if (entry.getFile() == NULL) {
+ fprintf(stderr, "warning: null file being processed.\n");
+ } else {
+ String8 storagePath(entry.getPath());
+ storagePath.convertToResPath();
+ if (!processFile(bundle, zip, storagePath, entry.getFile())) {
return UNKNOWN_ERROR;
}
count++;
}
}
-
return count;
}
@@ -294,12 +243,10 @@
* delete the existing entry before adding the new one.
*/
bool processFile(Bundle* bundle, ZipFile* zip,
- const sp<AaptGroup>& group, const sp<AaptFile>& file)
+ String8 storageName, const sp<const AaptFile>& file)
{
const bool hasData = file->hasData();
- String8 storageName(group->getPath());
- storageName.convertToResPath();
ZipEntry* entry;
bool fromGzip = false;
status_t result;
@@ -403,8 +350,8 @@
fprintf(stderr, " Unable to add '%s': file already in archive (try '-u'?)\n",
file->getPrintableSource().string());
} else {
- fprintf(stderr, " Unable to add '%s': Zip add failed\n",
- file->getPrintableSource().string());
+ fprintf(stderr, " Unable to add '%s': Zip add failed (%d)\n",
+ file->getPrintableSource().string(), result);
}
return false;
}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 1348be3..e599643 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -179,24 +179,6 @@
|| type == "color" || type == "menu" || type == "mipmap";
}
-static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true)
-{
- sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc"));
- sp<AaptFile> file;
- if (group != NULL) {
- file = group->getFiles().valueFor(AaptGroupEntry());
- if (file != NULL) {
- return file;
- }
- }
-
- if (!makeIfNecessary) {
- return NULL;
- }
- return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(),
- NULL, String8());
-}
-
static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets,
const sp<AaptGroup>& grp)
{
@@ -359,23 +341,6 @@
return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
}
-status_t postProcessImages(const sp<AaptAssets>& assets,
- ResourceTable* table,
- const sp<ResourceTypeSet>& set)
-{
- ResourceDirIterator it(set, String8("drawable"));
- bool hasErrors = false;
- ssize_t res;
- while ((res=it.next()) == NO_ERROR) {
- res = postProcessImage(assets, table, it.getFile());
- if (res < NO_ERROR) {
- hasErrors = true;
- }
- }
-
- return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
-}
-
static void collect_files(const sp<AaptDir>& dir,
KeyedVector<String8, sp<ResourceTypeSet> >* resources)
{
@@ -906,7 +871,38 @@
return 0;
}
-status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
+status_t generateAndroidManifestForSplit(const String16& package, const sp<ApkSplit>& split,
+ sp<AaptFile>& outFile) {
+ const String8 filename("AndroidManifest.xml");
+ const String16 androidPrefix("android");
+ const String16 androidNSUri("http://schemas.android.com/apk/res/android");
+ sp<XMLNode> root = XMLNode::newNamespace(filename, androidPrefix, androidNSUri);
+
+ // Build the <manifest> tag
+ sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest"));
+
+ // Add the 'package' attribute which is set to the original package name.
+ manifest->addAttribute(String16(), String16("package"), package);
+
+ // Add the 'split' attribute which describes the configurations included.
+ String8 splitName("config_");
+ splitName.append(split->getDirectorySafeName());
+ manifest->addAttribute(String16(), String16("split"), String16(splitName));
+
+ // Build an empty <application> tag (required).
+ sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application"));
+ manifest->addChild(app);
+ root->addChild(manifest);
+
+ status_t err = root->flatten(outFile, true, true);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ outFile->setCompressionMethod(ZipEntry::kCompressDeflated);
+ return NO_ERROR;
+}
+
+status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder)
{
// First, look for a package file to parse. This is required to
// be able to generate the resource information.
@@ -1122,12 +1118,6 @@
// --------------------------------------------------------------------
if (table.hasResources()) {
- sp<AaptFile> resFile(getResourceFile(assets));
- if (resFile == NULL) {
- fprintf(stderr, "Error: unable to generate entry for resource data\n");
- return UNKNOWN_ERROR;
- }
-
err = table.assignResourceIds();
if (err < NO_ERROR) {
return err;
@@ -1235,16 +1225,24 @@
}
if (drawables != NULL) {
- err = postProcessImages(assets, &table, drawables);
- if (err != NO_ERROR) {
+ ResourceDirIterator it(drawables, String8("drawable"));
+ while ((err=it.next()) == NO_ERROR) {
+ err = postProcessImage(assets, &table, it.getFile());
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
+ if (err < NO_ERROR) {
hasErrors = true;
}
+ err = NO_ERROR;
}
if (colors != NULL) {
ResourceDirIterator it(colors, String8("color"));
while ((err=it.next()) == NO_ERROR) {
- err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
if (err != NO_ERROR) {
hasErrors = true;
}
@@ -1320,15 +1318,35 @@
return err;
}
- resFile = getResourceFile(assets);
- if (resFile == NULL) {
- fprintf(stderr, "Error: unable to generate entry for resource data\n");
- return UNKNOWN_ERROR;
- }
+ Vector<sp<ApkSplit> >& splits = builder->getSplits();
+ const size_t numSplits = splits.size();
+ for (size_t i = 0; i < numSplits; i++) {
+ sp<ApkSplit>& split = splits.editItemAt(i);
+ sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"),
+ AaptGroupEntry(), String8());
+ err = table.flatten(bundle, split->getResourceFilter(), flattenedTable);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "Failed to generate resource table for split '%s'\n",
+ split->getPrintableName().string());
+ return err;
+ }
+ split->addEntry(String8("resources.arsc"), flattenedTable);
- err = table.flatten(bundle, resFile);
- if (err < NO_ERROR) {
- return err;
+ if (split->isBase()) {
+ resFile = flattenedTable;
+ finalResTable.add(flattenedTable->getData(), flattenedTable->getSize());
+ } else {
+ sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"),
+ AaptGroupEntry(), String8());
+ err = generateAndroidManifestForSplit(String16(assets->getPackage()), split,
+ generatedManifest);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n",
+ split->getPrintableName().string());
+ return err;
+ }
+ split->addEntry(String8("AndroidManifest.xml"), generatedManifest);
+ }
}
if (bundle->getPublicOutputFile()) {
@@ -1344,18 +1362,13 @@
table.writePublicDefinitions(String16(assets->getPackage()), fp);
fclose(fp);
}
-
- // Read resources back in,
- finalResTable.add(resFile->getData(), resFile->getSize());
-
-#if 0
- NOISY(
- printf("Generated resources:\n");
- finalResTable.print();
- )
-#endif
+
+ if (finalResTable.getTableCount() == 0 || resFile == NULL) {
+ fprintf(stderr, "No resource table was generated.\n");
+ return UNKNOWN_ERROR;
+ }
}
-
+
// Perform a basic validation of the manifest file. This time we
// parse it with the comments intact, so that we can use them to
// generate java docs... so we are not going to write this one
diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp
index 8ca852e..de8b4fc 100644
--- a/tools/aapt/ResourceFilter.cpp
+++ b/tools/aapt/ResourceFilter.cpp
@@ -1,119 +1,92 @@
//
-// Copyright 2011 The Android Open Source Project
+// Copyright 2014 The Android Open Source Project
//
// Build resource files from raw assets.
//
#include "ResourceFilter.h"
+#include "AaptUtil.h"
+#include "AaptConfig.h"
status_t
-ResourceFilter::parse(const char* arg)
+WeakResourceFilter::parse(const String8& str)
{
- if (arg == NULL) {
- return 0;
- }
-
- const char* p = arg;
- const char* q;
-
- while (true) {
- q = strchr(p, ',');
- if (q == NULL) {
- q = p + strlen(p);
- }
-
- String8 part(p, q-p);
-
+ Vector<String8> configStrs = AaptUtil::split(str, ',');
+ const size_t N = configStrs.size();
+ mConfigs.clear();
+ mConfigMask = 0;
+ mConfigs.resize(N);
+ for (size_t i = 0; i < N; i++) {
+ const String8& part = configStrs[i];
if (part == "en_XA") {
mContainsPseudoAccented = true;
} else if (part == "ar_XB") {
mContainsPseudoBidi = true;
}
- int axis;
- AxisValue value;
- if (!AaptGroupEntry::parseFilterNamePart(part, &axis, &value)) {
- fprintf(stderr, "Invalid configuration: %s\n", arg);
- fprintf(stderr, " ");
- for (int i=0; i<p-arg; i++) {
- fprintf(stderr, " ");
- }
- for (int i=0; i<q-p; i++) {
- fprintf(stderr, "^");
- }
- fprintf(stderr, "\n");
- return 1;
+
+ std::pair<ConfigDescription, uint32_t>& entry = mConfigs.editItemAt(i);
+
+ AaptLocaleValue val;
+ if (val.initFromFilterString(part)) {
+ // For backwards compatibility, we accept configurations that
+ // only specify locale in the standard 'en_US' format.
+ val.writeTo(&entry.first);
+ } else if (!AaptConfig::parse(part, &entry.first)) {
+ fprintf(stderr, "Invalid configuration: %s\n", part.string());
+ return UNKNOWN_ERROR;
}
- ssize_t index = mData.indexOfKey(axis);
- if (index < 0) {
- mData.add(axis, SortedVector<AxisValue>());
- }
- SortedVector<AxisValue>& sv = mData.editValueFor(axis);
- sv.add(value);
+ entry.second = mDefault.diff(entry.first);
- // If it's a locale with a region, script or variant, we should also match an
- // unmodified locale of the same language
- if (axis == AXIS_LOCALE) {
- if (value.localeValue.region[0] || value.localeValue.script[0] ||
- value.localeValue.variant[0]) {
- AxisValue copy;
- memcpy(copy.localeValue.language, value.localeValue.language,
- sizeof(value.localeValue.language));
- sv.add(copy);
- }
- }
- p = q;
- if (!*p) break;
- p++;
+ // Ignore the version
+ entry.second &= ~ResTable_config::CONFIG_VERSION;
+
+ mConfigMask |= entry.second;
}
return NO_ERROR;
}
bool
-ResourceFilter::isEmpty() const
+WeakResourceFilter::match(const ResTable_config& config) const
{
- return mData.size() == 0;
-}
-
-bool
-ResourceFilter::match(int axis, const AxisValue& value) const
-{
- if (value.intValue == 0 && (value.localeValue.language[0] == 0)) {
- // they didn't specify anything so take everything
+ uint32_t mask = mDefault.diff(config);
+ if ((mConfigMask & mask) == 0) {
+ // The two configurations don't have any common axis.
return true;
}
- ssize_t index = mData.indexOfKey(axis);
- if (index < 0) {
- // we didn't request anything on this axis so take everything
- return true;
- }
- const SortedVector<AxisValue>& sv = mData.valueAt(index);
- return sv.indexOf(value) >= 0;
-}
-bool
-ResourceFilter::match(int axis, const ResTable_config& config) const
-{
- return match(axis, AaptGroupEntry::getConfigValueForAxis(config, axis));
-}
-
-bool
-ResourceFilter::match(const ResTable_config& config) const
-{
- for (int i=AXIS_START; i<=AXIS_END; i++) {
- if (!match(i, AaptGroupEntry::getConfigValueForAxis(config, i))) {
- return false;
+ const size_t N = mConfigs.size();
+ for (size_t i = 0; i < N; i++) {
+ const std::pair<ConfigDescription, uint32_t>& entry = mConfigs[i];
+ uint32_t diff = entry.first.diff(config);
+ if ((diff & entry.second) == 0) {
+ return true;
+ } else if ((diff & entry.second) == ResTable_config::CONFIG_LOCALE) {
+ // If the locales differ, but the languages are the same and
+ // the locale we are matching only has a language specified,
+ // we match.
+ if (config.language[0] && memcmp(config.language, entry.first.language, sizeof(config.language)) == 0) {
+ if (config.country[0] == 0) {
+ return true;
+ }
+ }
}
}
- return true;
+ return false;
}
-const SortedVector<AxisValue>* ResourceFilter::configsForAxis(int axis) const
-{
- ssize_t index = mData.indexOfKey(axis);
- if (index < 0) {
- return NULL;
+status_t
+StrongResourceFilter::parse(const String8& str) {
+ Vector<String8> configStrs = AaptUtil::split(str, ',');
+ ConfigDescription config;
+ mConfigs.clear();
+ for (size_t i = 0; i < configStrs.size(); i++) {
+ if (!AaptConfig::parse(configStrs[i], &config)) {
+ fprintf(stderr, "Invalid configuration: %s\n", configStrs[i].string());
+ return UNKNOWN_ERROR;
+ }
+ mConfigs.insert(config);
}
- return &mData.valueAt(index);
+ return NO_ERROR;
}
diff --git a/tools/aapt/ResourceFilter.h b/tools/aapt/ResourceFilter.h
index c57770e..f459584 100644
--- a/tools/aapt/ResourceFilter.h
+++ b/tools/aapt/ResourceFilter.h
@@ -7,31 +7,137 @@
#ifndef RESOURCE_FILTER_H
#define RESOURCE_FILTER_H
+#include <androidfw/ResourceTypes.h>
+#include <set>
+#include <utility>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
#include "AaptAssets.h"
+#include "ConfigDescription.h"
+
+class ResourceFilter : public virtual android::RefBase {
+public:
+ virtual bool match(const android::ResTable_config& config) const = 0;
+};
/**
* Implements logic for parsing and handling "-c" and "--preferred-configurations"
* options.
*/
-class ResourceFilter
-{
+class WeakResourceFilter : public ResourceFilter {
public:
- 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 mContainsPseudoAccented; }
- inline bool containsPseudoBidi() const { return mContainsPseudoBidi; }
+ WeakResourceFilter()
+ : mContainsPseudoAccented(false)
+ , mContainsPseudoBidi(false) {}
+
+ android::status_t parse(const android::String8& str);
+
+ bool match(const android::ResTable_config& config) const;
+
+ inline bool isEmpty() const {
+ return mConfigMask == 0;
+ }
+
+ inline bool containsPseudo() const {
+ return mContainsPseudoAccented;
+ }
+
+ inline bool containsPseudoBidi() const {
+ return mContainsPseudoBidi;
+ }
private:
- bool match(int axis, const AxisValue& value) const;
+ ConfigDescription mDefault;
+ uint32_t mConfigMask;
+ android::Vector<std::pair<ConfigDescription, uint32_t> > mConfigs;
- KeyedVector<int,SortedVector<AxisValue> > mData;
bool mContainsPseudoAccented;
bool mContainsPseudoBidi;
};
+/**
+ * Matches resources that have at least one of the configurations
+ * that this filter is looking for. In order to match a configuration,
+ * the resource must have the exact same configuration.
+ *
+ * This filter acts as a logical OR when matching resources.
+ *
+ * For example, if the filter is looking for resources with
+ * fr-land, de-land, or sw600dp:
+ *
+ * (PASS) fr-land
+ * (FAIL) fr
+ * (PASS) de-land
+ * (FAIL) de
+ * (FAIL) de-sw600dp
+ * (PASS) sw600dp
+ * (FAIL) sw600dp-land
+ */
+class StrongResourceFilter : public ResourceFilter {
+public:
+ StrongResourceFilter() {}
+ StrongResourceFilter(const std::set<ConfigDescription>& configs)
+ : mConfigs(configs) {}
+
+ android::status_t parse(const android::String8& str);
+
+ bool match(const android::ResTable_config& config) const {
+ std::set<ConfigDescription>::const_iterator iter = mConfigs.begin();
+ for (; iter != mConfigs.end(); iter++) {
+ if (iter->compare(config) == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ inline const std::set<ConfigDescription>& getConfigs() const {
+ return mConfigs;
+ }
+
+private:
+ std::set<ConfigDescription> mConfigs;
+};
+
+/**
+ * Negates the response of the target filter.
+ */
+class InverseResourceFilter : public ResourceFilter {
+public:
+ InverseResourceFilter(const android::sp<ResourceFilter>& filter)
+ : mFilter(filter) {}
+
+ bool match(const android::ResTable_config& config) const {
+ return !mFilter->match(config);
+ }
+
+private:
+ const android::sp<ResourceFilter> mFilter;
+};
+
+/**
+ * A logical AND of all the added filters.
+ */
+class AndResourceFilter : public ResourceFilter {
+public:
+ void addFilter(const android::sp<ResourceFilter>& filter) {
+ mFilters.add(filter);
+ }
+
+ bool match(const android::ResTable_config& config) const {
+ const size_t N = mFilters.size();
+ for (size_t i = 0; i < N; i++) {
+ if (!mFilters[i]->match(config)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+private:
+ android::Vector<android::sp<ResourceFilter> > mFilters;
+};
#endif
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 6eab65b..efbba40 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2095,10 +2095,10 @@
return mNumLocal > 0;
}
-sp<AaptFile> ResourceTable::flatten(Bundle* bundle)
+sp<AaptFile> ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter)
{
sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
- status_t err = flatten(bundle, data);
+ status_t err = flatten(bundle, filter, data);
return err == NO_ERROR ? data : NULL;
}
@@ -2658,8 +2658,8 @@
}
// Check that all requested localizations are present for this string
- if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) {
- const char* allConfigs = mBundle->getConfigurations();
+ if (mBundle->getConfigurations().size() > 0 && mBundle->getRequireLocalization()) {
+ const char* allConfigs = mBundle->getConfigurations().string();
const char* start = allConfigs;
const char* comma;
@@ -2713,14 +2713,8 @@
return err;
}
-status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
+status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const sp<AaptFile>& dest)
{
- ResourceFilter filter;
- status_t err = filter.parse(bundle->getConfigurations());
- if (err != NO_ERROR) {
- return err;
- }
-
const ConfigDescription nullConfig;
const size_t N = mOrderedPackages.size();
@@ -2795,7 +2789,7 @@
const size_t N = c->getEntries().size();
for (size_t ei=0; ei<N; ei++) {
ConfigDescription config = c->getEntries().keyAt(ei);
- if (filterable && !filter.match(config)) {
+ if (filterable && !filter->match(config)) {
continue;
}
sp<Entry> e = c->getEntries().valueAt(ei);
@@ -2887,7 +2881,7 @@
return amt;
}
- err = flattenLibraryTable(data, libraryPackages);
+ status_t err = flattenLibraryTable(data, libraryPackages);
if (err != NO_ERROR) {
fprintf(stderr, "ERROR: failed to write library table\n");
return err;
@@ -2943,11 +2937,11 @@
}
const size_t CN = cl->getEntries().size();
for (size_t ci=0; ci<CN; ci++) {
- if (filterable && !filter.match(cl->getEntries().keyAt(ci))) {
+ if (filterable && !filter->match(cl->getEntries().keyAt(ci))) {
continue;
}
for (size_t cj=ci+1; cj<CN; cj++) {
- if (filterable && !filter.match(cl->getEntries().keyAt(cj))) {
+ if (filterable && !filter->match(cl->getEntries().keyAt(cj))) {
continue;
}
typeSpecFlags[ei] |= htodl(
@@ -2989,7 +2983,7 @@
config.screenHeightDp,
config.layoutDirection));
- if (filterable && !filter.match(config)) {
+ if (filterable && !filter->match(config)) {
continue;
}
@@ -3108,7 +3102,7 @@
}
ssize_t strStart = dest->getSize();
- err = valueStrings.writeStringBlock(dest);
+ status_t err = valueStrings.writeStringBlock(dest);
if (err != NO_ERROR) {
return err;
}
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index ec8fd175e..a73993c 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -7,8 +7,10 @@
#ifndef RESOURCE_TABLE_H
#define RESOURCE_TABLE_H
+#include "ConfigDescription.h"
#include "StringPool.h"
#include "SourcePos.h"
+#include "ResourceFilter.h"
#include <set>
#include <map>
@@ -76,37 +78,6 @@
class Type;
class Entry;
- struct ConfigDescription : public ResTable_config {
- ConfigDescription() {
- memset(this, 0, sizeof(*this));
- size = sizeof(ResTable_config);
- }
- ConfigDescription(const ResTable_config&o) {
- *static_cast<ResTable_config*>(this) = o;
- size = sizeof(ResTable_config);
- }
- ConfigDescription(const ConfigDescription&o) {
- *static_cast<ResTable_config*>(this) = o;
- }
-
- ConfigDescription& operator=(const ResTable_config& o) {
- *static_cast<ResTable_config*>(this) = o;
- size = sizeof(ResTable_config);
- return *this;
- }
- ConfigDescription& operator=(const ConfigDescription& o) {
- *static_cast<ResTable_config*>(this) = o;
- return *this;
- }
-
- inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; }
- inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; }
- inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; }
- inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; }
- inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; }
- inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; }
- };
-
ResourceTable(Bundle* bundle, const String16& assetsPackage);
status_t addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets);
@@ -182,7 +153,7 @@
size_t numLocalResources() const;
bool hasResources() const;
- sp<AaptFile> flatten(Bundle*);
+ sp<AaptFile> flatten(Bundle* bundle, const sp<const ResourceFilter>& filter);
static inline uint32_t makeResId(uint32_t packageId,
uint32_t typeId,
@@ -223,7 +194,7 @@
void addLocalization(const String16& name, const String8& locale, const SourcePos& src);
status_t validateLocalizations(void);
- status_t flatten(Bundle*, const sp<AaptFile>& dest);
+ status_t flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const sp<AaptFile>& dest);
status_t flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs);
void writePublicDefinitions(const String16& package, FILE* fp);
diff --git a/tools/aapt/tests/AaptConfig_test.cpp b/tools/aapt/tests/AaptConfig_test.cpp
new file mode 100644
index 0000000..e795d818
--- /dev/null
+++ b/tools/aapt/tests/AaptConfig_test.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/String8.h>
+#include <gtest/gtest.h>
+
+#include "AaptConfig.h"
+#include "ConfigDescription.h"
+#include "TestHelper.h"
+
+using android::String8;
+
+static ::testing::AssertionResult TestParse(const String8& input, ConfigDescription* config=NULL) {
+ if (AaptConfig::parse(String8(input), config)) {
+ return ::testing::AssertionSuccess() << input << " was successfully parsed";
+ }
+ return ::testing::AssertionFailure() << input << " could not be parsed";
+}
+
+static ::testing::AssertionResult TestParse(const char* input, ConfigDescription* config=NULL) {
+ return TestParse(String8(input), config);
+}
+
+TEST(AaptConfigTest, ParseFailWhenQualifiersAreOutOfOrder) {
+ EXPECT_FALSE(TestParse("en-sw600dp-ldrtl"));
+ EXPECT_FALSE(TestParse("land-en"));
+ EXPECT_FALSE(TestParse("hdpi-320dpi"));
+}
+
+TEST(AaptConfigTest, ParseFailWhenQualifiersAreNotMatched) {
+ EXPECT_FALSE(TestParse("en-sw600dp-ILLEGAL"));
+}
+
+TEST(AaptConfigTest, ParseFailWhenQualifiersHaveTrailingDash) {
+ EXPECT_FALSE(TestParse("en-sw600dp-land-"));
+}
+
+TEST(AaptConfigTest, ParseBasicQualifiers) {
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("", &config));
+ EXPECT_EQ(String8(""), config.toString());
+
+ EXPECT_TRUE(TestParse("fr-land", &config));
+ EXPECT_EQ(String8("fr-land"), config.toString());
+
+ EXPECT_TRUE(TestParse("mcc310-pl-sw720dp-normal-long-port-night-"
+ "xhdpi-keyssoft-qwerty-navexposed-nonav", &config));
+ EXPECT_EQ(String8("mcc310-pl-sw720dp-normal-long-port-night-"
+ "xhdpi-keyssoft-qwerty-navexposed-nonav-v13"), config.toString());
+}
+
+TEST(AaptConfigTest, ParseLocales) {
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("en-rUS", &config));
+ EXPECT_EQ(String8("en-US"), config.toString());
+}
+
+TEST(AaptConfigTest, ParseQualifierAddedInApi13) {
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("sw600dp", &config));
+ EXPECT_EQ(String8("sw600dp-v13"), config.toString());
+
+ EXPECT_TRUE(TestParse("sw600dp-v8", &config));
+ EXPECT_EQ(String8("sw600dp-v13"), config.toString());
+}
diff --git a/tools/aapt/tests/AaptGroupEntry_test.cpp b/tools/aapt/tests/AaptGroupEntry_test.cpp
new file mode 100644
index 0000000..7348a08
--- /dev/null
+++ b/tools/aapt/tests/AaptGroupEntry_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/String8.h>
+#include <gtest/gtest.h>
+
+#include "AaptAssets.h"
+#include "ResourceFilter.h"
+#include "TestHelper.h"
+
+using android::String8;
+
+static ::testing::AssertionResult TestParse(AaptGroupEntry& entry, const String8& dirName,
+ String8* outType) {
+ if (entry.initFromDirName(dirName, outType)) {
+ return ::testing::AssertionSuccess() << dirName << " was successfully parsed";
+ }
+ return ::testing::AssertionFailure() << dirName << " could not be parsed";
+}
+
+static ::testing::AssertionResult TestParse(AaptGroupEntry& entry, const char* input,
+ String8* outType) {
+ return TestParse(entry, String8(input), outType);
+}
+
+TEST(AaptGroupEntryTest, ParseNoQualifier) {
+ AaptGroupEntry entry;
+ String8 type;
+ EXPECT_TRUE(TestParse(entry, "menu", &type));
+ EXPECT_EQ(String8("menu"), type);
+}
+
+TEST(AaptGroupEntryTest, ParseCorrectType) {
+ AaptGroupEntry entry;
+ String8 type;
+ EXPECT_TRUE(TestParse(entry, "anim", &type));
+ EXPECT_EQ(String8("anim"), type);
+
+ EXPECT_TRUE(TestParse(entry, "animator", &type));
+ EXPECT_EQ(String8("animator"), type);
+}
diff --git a/tools/aapt/tests/ResourceFilter_test.cpp b/tools/aapt/tests/ResourceFilter_test.cpp
new file mode 100644
index 0000000..30697bb
--- /dev/null
+++ b/tools/aapt/tests/ResourceFilter_test.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <androidfw/ResourceTypes.h>
+#include <utils/String8.h>
+#include <gtest/gtest.h>
+
+#include "AaptConfig.h"
+#include "ResourceFilter.h"
+#include "ConfigDescription.h"
+
+using android::String8;
+
+// In this context, 'Axis' represents a particular field in the configuration,
+// such as language or density.
+
+TEST(WeakResourceFilterTest, EmptyFilterMatchesAnything) {
+ WeakResourceFilter filter;
+ ASSERT_EQ(NO_ERROR, filter.parse(String8("")));
+
+ ConfigDescription config;
+ config.density = 320;
+
+ EXPECT_TRUE(filter.match(config));
+
+ config.language[0] = 'f';
+ config.language[1] = 'r';
+
+ EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, MatchesConfigWithUnrelatedAxis) {
+ WeakResourceFilter filter;
+ ASSERT_EQ(NO_ERROR, filter.parse(String8("fr")));
+
+ ConfigDescription config;
+ config.density = 320;
+
+ EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, MatchesConfigWithSameValueAxis) {
+ WeakResourceFilter filter;
+ ASSERT_EQ(NO_ERROR, filter.parse(String8("fr")));
+
+ ConfigDescription config;
+ config.language[0] = 'f';
+ config.language[1] = 'r';
+
+ EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, MatchesConfigWithSameValueAxisAndOtherUnrelatedAxis) {
+ WeakResourceFilter filter;
+ ASSERT_EQ(NO_ERROR, filter.parse(String8("fr")));
+
+ ConfigDescription config;
+ config.language[0] = 'f';
+ config.language[1] = 'r';
+ config.density = 320;
+
+ EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, DoesNotMatchConfigWithDifferentValueAxis) {
+ WeakResourceFilter filter;
+ ASSERT_EQ(NO_ERROR, filter.parse(String8("fr")));
+
+ ConfigDescription config;
+ config.language[0] = 'd';
+ config.language[1] = 'e';
+
+ EXPECT_FALSE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, MatchesConfigWithSameLanguageButNoRegionSpecified) {
+ WeakResourceFilter filter;
+ ASSERT_EQ(NO_ERROR, filter.parse(String8("de-rDE")));
+
+ ConfigDescription config;
+ config.language[0] = 'd';
+ config.language[1] = 'e';
+
+ EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, ParsesStandardLocaleOnlyString) {
+ WeakResourceFilter filter;
+ EXPECT_EQ(NO_ERROR, filter.parse(String8("de_DE")));
+}
+
+TEST(WeakResourceFilterTest, IgnoresVersion) {
+ WeakResourceFilter filter;
+ ASSERT_EQ(NO_ERROR, filter.parse(String8("normal-v4")));
+
+ ConfigDescription config;
+ config.smallestScreenWidthDp = 600;
+ config.version = 13;
+
+ // The configs don't match on any axis besides version, which should be ignored.
+ EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, MatchesConfigWithRegion) {
+ WeakResourceFilter filter;
+ ASSERT_EQ(NO_ERROR, filter.parse(String8("kok,kok_IN,kok_419")));
+
+ ConfigDescription config;
+ AaptLocaleValue val;
+ ASSERT_TRUE(val.initFromFilterString(String8("kok_IN")));
+ val.writeTo(&config);
+
+ EXPECT_TRUE(filter.match(config));
+}
+
diff --git a/tools/aapt/tests/TestHelper.h b/tools/aapt/tests/TestHelper.h
new file mode 100644
index 0000000..79174832
--- /dev/null
+++ b/tools/aapt/tests/TestHelper.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TEST_HELPER_H
+#define __TEST_HELPER_H
+
+#include <utils/String8.h>
+
+namespace android {
+
+/**
+ * Stream operator for nicely printing String8's in gtest output.
+ */
+inline std::ostream& operator<<(std::ostream& stream, const String8& str) {
+ return stream << str.string();
+}
+
+}
+
+#endif
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 5af1e4e..15b65c1 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -402,12 +402,12 @@
public static final String EXTRA_LINK_PROPERTIES = "linkProperties";
/**
- * The lookup key for a {@link android.net.LinkCapabilities} object associated with the
+ * The lookup key for a {@link android.net.NetworkCapabilities} object associated with the
* Wi-Fi network. Retrieve with
* {@link android.content.Intent#getParcelableExtra(String)}.
* @hide
*/
- public static final String EXTRA_LINK_CAPABILITIES = "linkCapabilities";
+ public static final String EXTRA_NETWORK_CAPABILITIES = "networkCapabilities";
/**
* The network IDs of the configured networks could have changed.
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 7ded171..40e6649 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -21,7 +21,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.net.BaseNetworkStateTracker;
-import android.net.LinkCapabilities;
+import android.net.NetworkCapabilities;
import android.net.LinkQualityInfo;
import android.net.LinkProperties;
import android.net.NetworkInfo;
@@ -65,7 +65,7 @@
public WifiStateTracker(int netType, String networkName) {
mNetworkInfo = new NetworkInfo(netType, 0, networkName, "");
mLinkProperties = new LinkProperties();
- mLinkCapabilities = new LinkCapabilities();
+ mNetworkCapabilities = new NetworkCapabilities();
mNetworkInfo.setIsAvailable(false);
setTeardownRequested(false);
@@ -183,17 +183,6 @@
}
/**
- * A capability is an Integer/String pair, the capabilities
- * are defined in the class LinkSocket#Key.
- *
- * @return a copy of this connections capabilities, may be empty but never null.
- */
- @Override
- public LinkCapabilities getLinkCapabilities() {
- return new LinkCapabilities(mLinkCapabilities);
- }
-
- /**
* Return link info
* @return an object of type WifiLinkQualityInfo
*/
@@ -264,10 +253,10 @@
if (mLinkProperties == null) {
mLinkProperties = new LinkProperties();
}
- mLinkCapabilities = intent.getParcelableExtra(
- WifiManager.EXTRA_LINK_CAPABILITIES);
- if (mLinkCapabilities == null) {
- mLinkCapabilities = new LinkCapabilities();
+ mNetworkCapabilities = intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_CAPABILITIES);
+ if (mNetworkCapabilities == null) {
+ mNetworkCapabilities = new NetworkCapabilities();
}
mWifiInfo = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
@@ -323,4 +312,3 @@
mSamplingDataTracker.stopSampling(s);
}
}
-