Merge changes I4ef284da,Ie8cc69bd into jb-mr1-dev
* changes:
Make the SystemUI stop the screen saver when a full-screen notification is posted.
Do not play sounds or vibes for background users' notifications.
diff --git a/api/current.txt b/api/current.txt
index fbda3c6..4a99427 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -947,6 +947,7 @@
field public static final int subtitle = 16843473; // 0x10102d1
field public static final int subtitleTextStyle = 16843513; // 0x10102f9
field public static final int subtypeExtraValue = 16843674; // 0x101039a
+ field public static final int subtypeId = 16843713; // 0x10103c1
field public static final int subtypeLocale = 16843673; // 0x1010399
field public static final int suggestActionMsg = 16843228; // 0x10101dc
field public static final int suggestActionMsgColumn = 16843229; // 0x10101dd
@@ -4179,6 +4180,7 @@
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int USES_ENCRYPTED_STORAGE = 7; // 0x7
field public static final int USES_POLICY_DISABLE_CAMERA = 8; // 0x8
+ field public static final int USES_POLICY_DISABLE_KEYGUARD_WIDGETS = 9; // 0x9
field public static final int USES_POLICY_EXPIRE_PASSWORD = 6; // 0x6
field public static final int USES_POLICY_FORCE_LOCK = 3; // 0x3
field public static final int USES_POLICY_LIMIT_PASSWORD = 0; // 0x0
@@ -4214,6 +4216,7 @@
method public java.util.List<android.content.ComponentName> getActiveAdmins();
method public boolean getCameraDisabled(android.content.ComponentName);
method public int getCurrentFailedPasswordAttempts();
+ method public int getKeyguardWidgetsDisabled(android.content.ComponentName);
method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
method public long getMaximumTimeToLock(android.content.ComponentName);
method public long getPasswordExpiration(android.content.ComponentName);
@@ -4237,6 +4240,7 @@
method public void removeActiveAdmin(android.content.ComponentName);
method public boolean resetPassword(java.lang.String, int);
method public void setCameraDisabled(android.content.ComponentName, boolean);
+ method public void setKeyguardWidgetsDisabled(android.content.ComponentName, int);
method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
method public void setMaximumTimeToLock(android.content.ComponentName, long);
method public void setPasswordExpirationTimeout(android.content.ComponentName, long);
@@ -4260,6 +4264,8 @@
field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
+ field public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 2147483647; // 0x7fffffff
+ field public static final int KEYGUARD_DISABLE_WIDGETS_NONE = 0; // 0x0
field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
field public static final int PASSWORD_QUALITY_BIOMETRIC_WEAK = 32768; // 0x8000
@@ -6250,6 +6256,7 @@
field public static final int CONFIG_FONT_SCALE = 1073741824; // 0x40000000
field public static final int CONFIG_KEYBOARD = 16; // 0x10
field public static final int CONFIG_KEYBOARD_HIDDEN = 32; // 0x20
+ field public static final int CONFIG_LAYOUT_DIRECTION = 8192; // 0x2000
field public static final int CONFIG_LOCALE = 4; // 0x4
field public static final int CONFIG_MCC = 1; // 0x1
field public static final int CONFIG_MNC = 2; // 0x2
@@ -6852,9 +6859,12 @@
method public int describeContents();
method public int diff(android.content.res.Configuration);
method public boolean equals(android.content.res.Configuration);
+ method public int getLayoutDirection();
method public boolean isLayoutSizeAtLeast(int);
method public static boolean needNewResources(int, int);
method public void readFromParcel(android.os.Parcel);
+ method public void setLayoutDirection(java.util.Locale);
+ method public void setLocale(java.util.Locale);
method public void setTo(android.content.res.Configuration);
method public void setToDefaults();
method public int updateFrom(android.content.res.Configuration);
@@ -6883,6 +6893,11 @@
field public static final int ORIENTATION_PORTRAIT = 1; // 0x1
field public static final deprecated int ORIENTATION_SQUARE = 3; // 0x3
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
+ field public static final int SCREENLAYOUT_LAYOUTDIR_LTR = 64; // 0x40
+ field public static final int SCREENLAYOUT_LAYOUTDIR_MASK = 192; // 0xc0
+ field public static final int SCREENLAYOUT_LAYOUTDIR_RTL = 128; // 0x80
+ field public static final int SCREENLAYOUT_LAYOUTDIR_SHIFT = 6; // 0x6
+ field public static final int SCREENLAYOUT_LAYOUTDIR_UNDEFINED = 0; // 0x0
field public static final int SCREENLAYOUT_LONG_MASK = 48; // 0x30
field public static final int SCREENLAYOUT_LONG_NO = 16; // 0x10
field public static final int SCREENLAYOUT_LONG_UNDEFINED = 0; // 0x0
@@ -6893,6 +6908,7 @@
field public static final int SCREENLAYOUT_SIZE_SMALL = 1; // 0x1
field public static final int SCREENLAYOUT_SIZE_UNDEFINED = 0; // 0x0
field public static final int SCREENLAYOUT_SIZE_XLARGE = 4; // 0x4
+ field public static final int SCREENLAYOUT_UNDEFINED = 0; // 0x0
field public static final int SCREEN_HEIGHT_DP_UNDEFINED = 0; // 0x0
field public static final int SCREEN_WIDTH_DP_UNDEFINED = 0; // 0x0
field public static final int SMALLEST_SCREEN_WIDTH_DP_UNDEFINED = 0; // 0x0
@@ -26642,6 +26658,7 @@
public final class InputMethodSubtype implements android.os.Parcelable {
ctor public InputMethodSubtype(int, int, java.lang.String, java.lang.String, java.lang.String, boolean, boolean);
+ ctor public InputMethodSubtype(int, int, java.lang.String, java.lang.String, java.lang.String, boolean, boolean, int);
method public boolean containsExtraValueKey(java.lang.String);
method public int describeContents();
method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 7f3dbe5..bcd4588 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1181,12 +1181,12 @@
private class IntentReceiver extends IIntentReceiver.Stub {
private boolean mFinished = false;
- public synchronized void performReceive(
- Intent intent, int rc, String data, Bundle ext, boolean ord,
- boolean sticky) {
- String line = "Broadcast completed: result=" + rc;
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data, Bundle extras,
+ boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
+ String line = "Broadcast completed: result=" + resultCode;
if (data != null) line = line + ", data=\"" + data + "\"";
- if (ext != null) line = line + ", extras: " + ext;
+ if (extras != null) line = line + ", extras: " + extras;
System.out.println(line);
mFinished = true;
notifyAll();
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index ab64747..9e83a67 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -14,6 +14,7 @@
** limitations under the License.
*/
+#include <linux/capability.h>
#include "installd.h"
#include <diskusage/dirsize.h>
@@ -665,16 +666,16 @@
ALOGE("dexopt cannot open '%s' for output\n", dex_path);
goto fail;
}
- if (fchown(odex_fd, AID_SYSTEM, uid) < 0) {
- ALOGE("dexopt cannot chown '%s'\n", dex_path);
- goto fail;
- }
if (fchmod(odex_fd,
S_IRUSR|S_IWUSR|S_IRGRP |
(is_public ? S_IROTH : 0)) < 0) {
ALOGE("dexopt cannot chmod '%s'\n", dex_path);
goto fail;
}
+ if (fchown(odex_fd, AID_SYSTEM, uid) < 0) {
+ ALOGE("dexopt cannot chown '%s'\n", dex_path);
+ goto fail;
+ }
ALOGV("DexInv: --- BEGIN '%s' ---\n", apk_path);
@@ -690,13 +691,23 @@
ALOGE("setuid(%d) during dexopt\n", uid);
exit(65);
}
+ // drop capabilities
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ if (capset(&capheader, &capdata[0]) < 0) {
+ ALOGE("capset failed: %s\n", strerror(errno));
+ exit(66);
+ }
if (flock(odex_fd, LOCK_EX | LOCK_NB) != 0) {
ALOGE("flock(%s) failed: %s\n", dex_path, strerror(errno));
- exit(66);
+ exit(67);
}
run_dexopt(zip_fd, odex_fd, apk_path, dexopt_flags);
- exit(67); /* only get here on exec failure */
+ exit(68); /* only get here on exec failure */
} else {
res = wait_dexopt(pid, apk_path);
if (res != 0) {
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index d559639..652543fd 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -14,6 +14,9 @@
** limitations under the License.
*/
+#include <linux/capability.h>
+#include <linux/prctl.h>
+
#include "installd.h"
@@ -491,12 +494,53 @@
return res;
}
+static void drop_privileges() {
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (setgid(AID_INSTALL) < 0) {
+ ALOGE("setgid() can't drop privileges; exiting.\n");
+ exit(1);
+ }
+
+ if (setuid(AID_INSTALL) < 0) {
+ ALOGE("setuid() can't drop privileges; exiting.\n");
+ exit(1);
+ }
+
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capheader.pid = 0;
+
+ capdata[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);
+ capdata[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN);
+ capdata[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);
+ capdata[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);
+
+ capdata[0].effective = capdata[0].permitted;
+ capdata[1].effective = capdata[1].permitted;
+ capdata[0].inheritable = 0;
+ capdata[1].inheritable = 0;
+
+ if (capset(&capheader, &capdata[0]) < 0) {
+ ALOGE("capset failed: %s\n", strerror(errno));
+ exit(1);
+ }
+}
+
int main(const int argc, const char *argv[]) {
char buf[BUFFER_MAX];
struct sockaddr addr;
socklen_t alen;
int lsocket, s, count;
+ ALOGI("installd firing up\n");
+
if (initialize_globals() < 0) {
ALOGE("Could not initialize globals; exiting.\n");
exit(1);
@@ -507,6 +551,8 @@
exit(1);
}
+ drop_privileges();
+
lsocket = android_get_control_socket(SOCKET_PATH);
if (lsocket < 0) {
ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 14ba537..eed9254 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -300,7 +300,8 @@
= b != null ? IIntentReceiver.Stub.asInterface(b) : null;
IntentFilter filter = IntentFilter.CREATOR.createFromParcel(data);
String perm = data.readString();
- Intent intent = registerReceiver(app, packageName, rec, filter, perm);
+ int userId = data.readInt();
+ Intent intent = registerReceiver(app, packageName, rec, filter, perm, userId);
reply.writeNoException();
if (intent != null) {
reply.writeInt(1);
@@ -1999,7 +2000,7 @@
}
public Intent registerReceiver(IApplicationThread caller, String packageName,
IIntentReceiver receiver,
- IntentFilter filter, String perm) throws RemoteException
+ IntentFilter filter, String perm, int userId) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -2009,6 +2010,7 @@
data.writeStrongBinder(receiver != null ? receiver.asBinder() : null);
filter.writeToParcel(data, 0);
data.writeString(perm);
+ data.writeInt(userId);
mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0);
reply.readException();
Intent intent = null;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1b788c2..97dcec0 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -320,8 +320,9 @@
static final class ReceiverData extends BroadcastReceiver.PendingResult {
public ReceiverData(Intent intent, int resultCode, String resultData, Bundle resultExtras,
- boolean ordered, boolean sticky, IBinder token) {
- super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky, token);
+ boolean ordered, boolean sticky, IBinder token, int sendingUser) {
+ super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky,
+ token, sendingUser);
this.intent = intent;
}
@@ -613,9 +614,9 @@
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
- boolean sync) {
+ boolean sync, int sendingUser) {
ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
- sync, false, mAppThread.asBinder());
+ sync, false, mAppThread.asBinder(), sendingUser);
r.info = info;
r.compatInfo = compatInfo;
queueOrSendMessage(H.RECEIVER, r);
@@ -774,8 +775,9 @@
// applies transaction ordering per object for such calls.
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String dataStr, Bundle extras, boolean ordered,
- boolean sticky) throws RemoteException {
- receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky);
+ boolean sticky, int sendingUser) throws RemoteException {
+ receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
+ sticky, sendingUser);
}
public void scheduleLowMemory() {
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 8e6278d..63aa5f9 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -193,8 +193,9 @@
String resultData = data.readString();
Bundle resultExtras = data.readBundle();
boolean sync = data.readInt() != 0;
+ int sendingUser = data.readInt();
scheduleReceiver(intent, info, compatInfo, resultCode, resultData,
- resultExtras, sync);
+ resultExtras, sync, sendingUser);
return true;
}
@@ -378,8 +379,9 @@
Bundle extras = data.readBundle();
boolean ordered = data.readInt() != 0;
boolean sticky = data.readInt() != 0;
+ int sendingUser = data.readInt();
scheduleRegisteredReceiver(receiver, intent,
- resultCode, dataStr, extras, ordered, sticky);
+ resultCode, dataStr, extras, ordered, sticky, sendingUser);
return true;
}
@@ -755,7 +757,7 @@
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String resultData,
- Bundle map, boolean sync) throws RemoteException {
+ Bundle map, boolean sync, int sendingUser) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
intent.writeToParcel(data, 0);
@@ -765,6 +767,7 @@
data.writeString(resultData);
data.writeBundle(map);
data.writeInt(sync ? 1 : 0);
+ data.writeInt(sendingUser);
mRemote.transact(SCHEDULE_RECEIVER_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
@@ -991,8 +994,8 @@
}
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
- int resultCode, String dataStr, Bundle extras, boolean ordered, boolean sticky)
- throws RemoteException {
+ int resultCode, String dataStr, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(receiver.asBinder());
@@ -1002,6 +1005,7 @@
data.writeBundle(extras);
data.writeInt(ordered ? 1 : 0);
data.writeInt(sticky ? 1 : 0);
+ data.writeInt(sendingUser);
mRemote.transact(SCHEDULE_REGISTERED_RECEIVER_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 232ca07..65ea6a0 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -138,6 +138,17 @@
}
@Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ throw new ReceiverCallNotAllowedException(
+ "IntentReceiver components are not allowed to register to receive intents");
+ //ex.fillInStackTrace();
+ //Log.e("IntentReceiver", ex.getMessage(), ex);
+ //return mContext.registerReceiver(receiver, filter, broadcastPermission,
+ // scheduler);
+ }
+
+ @Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
throw new ReceiverCallNotAllowedException(
"IntentReceiver components are not allowed to bind to services");
@@ -1252,11 +1263,18 @@
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
- return registerReceiverInternal(receiver, filter, broadcastPermission,
- scheduler, getOuterContext());
+ return registerReceiverInternal(receiver, UserHandle.myUserId(),
+ filter, broadcastPermission, scheduler, getOuterContext());
}
- private Intent registerReceiverInternal(BroadcastReceiver receiver,
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ return registerReceiverInternal(receiver, user.getIdentifier(),
+ filter, broadcastPermission, scheduler, getOuterContext());
+ }
+
+ private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
@@ -1279,7 +1297,7 @@
try {
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
- rd, filter, broadcastPermission);
+ rd, filter, broadcastPermission, userId);
} catch (RemoteException e) {
return null;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index f7c7013..7a633ed 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -80,7 +80,7 @@
public boolean willActivityBeVisible(IBinder token) throws RemoteException;
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter,
- String requiredPermission) throws RemoteException;
+ String requiredPermission, int userId) throws RemoteException;
public void unregisterReceiver(IIntentReceiver receiver) throws RemoteException;
public int broadcastIntent(IApplicationThread caller, Intent intent,
String resolvedType, IIntentReceiver resultTo, int resultCode,
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index f60cfd6..03a26d4 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -65,7 +65,8 @@
void scheduleDestroyActivity(IBinder token, boolean finished,
int configChanges) throws RemoteException;
void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo,
- int resultCode, String data, Bundle extras, boolean sync) throws RemoteException;
+ int resultCode, String data, Bundle extras, boolean sync,
+ int sendingUser) throws RemoteException;
static final int BACKUP_MODE_INCREMENTAL = 0;
static final int BACKUP_MODE_FULL = 1;
static final int BACKUP_MODE_RESTORE = 2;
@@ -105,8 +106,8 @@
void dumpProvider(FileDescriptor fd, IBinder servicetoken, String[] args)
throws RemoteException;
void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
- int resultCode, String data, Bundle extras, boolean ordered, boolean sticky)
- throws RemoteException;
+ int resultCode, String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) throws RemoteException;
void scheduleLowMemory() throws RemoteException;
void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
void profilerControl(boolean start, String path, ParcelFileDescriptor fd, int profileType)
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 6197b1a..0a9ed58 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -670,8 +670,8 @@
mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
mStrongRef = strong ? rd : null;
}
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered, boolean sticky) {
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
LoadedApk.ReceiverDispatcher rd = mDispatcher.get();
if (ActivityThread.DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
@@ -680,7 +680,7 @@
}
if (rd != null) {
rd.performReceive(intent, resultCode, data, extras,
- ordered, sticky);
+ ordered, sticky, sendingUser);
} else {
// The activity manager dispatched a broadcast to a registered
// receiver in this process, but before it could be delivered the
@@ -716,10 +716,10 @@
private final boolean mOrdered;
public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
- boolean ordered, boolean sticky) {
+ boolean ordered, boolean sticky, int sendingUser) {
super(resultCode, resultData, resultExtras,
mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED,
- ordered, sticky, mIIntentReceiver.asBinder());
+ ordered, sticky, mIIntentReceiver.asBinder(), sendingUser);
mCurIntent = intent;
mOrdered = ordered;
}
@@ -830,14 +830,15 @@
return mUnregisterLocation;
}
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered, boolean sticky) {
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
if (ActivityThread.DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq
+ " to " + mReceiver);
}
- Args args = new Args(intent, resultCode, data, extras, ordered, sticky);
+ Args args = new Args(intent, resultCode, data, extras, ordered,
+ sticky, sendingUser);
if (!mActivityThread.post(args)) {
if (mRegistered && ordered) {
IActivityManager mgr = ActivityManagerNative.getDefault();
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index bcd42ea..a3c1838 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -147,8 +147,8 @@
mWho = who;
mHandler = handler;
}
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean serialized, boolean sticky) {
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean serialized, boolean sticky, int sendingUser) {
mIntent = intent;
mResultCode = resultCode;
mResultData = data;
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 1c37414..c8062ca 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -50,23 +50,23 @@
*/
public final class DeviceAdminInfo implements Parcelable {
static final String TAG = "DeviceAdminInfo";
-
+
/**
* A type of policy that this device admin can use: limit the passwords
* that the user can select, via {@link DevicePolicyManager#setPasswordQuality}
* and {@link DevicePolicyManager#setPasswordMinimumLength}.
- *
+ *
* <p>To control this policy, the device admin must have a "limit-password"
* tag in the "uses-policies" section of its meta-data.
*/
public static final int USES_POLICY_LIMIT_PASSWORD = 0;
-
+
/**
* A type of policy that this device admin can use: able to watch login
* attempts from the user, via {@link DeviceAdminReceiver#ACTION_PASSWORD_FAILED},
* {@link DeviceAdminReceiver#ACTION_PASSWORD_SUCCEEDED}, and
* {@link DevicePolicyManager#getCurrentFailedPasswordAttempts}.
- *
+ *
* <p>To control this policy, the device admin must have a "watch-login"
* tag in the "uses-policies" section of its meta-data.
*/
@@ -76,7 +76,7 @@
* A type of policy that this device admin can use: able to reset the
* user's password via
* {@link DevicePolicyManager#resetPassword}.
- *
+ *
* <p>To control this policy, the device admin must have a "reset-password"
* tag in the "uses-policies" section of its meta-data.
*/
@@ -87,7 +87,7 @@
* to lock via{@link DevicePolicyManager#lockNow} or limit the
* maximum lock timeout for the device via
* {@link DevicePolicyManager#setMaximumTimeToLock}.
- *
+ *
* <p>To control this policy, the device admin must have a "force-lock"
* tag in the "uses-policies" section of its meta-data.
*/
@@ -97,7 +97,7 @@
* A type of policy that this device admin can use: able to factory
* reset the device, erasing all of the user's data, via
* {@link DevicePolicyManager#wipeData}.
- *
+ *
* <p>To control this policy, the device admin must have a "wipe-data"
* tag in the "uses-policies" section of its meta-data.
*/
@@ -138,13 +138,21 @@
*/
public static final int USES_POLICY_DISABLE_CAMERA = 8;
+ /**
+ * A type of policy that this device admin can use: disables use of keyguard widgets.
+ *
+ * <p>To control this policy, the device admin must have a "disable-keyguard-widgets"
+ * tag in the "uses-policies" section of its meta-data.
+ */
+ public static final int USES_POLICY_DISABLE_KEYGUARD_WIDGETS = 9;
+
/** @hide */
public static class PolicyInfo {
public final int ident;
final public String tag;
final public int label;
final public int description;
-
+
public PolicyInfo(int identIn, String tagIn, int labelIn, int descriptionIn) {
ident = identIn;
tag = tagIn;
@@ -152,11 +160,11 @@
description = descriptionIn;
}
}
-
+
static ArrayList<PolicyInfo> sPoliciesDisplayOrder = new ArrayList<PolicyInfo>();
static HashMap<String, Integer> sKnownPolicies = new HashMap<String, Integer>();
static SparseArray<PolicyInfo> sRevKnownPolicies = new SparseArray<PolicyInfo>();
-
+
static {
sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_WIPE_DATA, "wipe-data",
com.android.internal.R.string.policylab_wipeData,
@@ -185,6 +193,10 @@
sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_DISABLE_CAMERA, "disable-camera",
com.android.internal.R.string.policylab_disableCamera,
com.android.internal.R.string.policydesc_disableCamera));
+ sPoliciesDisplayOrder.add(new PolicyInfo(
+ USES_POLICY_DISABLE_KEYGUARD_WIDGETS, "disable-keyguard-widgets",
+ com.android.internal.R.string.policylab_disableKeyguardWidgets,
+ com.android.internal.R.string.policydesc_disableKeyguardWidgets));
for (int i=0; i<sPoliciesDisplayOrder.size(); i++) {
PolicyInfo pi = sPoliciesDisplayOrder.get(i);
@@ -192,25 +204,25 @@
sKnownPolicies.put(pi.tag, pi.ident);
}
}
-
+
/**
* The BroadcastReceiver that implements this device admin component.
*/
final ResolveInfo mReceiver;
-
+
/**
* Whether this should be visible to the user.
*/
boolean mVisible;
-
+
/**
* The policies this administrator needs access to.
*/
int mUsesPolicies;
-
+
/**
* Constructor.
- *
+ *
* @param context The Context in which we are parsing the device admin.
* @param receiver The ResolveInfo returned from the package manager about
* this device admin's component.
@@ -219,9 +231,9 @@
throws XmlPullParserException, IOException {
mReceiver = receiver;
ActivityInfo ai = receiver.activityInfo;
-
+
PackageManager pm = context.getPackageManager();
-
+
XmlResourceParser parser = null;
try {
parser = ai.loadXmlMetaData(pm, DeviceAdminReceiver.DEVICE_ADMIN_META_DATA);
@@ -229,30 +241,30 @@
throw new XmlPullParserException("No "
+ DeviceAdminReceiver.DEVICE_ADMIN_META_DATA + " meta-data");
}
-
+
Resources res = pm.getResourcesForApplication(ai.applicationInfo);
-
+
AttributeSet attrs = Xml.asAttributeSet(parser);
-
+
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.START_TAG) {
}
-
+
String nodeName = parser.getName();
if (!"device-admin".equals(nodeName)) {
throw new XmlPullParserException(
"Meta-data does not start with device-admin tag");
}
-
+
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.DeviceAdmin);
mVisible = sa.getBoolean(
com.android.internal.R.styleable.DeviceAdmin_visible, true);
-
+
sa.recycle();
-
+
int outerDepth = parser.getDepth();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
@@ -290,14 +302,14 @@
mReceiver = ResolveInfo.CREATOR.createFromParcel(source);
mUsesPolicies = source.readInt();
}
-
+
/**
* Return the .apk package that implements this device admin.
*/
public String getPackageName() {
return mReceiver.activityInfo.packageName;
}
-
+
/**
* Return the class name of the receiver component that implements
* this device admin.
@@ -321,20 +333,20 @@
return new ComponentName(mReceiver.activityInfo.packageName,
mReceiver.activityInfo.name);
}
-
+
/**
* Load the user-displayed label for this device admin.
- *
+ *
* @param pm Supply a PackageManager used to load the device admin's
* resources.
*/
public CharSequence loadLabel(PackageManager pm) {
return mReceiver.loadLabel(pm);
}
-
+
/**
* Load user-visible description associated with this device admin.
- *
+ *
* @param pm Supply a PackageManager used to load the device admin's
* resources.
*/
@@ -351,17 +363,17 @@
}
throw new NotFoundException();
}
-
+
/**
* Load the user-displayed icon for this device admin.
- *
+ *
* @param pm Supply a PackageManager used to load the device admin's
* resources.
*/
public Drawable loadIcon(PackageManager pm) {
return mReceiver.loadIcon(pm);
}
-
+
/**
* Returns whether this device admin would like to be visible to the
* user, even when it is not enabled.
@@ -369,7 +381,7 @@
public boolean isVisible() {
return mVisible;
}
-
+
/**
* Return true if the device admin has requested that it be able to use
* the given policy control. The possible policy identifier inputs are:
@@ -382,7 +394,7 @@
public boolean usesPolicy(int policyIdent) {
return (mUsesPolicies & (1<<policyIdent)) != 0;
}
-
+
/**
* Return the XML tag name for the given policy identifier. Valid identifiers
* are as per {@link #usesPolicy(int)}. If the given identifier is not
@@ -391,7 +403,7 @@
public String getTagForPolicy(int policyIdent) {
return sRevKnownPolicies.get(policyIdent).tag;
}
-
+
/** @hide */
public ArrayList<PolicyInfo> getUsedPolicies() {
ArrayList<PolicyInfo> res = new ArrayList<PolicyInfo>();
@@ -403,25 +415,25 @@
}
return res;
}
-
+
/** @hide */
public void writePoliciesToXml(XmlSerializer out)
throws IllegalArgumentException, IllegalStateException, IOException {
out.attribute(null, "flags", Integer.toString(mUsesPolicies));
}
-
+
/** @hide */
public void readPoliciesFromXml(XmlPullParser parser)
throws XmlPullParserException, IOException {
mUsesPolicies = Integer.parseInt(
parser.getAttributeValue(null, "flags"));
}
-
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + "Receiver:");
mReceiver.dump(pw, prefix + " ");
}
-
+
@Override
public String toString() {
return "DeviceAdminInfo{" + mReceiver.activityInfo.name + "}";
@@ -429,7 +441,7 @@
/**
* Used to package this object into a {@link Parcel}.
- *
+ *
* @param dest The {@link Parcel} to be written.
* @param flags The flags used for parceling.
*/
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0b58396..4c55bb3 100755
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1155,6 +1155,16 @@
= "android.app.action.START_ENCRYPTION";
/**
+ * Widgets are enabled in keyguard
+ */
+ public static final int KEYGUARD_DISABLE_WIDGETS_NONE = 0;
+
+ /**
+ * Disable all keyguard widgets
+ */
+ public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 0x7fffffff;
+
+ /**
* Called by an application that is administering the device to
* request that the storage system be encrypted.
*
@@ -1284,6 +1294,46 @@
}
/**
+ * Called by an application that is administering the device to disable adding widgets to
+ * keyguard. After setting this, keyguard widgets will be disabled according to the state
+ * provided.
+ *
+ * <p>The calling device admin must have requested
+ * {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_WIDGETS} to be able to call
+ * this method; if it has not, a security exception will be thrown.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param which {@link DevicePolicyManager#KEYGUARD_DISABLE_WIDGETS_ALL} or
+ * {@link DevicePolicyManager#KEYGUARD_DISABLE_WIDGETS_NONE} (the default).
+ */
+ public void setKeyguardWidgetsDisabled(ComponentName admin, int which) {
+ if (mService != null) {
+ try {
+ mService.setKeyguardWidgetsDisabled(admin, which);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Determine whether or not widgets have been disabled in keyguard either by the current
+ * admin, if specified, or all admins.
+ * @param admin The name of the admin component to check, or null to check if any admins
+ * have disabled widgets in keyguard.
+ */
+ public int getKeyguardWidgetsDisabled(ComponentName admin) {
+ if (mService != null) {
+ try {
+ return mService.getKeyguardWidgetsDisabled(admin);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return KEYGUARD_DISABLE_WIDGETS_NONE;
+ }
+
+ /**
* @hide
*/
public void setActiveAdmin(ComponentName policyReceiver, boolean refreshing) {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9419a62..0b7ec122 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -48,7 +48,7 @@
void setPasswordMinimumNonLetter(in ComponentName who, int length);
int getPasswordMinimumNonLetter(in ComponentName who);
-
+
void setPasswordHistoryLength(in ComponentName who, int length);
int getPasswordHistoryLength(in ComponentName who);
@@ -59,17 +59,17 @@
boolean isActivePasswordSufficient();
int getCurrentFailedPasswordAttempts();
-
+
void setMaximumFailedPasswordsForWipe(in ComponentName admin, int num);
int getMaximumFailedPasswordsForWipe(in ComponentName admin);
-
+
boolean resetPassword(String password, int flags);
-
+
void setMaximumTimeToLock(in ComponentName who, long timeMs);
long getMaximumTimeToLock(in ComponentName who);
-
+
void lockNow();
-
+
void wipeData(int flags);
ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList);
@@ -82,6 +82,9 @@
void setCameraDisabled(in ComponentName who, boolean disabled);
boolean getCameraDisabled(in ComponentName who);
+ void setKeyguardWidgetsDisabled(in ComponentName who, int which);
+ int getKeyguardWidgetsDisabled(in ComponentName who);
+
void setActiveAdmin(in ComponentName policyReceiver, boolean refreshing);
boolean isAdminActive(in ComponentName policyReceiver);
List<ComponentName> getActiveAdmins();
@@ -89,7 +92,7 @@
void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result);
void removeActiveAdmin(in ComponentName policyReceiver);
boolean hasGrantedPolicy(in ComponentName policyReceiver, int usesPolicy);
-
+
void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase,
int numbers, int symbols, int nonletter);
void reportFailedPasswordAttempt();
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index 446f1af..1500b00 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -237,16 +237,17 @@
final boolean mOrderedHint;
final boolean mInitialStickyHint;
final IBinder mToken;
+ final int mSendingUser;
int mResultCode;
String mResultData;
Bundle mResultExtras;
boolean mAbortBroadcast;
boolean mFinished;
-
+
/** @hide */
public PendingResult(int resultCode, String resultData, Bundle resultExtras,
- int type, boolean ordered, boolean sticky, IBinder token) {
+ int type, boolean ordered, boolean sticky, IBinder token, int userId) {
mResultCode = resultCode;
mResultData = resultData;
mResultExtras = resultExtras;
@@ -254,6 +255,7 @@
mOrderedHint = ordered;
mInitialStickyHint = sticky;
mToken = token;
+ mSendingUser = userId;
}
/**
@@ -425,7 +427,12 @@
}
}
}
-
+
+ /** @hide */
+ public int getSendingUserId() {
+ return mSendingUser;
+ }
+
void checkSynchronousHint() {
// Note that we don't assert when receiving the initial sticky value,
// since that may have come from an ordered broadcast. We'll catch
@@ -733,6 +740,11 @@
return mPendingResult;
}
+ /** @hide */
+ public int getSendingUserId() {
+ return mPendingResult.mSendingUser;
+ }
+
/**
* Control inclusion of debugging help for mismatched
* calls to {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 8a4cb44..7438ba8 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1410,9 +1410,35 @@
* @see #unregisterReceiver
*/
public abstract Intent registerReceiver(BroadcastReceiver receiver,
- IntentFilter filter,
- String broadcastPermission,
- Handler scheduler);
+ IntentFilter filter, String broadcastPermission, Handler scheduler);
+
+ /**
+ * @hide
+ * Same as {@link #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
+ * but for a specific user. This receiver will receiver broadcasts that
+ * are sent to the requested user. It
+ * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+ * permission.
+ *
+ * @param receiver The BroadcastReceiver to handle the broadcast.
+ * @param user UserHandle to send the intent to.
+ * @param filter Selects the Intent broadcasts to be received.
+ * @param broadcastPermission String naming a permissions that a
+ * broadcaster must hold in order to send an Intent to you. If null,
+ * no permission is required.
+ * @param scheduler Handler identifying the thread that will receive
+ * the Intent. If null, the main thread of the process will be used.
+ *
+ * @return The first sticky intent found that matches <var>filter</var>,
+ * or null if there are none.
+ *
+ * @see #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler
+ * @see #sendBroadcast
+ * @see #unregisterReceiver
+ */
+ public abstract Intent registerReceiverAsUser(BroadcastReceiver receiver,
+ UserHandle user, IntentFilter filter, String broadcastPermission,
+ Handler scheduler);
/**
* Unregister a previously registered BroadcastReceiver. <em>All</em>
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index e8c63d6..6101f4e 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -425,6 +425,15 @@
scheduler);
}
+ /** @hide */
+ @Override
+ public Intent registerReceiverAsUser(
+ BroadcastReceiver receiver, UserHandle user, IntentFilter filter,
+ String broadcastPermission, Handler scheduler) {
+ return mBase.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
+ scheduler);
+ }
+
@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
mBase.unregisterReceiver(receiver);
diff --git a/core/java/android/content/IIntentReceiver.aidl b/core/java/android/content/IIntentReceiver.aidl
index 6f2f7c4..3d92723 100755
--- a/core/java/android/content/IIntentReceiver.aidl
+++ b/core/java/android/content/IIntentReceiver.aidl
@@ -27,7 +27,7 @@
* {@hide}
*/
oneway interface IIntentReceiver {
- void performReceive(in Intent intent, int resultCode,
- String data, in Bundle extras, boolean ordered, boolean sticky);
+ void performReceive(in Intent intent, int resultCode, String data,
+ in Bundle extras, boolean ordered, boolean sticky, int sendingUser);
}
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index 079241a..6c3cf99 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -114,8 +114,8 @@
mWho = who;
mHandler = handler;
}
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean serialized, boolean sticky) {
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean serialized, boolean sticky, int sendingUser) {
mIntent = intent;
mResultCode = resultCode;
mResultData = data;
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 773e0fe..de97481 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -25,6 +25,7 @@
import android.accounts.Account;
import android.accounts.AccountAndUser;
+import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
@@ -336,6 +337,7 @@
private int mNextHistoryId = 0;
private SparseArray<Boolean> mMasterSyncAutomatically = new SparseArray<Boolean>();
+ private boolean mDefaultMasterSyncAutomatically;
private OnSyncRequestListener mSyncRequestListener;
@@ -345,6 +347,9 @@
mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
+ mDefaultMasterSyncAutomatically = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_syncstorageengine_masterSyncAutomatically);
+
File systemDir = new File(dataDir, "system");
File syncDir = new File(systemDir, "sync");
syncDir.mkdirs();
@@ -780,7 +785,7 @@
public boolean getMasterSyncAutomatically(int userId) {
synchronized (mAuthorities) {
Boolean auto = mMasterSyncAutomatically.get(userId);
- return auto == null ? true : auto;
+ return auto == null ? mDefaultMasterSyncAutomatically : auto;
}
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 3035729..0b320f0 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -372,6 +372,12 @@
public static final int CONFIG_DENSITY = 0x1000;
/**
* Bit in {@link #configChanges} that indicates that the activity
+ * can itself handle the change to layout direction. Set from the
+ * {@link android.R.attr#configChanges} attribute.
+ */
+ public static final int CONFIG_LAYOUT_DIRECTION = 0x2000;
+ /**
+ * Bit in {@link #configChanges} that indicates that the activity
* can itself handle changes to the font scaling factor. Set from the
* {@link android.R.attr#configChanges} attribute. This is
* not a core resource configutation, but a higher-level value, so its
@@ -398,6 +404,7 @@
0x0200, // SCREEN SIZE
0x2000, // SMALLEST SCREEN SIZE
0x0100, // DENSITY
+ 0x4000, // LAYOUT DIRECTION
};
/** @hide
* Convert Java change bits to native.
@@ -434,8 +441,9 @@
* {@link #CONFIG_MCC}, {@link #CONFIG_MNC},
* {@link #CONFIG_LOCALE}, {@link #CONFIG_TOUCHSCREEN},
* {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION},
- * {@link #CONFIG_ORIENTATION}, and {@link #CONFIG_SCREEN_LAYOUT}. Set from the
- * {@link android.R.attr#configChanges} attribute.
+ * {@link #CONFIG_ORIENTATION}, {@link #CONFIG_SCREEN_LAYOUT} and
+ * {@link #CONFIG_LAYOUT_DIRECTION}. Set from the {@link android.R.attr#configChanges}
+ * attribute.
*/
public int configChanges;
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 52b6498..0b77842 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -125,7 +125,25 @@
* <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a>
* resource qualifier. */
public static final int SCREENLAYOUT_LONG_YES = 0x20;
-
+
+ /** Constant for {@link #screenLayout}: bits that encode the layout direction. */
+ public static final int SCREENLAYOUT_LAYOUTDIR_MASK = 0xC0;
+ /** Constant for {@link #screenLayout}: bits shift to get the layout direction. */
+ public static final int SCREENLAYOUT_LAYOUTDIR_SHIFT = 6;
+ /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LAYOUTDIR_MASK}
+ * value indicating that no layout dir has been set. */
+ public static final int SCREENLAYOUT_LAYOUTDIR_UNDEFINED = 0x00;
+ /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LAYOUTDIR_MASK}
+ * value indicating that a layout dir has been set to LTR. */
+ public static final int SCREENLAYOUT_LAYOUTDIR_LTR = 0x01 << SCREENLAYOUT_LAYOUTDIR_SHIFT;
+ /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LAYOUTDIR_MASK}
+ * value indicating that a layout dir has been set to RTL. */
+ public static final int SCREENLAYOUT_LAYOUTDIR_RTL = 0x02 << SCREENLAYOUT_LAYOUTDIR_SHIFT;
+
+ /** Constant for {@link #screenLayout}: a value indicating that screenLayout is undefined */
+ public static final int SCREENLAYOUT_UNDEFINED = SCREENLAYOUT_SIZE_UNDEFINED |
+ SCREENLAYOUT_LONG_UNDEFINED | SCREENLAYOUT_LAYOUTDIR_UNDEFINED;
+
/**
* Special flag we generate to indicate that the screen layout requires
* us to use a compatibility mode for apps that are not modern layout
@@ -146,6 +164,10 @@
* is wider/taller than normal. They may be one of
* {@link #SCREENLAYOUT_LONG_NO} or {@link #SCREENLAYOUT_LONG_YES}.
*
+ * <p>The {@link #SCREENLAYOUT_LAYOUTDIR_MASK} defines whether the screen layout
+ * is either LTR or RTL. They may be one of
+ * {@link #SCREENLAYOUT_LAYOUTDIR_LTR} or {@link #SCREENLAYOUT_LAYOUTDIR_RTL}.
+ *
* <p>See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
* Multiple Screens</a> for more information.
*/
@@ -442,11 +464,6 @@
public int compatSmallestScreenWidthDp;
/**
- * @hide The layout direction associated to the current Locale
- */
- public int layoutDirection;
-
- /**
* @hide Internal book-keeping.
*/
public int seq;
@@ -472,7 +489,6 @@
mnc = o.mnc;
if (o.locale != null) {
locale = (Locale) o.locale.clone();
- layoutDirection = o.layoutDirection;
}
userSetLocale = o.userSetLocale;
touchscreen = o.touchscreen;
@@ -517,10 +533,13 @@
} else {
sb.append(" ?locale");
}
- switch (layoutDirection) {
- case View.LAYOUT_DIRECTION_LTR: /* ltr not interesting */ break;
- case View.LAYOUT_DIRECTION_RTL: sb.append(" rtl"); break;
- default: sb.append(" layoutDir="); sb.append(layoutDirection); break;
+ int layoutDir = (screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK);
+ switch (layoutDir) {
+ case SCREENLAYOUT_LAYOUTDIR_UNDEFINED: sb.append(" ?layoutDir"); break;
+ case SCREENLAYOUT_LAYOUTDIR_LTR: sb.append(" ltr"); break;
+ case SCREENLAYOUT_LAYOUTDIR_RTL: sb.append(" rtl"); break;
+ default: sb.append(" layoutDir=");
+ sb.append(layoutDir >> SCREENLAYOUT_LAYOUTDIR_SHIFT); break;
}
if (smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
sb.append(" sw"); sb.append(smallestScreenWidthDp); sb.append("dp");
@@ -643,13 +662,12 @@
navigation = NAVIGATION_UNDEFINED;
navigationHidden = NAVIGATIONHIDDEN_UNDEFINED;
orientation = ORIENTATION_UNDEFINED;
- screenLayout = SCREENLAYOUT_SIZE_UNDEFINED;
+ screenLayout = SCREENLAYOUT_UNDEFINED;
uiMode = UI_MODE_TYPE_UNDEFINED;
screenWidthDp = compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
screenHeightDp = compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
densityDpi = DENSITY_DPI_UNDEFINED;
- layoutDirection = View.LAYOUT_DIRECTION_LTR;
seq = 0;
}
@@ -685,7 +703,11 @@
changed |= ActivityInfo.CONFIG_LOCALE;
locale = delta.locale != null
? (Locale) delta.locale.clone() : null;
- layoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale);
+ // If locale has changed, then layout direction is also changed ...
+ changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
+ // ... and we need to update the layout direction (represented by the first
+ // 2 most significant bits in screenLayout).
+ setLayoutDirection(locale);
}
if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
{
@@ -727,10 +749,17 @@
changed |= ActivityInfo.CONFIG_ORIENTATION;
orientation = delta.orientation;
}
- if (delta.screenLayout != SCREENLAYOUT_SIZE_UNDEFINED
- && screenLayout != delta.screenLayout) {
+ if (getScreenLayoutNoDirection(delta.screenLayout) !=
+ (SCREENLAYOUT_SIZE_UNDEFINED | SCREENLAYOUT_LONG_UNDEFINED)
+ && (getScreenLayoutNoDirection(screenLayout) !=
+ getScreenLayoutNoDirection(delta.screenLayout))) {
changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
- screenLayout = delta.screenLayout;
+ // We need to preserve the previous layout dir bits if they were defined
+ if ((delta.screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK) == 0) {
+ screenLayout = (screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK)|delta.screenLayout;
+ } else {
+ screenLayout = delta.screenLayout;
+ }
}
if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
&& uiMode != delta.uiMode) {
@@ -771,7 +800,6 @@
if (delta.compatSmallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
compatSmallestScreenWidthDp = delta.compatSmallestScreenWidthDp;
}
-
if (delta.seq != 0) {
seq = delta.seq;
}
@@ -807,6 +835,8 @@
* PackageManager.ActivityInfo.CONFIG_SCREEN_SIZE}, or
* {@link android.content.pm.ActivityInfo#CONFIG_SMALLEST_SCREEN_SIZE
* PackageManager.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE}.
+ * {@link android.content.pm.ActivityInfo#CONFIG_LAYOUT_DIRECTION
+ * PackageManager.ActivityInfo.CONFIG_LAYOUT_DIRECTION}.
*/
public int diff(Configuration delta) {
int changed = 0;
@@ -822,6 +852,7 @@
if (delta.locale != null
&& (locale == null || !locale.equals(delta.locale))) {
changed |= ActivityInfo.CONFIG_LOCALE;
+ changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
}
if (delta.touchscreen != TOUCHSCREEN_UNDEFINED
&& touchscreen != delta.touchscreen) {
@@ -851,8 +882,10 @@
&& orientation != delta.orientation) {
changed |= ActivityInfo.CONFIG_ORIENTATION;
}
- if (delta.screenLayout != SCREENLAYOUT_SIZE_UNDEFINED
- && screenLayout != delta.screenLayout) {
+ if (getScreenLayoutNoDirection(delta.screenLayout) !=
+ (SCREENLAYOUT_SIZE_UNDEFINED | SCREENLAYOUT_LONG_UNDEFINED)
+ && getScreenLayoutNoDirection(screenLayout) !=
+ getScreenLayoutNoDirection(delta.screenLayout)) {
changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
}
if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
@@ -875,7 +908,7 @@
&& densityDpi != delta.densityDpi) {
changed |= ActivityInfo.CONFIG_DENSITY;
}
-
+
return changed;
}
@@ -963,7 +996,6 @@
dest.writeInt(compatScreenWidthDp);
dest.writeInt(compatScreenHeightDp);
dest.writeInt(compatSmallestScreenWidthDp);
- dest.writeInt(layoutDirection);
dest.writeInt(seq);
}
@@ -992,7 +1024,6 @@
compatScreenWidthDp = source.readInt();
compatScreenHeightDp = source.readInt();
compatSmallestScreenWidthDp = source.readInt();
- layoutDirection = source.readInt();
seq = source.readInt();
}
@@ -1100,4 +1131,50 @@
result = 31 * result + densityDpi;
return result;
}
+
+ /**
+ * Set the locale. This is the preferred way for setting up the locale (instead of using the
+ * direct accessor). This will also set the userLocale and layout direction according to
+ * the locale.
+ *
+ * @param loc The locale. Can be null.
+ */
+ public void setLocale(Locale loc) {
+ locale = loc;
+ userSetLocale = true;
+ setLayoutDirection(locale);
+ }
+
+ /**
+ * Return the layout direction. Will be either {@link View#LAYOUT_DIRECTION_LTR} or
+ * {@link View#LAYOUT_DIRECTION_RTL}.
+ *
+ * @return the layout direction
+ */
+ public int getLayoutDirection() {
+ // We need to substract one here as the configuration values are using "0" as undefined thus
+ // having LRT set to "1" and RTL set to "2"
+ return ((screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK) >> SCREENLAYOUT_LAYOUTDIR_SHIFT) - 1;
+ }
+
+ /**
+ * Set the layout direction from the Locale.
+ *
+ * @param locale The Locale. If null will set the layout direction to
+ * {@link View#LAYOUT_DIRECTION_LTR}. If not null will set it to the layout direction
+ * corresponding to the Locale.
+ *
+ * @see {@link View#LAYOUT_DIRECTION_LTR} and {@link View#LAYOUT_DIRECTION_RTL}
+ */
+ public void setLayoutDirection(Locale locale) {
+ // There is a "1" difference between the configuration values for
+ // layout direction and View constants for layout direction, just add "1".
+ final int layoutDirection = 1 + LocaleUtil.getLayoutDirectionFromLocale(locale);
+ screenLayout = (screenLayout&~SCREENLAYOUT_LAYOUTDIR_MASK)|
+ (layoutDirection << SCREENLAYOUT_LAYOUTDIR_SHIFT);
+ }
+
+ private static int getScreenLayoutNoDirection(int screenLayout) {
+ return screenLayout&~SCREENLAYOUT_LAYOUTDIR_MASK;
+ }
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 7559f1e..42a6bdc 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1444,12 +1444,14 @@
}
if (mTmpConfig.locale == null) {
mTmpConfig.locale = Locale.getDefault();
+ mTmpConfig.setLayoutDirection(mTmpConfig.locale);
}
configChanges = mConfiguration.updateFrom(mTmpConfig);
configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
}
if (mConfiguration.locale == null) {
mConfiguration.locale = Locale.getDefault();
+ mConfiguration.setLayoutDirection(mConfiguration.locale);
}
if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
mMetrics.densityDpi = mConfiguration.densityDpi;
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index dc36088..bcb8800 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -86,8 +86,8 @@
* pointers going up.
*
* Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
- * and {@link ScaleGestureDetector#getFocusY()} will return the location
- * of the pointer remaining on the screen.
+ * and {@link ScaleGestureDetector#getFocusY()} will return focal point
+ * of the pointers remaining on the screen.
*
* @param detector The detector reporting the event - use this to
* retrieve extended info about event state.
@@ -128,6 +128,7 @@
private float mCurrSpan;
private float mPrevSpan;
+ private float mInitialSpan;
private float mCurrSpanX;
private float mCurrSpanY;
private float mPrevSpanX;
@@ -135,6 +136,7 @@
private long mCurrTime;
private long mPrevTime;
private boolean mInProgress;
+ private int mSpanSlop;
/**
* Consistency verifier for debugging purposes.
@@ -146,6 +148,7 @@
public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
mContext = context;
mListener = listener;
+ mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2;
}
/**
@@ -176,6 +179,7 @@
if (mInProgress) {
mListener.onScaleEnd(this);
mInProgress = false;
+ mInitialSpan = 0;
}
if (streamComplete) {
@@ -221,18 +225,24 @@
// Dispatch begin/end events as needed.
// If the configuration changes, notify the app to reset its current state by beginning
// a fresh scale event stream.
+ final boolean wasInProgress = mInProgress;
+ mFocusX = focusX;
+ mFocusY = focusY;
if (mInProgress && (span == 0 || configChanged)) {
mListener.onScaleEnd(this);
mInProgress = false;
+ mInitialSpan = span;
}
if (configChanged) {
mPrevSpanX = mCurrSpanX = spanX;
mPrevSpanY = mCurrSpanY = spanY;
- mPrevSpan = mCurrSpan = span;
+ mInitialSpan = mPrevSpan = mCurrSpan = span;
}
- if (!mInProgress && span != 0) {
- mFocusX = focusX;
- mFocusY = focusY;
+ if (!mInProgress && span != 0 &&
+ (wasInProgress || Math.abs(span - mInitialSpan) > mSpanSlop)) {
+ mPrevSpanX = mCurrSpanX = spanX;
+ mPrevSpanY = mCurrSpanY = spanY;
+ mPrevSpan = mCurrSpan = span;
mInProgress = mListener.onScaleBegin(this);
}
@@ -241,8 +251,6 @@
mCurrSpanX = spanX;
mCurrSpanY = spanY;
mCurrSpan = span;
- mFocusX = focusX;
- mFocusY = focusY;
boolean updatePrev = true;
if (mInProgress) {
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 6616894..db05e10 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -262,12 +262,8 @@
IBinder displayToken, SurfaceTexture surfaceTexture);
private static native void nativeSetDisplayLayerStack(
IBinder displayToken, int layerStack);
- private static native void nativeSetDisplayOrientation(
- IBinder displayToken, int orientation);
- private static native void nativeSetDisplayViewport(
- IBinder displayToken, Rect viewport);
- private static native void nativeSetDisplayFrame(
- IBinder displayToken, Rect frame);
+ private static native void nativeSetDisplayProjection(
+ IBinder displayToken, int orientation, Rect layerStackRect, Rect displayRect);
private static native boolean nativeGetDisplayInfo(
IBinder displayToken, PhysicalDisplayInfo outInfo);
@@ -617,33 +613,18 @@
}
/** @hide */
- public static void setDisplayOrientation(IBinder displayToken, int orientation) {
+ public static void setDisplayProjection(IBinder displayToken,
+ int orientation, Rect layerStackRect, Rect displayRect) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
- nativeSetDisplayOrientation(displayToken, orientation);
- }
-
- /** @hide */
- public static void setDisplayViewport(IBinder displayToken, Rect viewport) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
+ if (layerStackRect == null) {
+ throw new IllegalArgumentException("layerStackRect must not be null");
}
- if (viewport == null) {
- throw new IllegalArgumentException("viewport must not be null");
+ if (displayRect == null) {
+ throw new IllegalArgumentException("displayRect must not be null");
}
- nativeSetDisplayViewport(displayToken, viewport);
- }
-
- /** @hide */
- public static void setDisplayFrame(IBinder displayToken, Rect frame) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
- if (frame == null) {
- throw new IllegalArgumentException("frame must not be null");
- }
- nativeSetDisplayFrame(displayToken, frame);
+ nativeSetDisplayProjection(displayToken, orientation, layerStackRect, displayRect);
}
/** @hide */
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 131f0ae..08e30aa 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -34,6 +34,7 @@
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Printer;
+import android.util.Slog;
import android.util.Xml;
import java.io.IOException;
@@ -169,7 +170,10 @@
a.getBoolean(com.android.internal.R.styleable
.InputMethod_Subtype_isAuxiliary, false),
a.getBoolean(com.android.internal.R.styleable
- .InputMethod_Subtype_overridesImplicitlyEnabledSubtype, false));
+ .InputMethod_Subtype_overridesImplicitlyEnabledSubtype, false),
+ a.getInt(com.android.internal.R.styleable
+ .InputMethod_Subtype_subtypeId, 0 /* use Arrays.hashCode */)
+ );
if (!subtype.isAuxiliary()) {
mIsAuxIme = false;
}
@@ -194,6 +198,9 @@
final InputMethodSubtype subtype = additionalSubtypes.get(i);
if (!mSubtypes.contains(subtype)) {
mSubtypes.add(subtype);
+ } else {
+ Slog.w(TAG, "Duplicated subtype definition found: "
+ + subtype.getLocale() + ", " + subtype.getMode());
}
}
}
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index b7c94a3..7895e6f 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -55,13 +55,14 @@
private final int mSubtypeHashCode;
private final int mSubtypeIconResId;
private final int mSubtypeNameResId;
+ private final int mSubtypeId;
private final String mSubtypeLocale;
private final String mSubtypeMode;
private final String mSubtypeExtraValue;
private volatile HashMap<String, String> mExtraValueHashMapCache;
/**
- * Constructor.
+ * Constructor with no subtype ID specified, overridesImplicitlyEnabledSubtype not specified.
* @param nameId Resource ID of the subtype name string. The string resource may have exactly
* one %s in it. If there is, the %s part will be replaced with the locale's display name by
* the formatter. Please refer to {@link #getDisplayName} for details.
@@ -87,7 +88,7 @@
}
/**
- * Constructor.
+ * Constructor with no subtype ID specified.
* @param nameId Resource ID of the subtype name string. The string resource may have exactly
* one %s in it. If there is, the %s part will be replaced with the locale's display name by
* the formatter. Please refer to {@link #getDisplayName} for details.
@@ -112,6 +113,41 @@
*/
public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype) {
+ this(nameId, iconId, locale, mode, extraValue, isAuxiliary,
+ overridesImplicitlyEnabledSubtype, 0);
+ }
+
+ /**
+ * Constructor.
+ * @param nameId Resource ID of the subtype name string. The string resource may have exactly
+ * one %s in it. If there is, the %s part will be replaced with the locale's display name by
+ * the formatter. Please refer to {@link #getDisplayName} for details.
+ * @param iconId Resource ID of the subtype icon drawable.
+ * @param locale The locale supported by the subtype
+ * @param mode The mode supported by the subtype
+ * @param extraValue The extra value of the subtype. This string is free-form, but the API
+ * supplies tools to deal with a key-value comma-separated list; see
+ * {@link #containsExtraValueKey} and {@link #getExtraValueOf}.
+ * @param isAuxiliary true when this subtype is auxiliary, false otherwise. An auxiliary
+ * subtype will not be shown in the list of enabled IMEs for choosing the current IME in
+ * the Settings even when this subtype is enabled. Please note that this subtype will still
+ * be shown in the list of IMEs in the IME switcher to allow the user to tentatively switch
+ * to this subtype while an IME is shown. The framework will never switch the current IME to
+ * this subtype by {@link android.view.inputmethod.InputMethodManager#switchToLastInputMethod}.
+ * The intent of having this flag is to allow for IMEs that are invoked in a one-shot way as
+ * auxiliary input mode, and return to the previous IME once it is finished (e.g. voice input).
+ * @param overridesImplicitlyEnabledSubtype true when this subtype should be enabled by default
+ * if no other subtypes in the IME are enabled explicitly. Note that a subtype with this
+ * parameter being true will not be shown in the list of subtypes in each IME's subtype enabler.
+ * Having an "automatic" subtype is an example use of this flag.
+ * @param id The unique ID for the subtype. The input method framework keeps track of enabled
+ * subtypes by ID. When the IME package gets upgraded, enabled IDs will stay enabled even if
+ * other attributes are different. If the ID is unspecified or 0,
+ * Arrays.hashCode(new Object[] {locale, mode, extraValue,
+ * isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead.
+ */
+ public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
+ boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, int id) {
mSubtypeNameResId = nameId;
mSubtypeIconResId = iconId;
mSubtypeLocale = locale != null ? locale : "";
@@ -119,8 +155,11 @@
mSubtypeExtraValue = extraValue != null ? extraValue : "";
mIsAuxiliary = isAuxiliary;
mOverridesImplicitlyEnabledSubtype = overridesImplicitlyEnabledSubtype;
- mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue,
- mIsAuxiliary, mOverridesImplicitlyEnabledSubtype);
+ // If hashCode() of this subtype is 0 and you want to specify it as an id of this subtype,
+ // just specify 0 as this subtype's id. Then, this subtype's id is treated as 0.
+ mSubtypeHashCode = id != 0 ? id : hashCodeInternal(mSubtypeLocale, mSubtypeMode,
+ mSubtypeExtraValue, mIsAuxiliary, mOverridesImplicitlyEnabledSubtype);
+ mSubtypeId = id;
}
InputMethodSubtype(Parcel source) {
@@ -135,8 +174,8 @@
mSubtypeExtraValue = s != null ? s : "";
mIsAuxiliary = (source.readInt() == 1);
mOverridesImplicitlyEnabledSubtype = (source.readInt() == 1);
- mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue,
- mIsAuxiliary, mOverridesImplicitlyEnabledSubtype);
+ mSubtypeHashCode = source.readInt();
+ mSubtypeId = source.readInt();
}
/**
@@ -288,6 +327,9 @@
public boolean equals(Object o) {
if (o instanceof InputMethodSubtype) {
InputMethodSubtype subtype = (InputMethodSubtype) o;
+ if (subtype.mSubtypeId != 0 || mSubtypeId != 0) {
+ return (subtype.hashCode() == hashCode());
+ }
return (subtype.hashCode() == hashCode())
&& (subtype.getNameResId() == getNameResId())
&& (subtype.getMode().equals(getMode()))
@@ -313,6 +355,8 @@
dest.writeString(mSubtypeExtraValue);
dest.writeInt(mIsAuxiliary ? 1 : 0);
dest.writeInt(mOverridesImplicitlyEnabledSubtype ? 1 : 0);
+ dest.writeInt(mSubtypeHashCode);
+ dest.writeInt(mSubtypeId);
}
public static final Parcelable.Creator<InputMethodSubtype> CREATOR
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 591b87f..9334036 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -1346,20 +1346,40 @@
private void onHandleUiTouchEvent(MotionEvent ev) {
final ScaleGestureDetector detector =
- mZoomManager.getMultiTouchGestureDetector();
+ mZoomManager.getScaleGestureDetector();
- float x = ev.getX();
- float y = ev.getY();
+ int action = ev.getActionMasked();
+ final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
+ final boolean configChanged =
+ action == MotionEvent.ACTION_POINTER_UP ||
+ action == MotionEvent.ACTION_POINTER_DOWN;
+ final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
+
+ // Determine focal point
+ float sumX = 0, sumY = 0;
+ final int count = ev.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ sumX += ev.getX(i);
+ sumY += ev.getY(i);
+ }
+ final int div = pointerUp ? count - 1 : count;
+ float x = sumX / div;
+ float y = sumY / div;
+
+ if (configChanged) {
+ mLastTouchX = Math.round(x);
+ mLastTouchY = Math.round(y);
+ mLastTouchTime = ev.getEventTime();
+ mWebView.cancelLongPress();
+ mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+ }
if (detector != null) {
detector.onTouchEvent(ev);
if (detector.isInProgress()) {
mLastTouchTime = ev.getEventTime();
- x = detector.getFocusX();
- y = detector.getFocusY();
- mWebView.cancelLongPress();
- mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
if (!mZoomManager.supportsPanDuringZoom()) {
return;
}
@@ -1370,14 +1390,9 @@
}
}
- int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_POINTER_DOWN) {
cancelTouch();
action = MotionEvent.ACTION_DOWN;
- } else if (action == MotionEvent.ACTION_POINTER_UP) {
- // set mLastTouchX/Y to the remaining points for multi-touch.
- mLastTouchX = Math.round(x);
- mLastTouchY = Math.round(y);
} else if (action == MotionEvent.ACTION_MOVE) {
// negative x or y indicate it is on the edge, skip it.
if (x < 0 || y < 0) {
@@ -4385,7 +4400,7 @@
// A multi-finger gesture can look like a long press; make sure we don't take
// long press actions if we're scaling.
- final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
+ final ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
if (detector != null && detector.isInProgress()) {
return false;
}
@@ -5823,7 +5838,7 @@
* and the middle point for multi-touch.
*/
private void handleTouchEventCommon(MotionEvent event, int action, int x, int y) {
- ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
+ ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
long eventTime = event.getEventTime();
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 8830119..80a6782 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -204,7 +204,7 @@
*/
private boolean mAllowPanAndScale;
- // use the framework's ScaleGestureDetector to handle multi-touch
+ // use the framework's ScaleGestureDetector to handle scaling gestures
private ScaleGestureDetector mScaleDetector;
private boolean mPinchToZoomAnimating = false;
@@ -768,7 +768,7 @@
return isZoomAnimating();
}
- public ScaleGestureDetector getMultiTouchGestureDetector() {
+ public ScaleGestureDetector getScaleGestureDetector() {
return mScaleDetector;
}
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index a30567f..9b62a51 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -1517,6 +1517,9 @@
// because the voice search activity will always need to insert "QUERY" into
// it anyway.
Bundle queryExtras = new Bundle();
+ if (mAppSearchData != null) {
+ queryExtras.putParcelable(SearchManager.APP_DATA, mAppSearchData);
+ }
// Now build the intent to launch the voice search. Add all necessary
// extras to launch the voice recognizer, and then all the necessary extras
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f502de4..c9688c8 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -302,6 +302,7 @@
// The alignment to pass to Layout, or null if not resolved.
private Layout.Alignment mLayoutAlignment;
+ private int mResolvedTextAlignment;
private boolean mResolvedDrawables;
@@ -5639,9 +5640,8 @@
@Override
public void onResolvedLayoutDirectionReset() {
if (mLayoutAlignment != null) {
- int resolvedTextAlignment = getResolvedTextAlignment();
- if (resolvedTextAlignment == TEXT_ALIGNMENT_VIEW_START ||
- resolvedTextAlignment == TEXT_ALIGNMENT_VIEW_END) {
+ if (mResolvedTextAlignment == TEXT_ALIGNMENT_VIEW_START ||
+ mResolvedTextAlignment == TEXT_ALIGNMENT_VIEW_END) {
mLayoutAlignment = null;
}
}
@@ -5649,8 +5649,8 @@
private Layout.Alignment getLayoutAlignment() {
if (mLayoutAlignment == null) {
- int textAlign = getResolvedTextAlignment();
- switch (textAlign) {
+ mResolvedTextAlignment = getResolvedTextAlignment();
+ switch (mResolvedTextAlignment) {
case TEXT_ALIGNMENT_GRAVITY:
switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
case Gravity.START:
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index e8bf9d9..485bd37 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -379,7 +379,7 @@
// We can resolve the Gravity here by using the Locale for getting
// the layout direction
final Configuration config = mView.getContext().getResources().getConfiguration();
- final int gravity = Gravity.getAbsoluteGravity(mGravity, config.layoutDirection);
+ final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
mParams.gravity = gravity;
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
mParams.horizontalWeight = 1.0f;
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index 7d1231e..f173327 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -243,10 +243,9 @@
IActivityManager am = ActivityManagerNative.getDefault();
Configuration config = am.getConfiguration();
- config.locale = locale;
-
- // indicate this isn't some passing default - the user wants this remembered
- config.userSetLocale = true;
+ // Will set userSetLocale to indicate this isn't some passing default - the user
+ // wants this remembered
+ config.setLocale(locale);
am.updateConfiguration(config);
// Trigger the dirty bit for the Settings Provider.
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 4d5e680..4fbfab6 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -647,38 +647,24 @@
SurfaceComposerClient::setDisplayLayerStack(token, layerStack);
}
-static void nativeSetDisplayOrientation(JNIEnv* env, jclass clazz,
- jobject tokenObj, jint orientation) {
+static void nativeSetDisplayProjection(JNIEnv* env, jclass clazz,
+ jobject tokenObj, jint orientation, jobject rect1Obj, jobject rect2Obj) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == NULL) return;
- SurfaceComposerClient::setDisplayOrientation(token, orientation);
-}
+ Rect rect1;
+ rect1.left = env->GetIntField(rect1Obj, gRectClassInfo.left);
+ rect1.top = env->GetIntField(rect1Obj, gRectClassInfo.top);
+ rect1.right = env->GetIntField(rect1Obj, gRectClassInfo.right);
+ rect1.bottom = env->GetIntField(rect1Obj, gRectClassInfo.bottom);
-static void nativeSetDisplayViewport(JNIEnv* env, jclass clazz,
- jobject tokenObj, jobject rectObj) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
- if (token == NULL) return;
+ Rect rect2;
+ rect2.left = env->GetIntField(rect2Obj, gRectClassInfo.left);
+ rect2.top = env->GetIntField(rect2Obj, gRectClassInfo.top);
+ rect2.right = env->GetIntField(rect2Obj, gRectClassInfo.right);
+ rect2.bottom = env->GetIntField(rect2Obj, gRectClassInfo.bottom);
- Rect rect;
- rect.left = env->GetIntField(rectObj, gRectClassInfo.left);
- rect.top = env->GetIntField(rectObj, gRectClassInfo.top);
- rect.right = env->GetIntField(rectObj, gRectClassInfo.right);
- rect.bottom = env->GetIntField(rectObj, gRectClassInfo.bottom);
- SurfaceComposerClient::setDisplayViewport(token, rect);
-}
-
-static void nativeSetDisplayFrame(JNIEnv* env, jclass clazz,
- jobject tokenObj, jobject rectObj) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
- if (token == NULL) return;
-
- Rect rect;
- rect.left = env->GetIntField(rectObj, gRectClassInfo.left);
- rect.top = env->GetIntField(rectObj, gRectClassInfo.top);
- rect.right = env->GetIntField(rectObj, gRectClassInfo.right);
- rect.bottom = env->GetIntField(rectObj, gRectClassInfo.bottom);
- SurfaceComposerClient::setDisplayFrame(token, rect);
+ SurfaceComposerClient::setDisplayProjection(token, orientation, rect1, rect2);
}
static jboolean nativeGetDisplayInfo(JNIEnv* env, jclass clazz,
@@ -818,12 +804,8 @@
(void*)nativeSetDisplaySurface },
{"nativeSetDisplayLayerStack", "(Landroid/os/IBinder;I)V",
(void*)nativeSetDisplayLayerStack },
- {"nativeSetDisplayOrientation", "(Landroid/os/IBinder;I)V",
- (void*)nativeSetDisplayOrientation },
- {"nativeSetDisplayViewport", "(Landroid/os/IBinder;Landroid/graphics/Rect;)V",
- (void*)nativeSetDisplayViewport },
- {"nativeSetDisplayFrame", "(Landroid/os/IBinder;Landroid/graphics/Rect;)V",
- (void*)nativeSetDisplayFrame },
+ {"nativeSetDisplayProjection", "(Landroid/os/IBinder;ILandroid/graphics/Rect;Landroid/graphics/Rect;)V",
+ (void*)nativeSetDisplayProjection },
{"nativeGetDisplayInfo", "(Landroid/os/IBinder;Landroid/view/Surface$PhysicalDisplayInfo;)Z",
(void*)nativeGetDisplayInfo },
{"nativeCopyFrom", "(Landroid/view/Surface;)V",
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml
index df999f0..c6ddd1b 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml
@@ -109,7 +109,7 @@
/>
<TextView
- android:id="@+id/propertyOf"
+ android:id="@+id/owner_info"
android:lineSpacingExtra="8dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml
index 565785b..765dc95 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml
@@ -109,7 +109,7 @@
/>
<TextView
- android:id="@+id/propertyOf"
+ android:id="@+id/owner_info"
android:lineSpacingExtra="8dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/core/res/res/layout/keyguard_navigation.xml b/core/res/res/layout/keyguard_navigation.xml
index f9e3ef8..a033101 100644
--- a/core/res/res/layout/keyguard_navigation.xml
+++ b/core/res/res/layout/keyguard_navigation.xml
@@ -31,11 +31,11 @@
android:padding="10dip"
android:clickable="true">
- <TextView
- android:layout_width="20dip"
- android:layout_height="wrap_content"
- android:textSize="28dp"
- android:text="@string/kg_temp_back_string" />
+ <ImageView
+ android:src="?android:attr/homeAsUpIndicator"
+ android:layout_gravity="center_vertical|start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
<!-- message area for security screen -->
<TextView
@@ -43,6 +43,7 @@
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
+ android:layout_gravity="end"
android:singleLine="true"
android:ellipsize="marquee"
android:layout_marginEnd="6dip"
diff --git a/core/res/res/layout/keyguard_status_view.xml b/core/res/res/layout/keyguard_status_view.xml
index f8d05b7..37e6779 100644
--- a/core/res/res/layout/keyguard_status_view.xml
+++ b/core/res/res/layout/keyguard_status_view.xml
@@ -94,7 +94,7 @@
</LinearLayout>
<TextView
- android:id="@*android:id/status1"
+ android:id="@+id/status1"
android:layout_gravity="end"
android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
android:singleLine="true"
@@ -105,7 +105,18 @@
/>
<TextView
- android:id="@*android:id/carrier"
+ android:id="@+id/owner_info"
+ android:layout_gravity="end"
+ android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
+ android:drawablePadding="4dip"
+ />
+
+ <TextView
+ android:id="@+id/carrier"
android:layout_gravity="end"
android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
android:singleLine="true"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 4f67789..2eaf87a 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Laat die program toe om na die SD-kaart te skryf."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"verander/vee uit interne mediabergingsinhoud"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Laat die program toe om die inhoud van die interne mediaberging te verander."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"lees die kaslêerstelsel"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Laat die program toe om die kaslêerstelsel te lees en skryf."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"maak/ontvang internetoproepe"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN is geaktiveer deur <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Raak om die netwerk te bestuur."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Gekoppel aan <xliff:g id="SESSION">%s</xliff:g>. Raak om die netwerk te bestuur."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Altydaan-VPN koppel tans..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Altydaan-VPN gekoppel"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Altydaan-VPN se fout"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Raak om verbinding terug te stel"</string>
<string name="upload_file" msgid="2897957172366730416">"Kies lêer"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Geen lêer gekies nie"</string>
<string name="reset" msgid="2448168080964209908">"Stel terug"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index b85ad6a..bbe666a 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"መተግበሪያውን ወደ SD ካርድ ለመፃፍ ይፈቅዳል።"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"የውስጥ ማህደረ መረጃ ማከማቻ ይዘቶችን ቀይር/ሰርዝ"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"የውስጥ ማህደረ መረጃ ማከማቻ ይዘትን ለመቀየር ለመተግበሪያው ይፈቅዳሉ፡፡"</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"የመሸጎጫ ስርዓተ ፋይል ድረስ"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"መሸጎጫ ስርዓተ ፋይል ለማንበብ እና ለመፃፍ ለመተግበሪያው ይፈቅዳሉ።"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"የበይነ መረብ ጥሪዎች አድርግ/ተቀበል"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index bf00a81..9b44dcd 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"للسماح للتطبيق بالكتابة إلى بطاقة SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"تعديل/حذف محتويات وحدة تخزين الوسائط الداخلية"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"للسماح للتطبيق بتعديل محتويات وحدة تخزين الوسائط الداخلية."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"الدخول إلى نظام ملفات ذاكرة التخزين المؤقت"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"للسماح للتطبيق بقراءة نظام ملفات ذاكرة التخزين المؤقت والكتابة به."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"إجراء/تلقي مكالمات عبر الإنترنت"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"تم تنشيط VPN بواسطة <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"المس لإدارة الشبكة."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"تم الاتصال بـ <xliff:g id="SESSION">%s</xliff:g>. المس لإدارة الشبكة."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"جارٍ الاتصال بشبكة ظاهرية خاصة (VPN) دائمة التشغيل..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"تم الاتصال بشبكة ظاهرية خاصة (VPN) دائمة التشغيل"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"خطأ بشبكة ظاهرية خاصة (VPN) دائمة التشغيل"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"المس لإعادة تعيين الاتصال"</string>
<string name="upload_file" msgid="2897957172366730416">"اختيار ملف"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"لم يتم اختيار أي ملف"</string>
<string name="reset" msgid="2448168080964209908">"إعادة تعيين"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 04053f6..7d8c3f3 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дазваляе прыкладанням запісваць на SD-карту."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"змяніць/выдаліць унутраныя носьбіты змесціва"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Дазваляе прыкладанням змяняць змесціва ўнутранай памяці."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"доступ да файлавай сістэмы кэша"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Дазваляе прыкладанню счытваць і выконваць запіс у файлавую сістэму кэш-памяці."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"рабіць/прымаць iнтэрнэт-выклікі"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 0813df0..85a2c9e 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Разрешава на приложението да записва върху SD картата."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"пром./изтр. на съдърж. на вътр. мултим. хранил."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Разрешава на приложението да променя съдържанието на вътрешното мултимедийно хранилище."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"достъп до файловата система на кеша"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Разрешава на приложението да чете и записва във файловата система на кеша."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"извършване/получаване на интернет обаждания"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN е активирана от <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Докоснете за управление на мрежата."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Свързана със: <xliff:g id="SESSION">%s</xliff:g>. Докоснете, за да управлявате мрежата."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Установява се връзка с винаги включената виртуална частна мрежа (VPN)…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Установена е връзка с винаги включената виртуална частна мрежа (VPN)"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Грешка във винаги включената виртуална частна мрежа (VPN)"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Докоснете, за да възстановите връзката"</string>
<string name="upload_file" msgid="2897957172366730416">"Избор на файл"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Няма избран файл"</string>
<string name="reset" msgid="2448168080964209908">"Повторно задаване"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 5924751..86c9f36 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permet a l\'aplicació escriure a la targeta SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Canvia/esborra emmagatz. intern"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permet que l\'aplicació modifiqui el contingut de l\'emmagatzematge multimèdia intern."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"accedir al sistema de fitxers de la memòria cau"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permet que l\'aplicació llegeixi el sistema de fitxers de la memòria cau i que hi escrigui."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"fer/rebre trucades per Internet"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 9395aec..5264a43 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Umožňuje aplikaci zapisovat na kartu SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Upravit/smazat interní úlož."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Umožňuje aplikaci upravovat obsah interního úložiště médií."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"přistupovat do souborového systému mezipaměti"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Umožňuje aplikaci číst a zapisovat do souborového systému mezipaměti."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"uskutečňovat a přijímat internetové hovory"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikace <xliff:g id="APP">%s</xliff:g> aktivovala síť VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotykem zobrazíte správu sítě."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Připojeno k relaci <xliff:g id="SESSION">%s</xliff:g>. Dotykem můžete síť spravovat."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Připojování k trvalé síti VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Je připojena trvalá síť VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Chyba trvalé sítě VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Klepnutím resetujete připojení"</string>
<string name="upload_file" msgid="2897957172366730416">"Zvolit soubor"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Není vybrán žádný soubor"</string>
<string name="reset" msgid="2448168080964209908">"Resetovat"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 050eca8..0aeef97 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Tillader, at appen kan skrive til SD-kortet."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Rediger/slet internt medielagringsindhold"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Tillader, appen kan ændre indholdet af det interne medielager."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"få adgang til cache-filsystemet"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Tillader, at appen kan læse og skrive i cachefilsystemet."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"foretage/modtage internetopkald"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 4fdc411..8b1e8bd 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Ermöglicht der App, auf die SD-Karte zu schreiben"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Intern. Mediensp. änd./löschen"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Ermöglicht der App, den Inhalt des internen Medienspeichers zu ändern"</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"Zugriff auf das Cache-Dateisystem"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Ermöglicht der App Lese- und Schreibzugriff auf das Cache-Dateisystem"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"Internetanrufe tätigen/annehmen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 181dc3f..a299dab 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Επιτρέπει στην εφαρμογή την εγγραφή στην κάρτα SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"τροπ./διαγ. περ. απ. εσ. μνήμ."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Επιτρέπει στην εφαρμογή να τροποποιήσει τα περιεχόμενα των εσωτερικών μέσων αποθήκευσης."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"πρόσβαση στο σύστημα αρχείων προσωρινής μνήμης"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Επιτρέπει στην εφαρμογή την ανάγνωση και την εγγραφή του συστήματος αρχείων προσωρινής μνήμης."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"πραγματοποίηση/λήψη κλήσεων μέσω Διαδικτύου"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 2dd96c7..2d96b9a 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Allows the app to write to the SD card."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modify/delete internal media storage contents"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Allows the app to modify the contents of the internal media storage."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"access the cache file system"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Allows the app to read and write the cache file system."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"make/receive Internet calls"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 3e9694c..bd41ffc 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Admite que la aplicación escriba en la tarjeta SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modificar/eliminar los contenidos del almacenamientos de medios internos"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite que la aplicación modifique el contenido del almacenamiento de medios interno."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"Acceder al sistema de archivos caché"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permite que la aplicación lea y escriba el sistema de archivos almacenado en caché."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"realizar o recibir llamadas por Internet"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN está activado por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca para administrar la red."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Conectado a <xliff:g id="SESSION">%s</xliff:g>. Toca para administrar la red."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Estableciendo conexión con VPN activada ininterrumpidamente..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Se estableció conexión con la VPN activada ininterrumpidamente."</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Se produjo un error al establecer conexión con la VPN activada ininterrumpidamente."</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Toca para restablecer la conexión."</string>
<string name="upload_file" msgid="2897957172366730416">"Elegir archivo"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"No se seleccionó un archivo."</string>
<string name="reset" msgid="2448168080964209908">"Restablecer"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 32f7e8d..c962de0 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que la aplicación escriba en la tarjeta SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modificar o eliminar el contenido del almacenamiento de medios interno"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite que la aplicación modifique el contenido del almacenamiento multimedia interno."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"acceder al sistema de archivos almacenado en caché"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permite que la aplicación lea y escriba el sistema de archivos almacenado en caché."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"realizar/recibir llamadas por Internet"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN activada por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca para administrar la red."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Conectado a <xliff:g id="SESSION">%s</xliff:g>. Toca para administrar la red."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Conectando VPN siempre activada…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN siempre activada conectada"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Error de VPN siempre activada"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Toca para restablecer la conexión."</string>
<string name="upload_file" msgid="2897957172366730416">"Seleccionar archivo"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Archivo no seleccionado"</string>
<string name="reset" msgid="2448168080964209908">"Restablecer"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 32f6ade..d8a0ec5 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Võimaldab rakendusel kirjutada SD-kaardile."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"sisemälu sisu muutm./kustut."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Võimaldab rakendusel muuta sisemise andmekandja sisu."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"juurdepääs vahemälu failisüsteemile"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Võimaldab rakendusel vahemälu failisüsteemi lugeda ja kirjutada."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"Interneti-kõnede tegemine/vastuvõtmine"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN-i aktiveeris <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Võrgu haldamiseks puudutage."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Ühendatud seansiga <xliff:g id="SESSION">%s</xliff:g>. Võrgu haldamiseks puudutage."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Ühendamine alati sees VPN-iga …"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Ühendatud alati sees VPN-iga"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Alati see VPN-i viga"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Ühenduse lähtestamiseks puudutage"</string>
<string name="upload_file" msgid="2897957172366730416">"Valige fail"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Ühtegi faili pole valitud"</string>
<string name="reset" msgid="2448168080964209908">"Lähtesta"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index a466354..fb9a1ff 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -85,7 +85,7 @@
<string name="serviceClassFAX" msgid="5566624998840486475">"نمابر"</string>
<string name="serviceClassSMS" msgid="2015460373701527489">"پیامک"</string>
<string name="serviceClassDataAsync" msgid="4523454783498551468">"غیر همگام"</string>
- <string name="serviceClassDataSync" msgid="7530000519646054776">"همگام سازی"</string>
+ <string name="serviceClassDataSync" msgid="7530000519646054776">"همگامسازی"</string>
<string name="serviceClassPacket" msgid="6991006557993423453">"بسته"</string>
<string name="serviceClassPAD" msgid="3235259085648271037">"PAD"</string>
<string name="roamingText0" msgid="7170335472198694945">"نشانگر رومینگ روشن"</string>
@@ -126,8 +126,8 @@
<string name="httpErrorFileNotFound" msgid="6203856612042655084">"فایل درخواستی پیدا نشد."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"درخواستهای زیادی در حال پردازش است. بعداً دوباره امتحان کنید."</string>
<string name="notification_title" msgid="8967710025036163822">"خطای ورود به سیستم برای <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
- <string name="contentServiceSync" msgid="8353523060269335667">"همگام سازی"</string>
- <string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"همگام سازی"</string>
+ <string name="contentServiceSync" msgid="8353523060269335667">"همگامسازی"</string>
+ <string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"همگامسازی"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"تعداد موارد حذف شده <xliff:g id="CONTENT_TYPE">%s</xliff:g> بسیار زیاد است."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"حافظه رایانهٔ لوحی پر است! برخی از فایلها را حذف کنید تا فضا آزاد شود."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"حافظه تلفن پر است. بعضی از فایلها را حذف کنید تا فضا آزاد شود."</string>
@@ -464,8 +464,8 @@
<string name="permdesc_devicePower" product="tablet" msgid="6689862878984631831">"به برنامه اجازه میدهد رایانهٔ لوحی را روشن یا خاموش کند."</string>
<string name="permdesc_devicePower" product="default" msgid="6037057348463131032">"به برنامه اجازه میدهد گوشی را روشن یا خاموش کند."</string>
<string name="permlab_factoryTest" msgid="3715225492696416187">"اجرا در حالت تست کارخانه"</string>
- <string name="permdesc_factoryTest" product="tablet" msgid="3952059318359653091">"اجرا به عنوان تست سازنده سطح پایین، امکان دسترسی کامل به سختافزار رایانهٔ لوحی شما را فراهم میآورد. فقط زمانی که رایانهٔ لوحی در حالت تست سازنده در حال اجراست قابل دسترسی است."</string>
- <string name="permdesc_factoryTest" product="default" msgid="8136644990319244802">"اجرا به عنوان تست سازنده سطح پایین، امکان دسترسی کامل به سختافزار تلفن شما را فراهم میآورد. فقط زمانی که تلفن در حالت تست سازنده در حال اجراست قابل دسترسی است."</string>
+ <string name="permdesc_factoryTest" product="tablet" msgid="3952059318359653091">"اجرا بهعنوان تست سازنده سطح پایین، امکان دسترسی کامل به سختافزار رایانهٔ لوحی شما را فراهم میآورد. فقط زمانی که رایانهٔ لوحی در حالت تست سازنده در حال اجراست قابل دسترسی است."</string>
+ <string name="permdesc_factoryTest" product="default" msgid="8136644990319244802">"اجرا بهعنوان تست سازنده سطح پایین، امکان دسترسی کامل به سختافزار تلفن شما را فراهم میآورد. فقط زمانی که تلفن در حالت تست سازنده در حال اجراست قابل دسترسی است."</string>
<string name="permlab_setWallpaper" msgid="6627192333373465143">"تنظیم تصویر زمینه"</string>
<string name="permdesc_setWallpaper" msgid="7373447920977624745">"به برنامه اجازه میدهد تا تصویر زمینه سیستم را تنظیم کند."</string>
<string name="permlab_setWallpaperHints" msgid="3278608165977736538">"تنظیم اندازه تصویر زمینه"</string>
@@ -478,7 +478,7 @@
<string name="permlab_setTimeZone" msgid="2945079801013077340">"تنظیم منطقهٔ زمانی"</string>
<string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"به برنامه اجازه میدهد تا منطقهٔ زمانی رایانهٔ لوحی را تغییر دهد."</string>
<string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"به برنامه اجازه میدهد تا منطقهٔ زمانی تلفن را تغییر دهد."</string>
- <string name="permlab_accountManagerService" msgid="4829262349691386986">"عملکرد به عنوان AccountManagerService"</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"عملکرد بهعنوان AccountManagerService"</string>
<string name="permdesc_accountManagerService" msgid="1948455552333615954">"به برنامه اجازه میدهد با AccountAuthenticators تماس برقرار کند."</string>
<string name="permlab_getAccounts" msgid="1086795467760122114">"یافتن حسابها در دستگاه"</string>
<string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"به برنامه اجازه میدهد به لیست حسابهای شناخته شده توسط رایانهٔ لوحی دسترسی پیدا کند. این ممکن است حسابهای ایجاد شده توسط برنامههایی را که نصب کردهاید، شامل شود."</string>
@@ -502,7 +502,7 @@
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"تغییر تنظیمات میزان استفاده داده در پسزمینه"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="5347729578468744379">"به برنامه اجازه میدهد تا تنظیم کاربرد دادههای پسزمینه را تغییر دهد."</string>
<string name="permlab_accessWifiState" msgid="5202012949247040011">"مشاهدهٔ اتصالات Wi-Fi"</string>
- <string name="permdesc_accessWifiState" msgid="5002798077387803726">"به برنامه امکان میدهد اطلاعات مربوط به شبکه Wi-Fi را مشاهده کند، به عنوان مثال فعال بودن Wi-Fi و نام دستگاههای Wi-Fi متصل."</string>
+ <string name="permdesc_accessWifiState" msgid="5002798077387803726">"به برنامه امکان میدهد اطلاعات مربوط به شبکه Wi-Fi را مشاهده کند، بهعنوان مثال فعال بودن Wi-Fi و نام دستگاههای Wi-Fi متصل."</string>
<string name="permlab_changeWifiState" msgid="6550641188749128035">"اتصال به Wi-Fi و قطع اتصال از آن"</string>
<string name="permdesc_changeWifiState" msgid="7137950297386127533">"به برنامه اجازه میدهد تا به نقاط دسترسی Wi-Fi وصل شود و ارتباط خود را با آنها قطع کند و تغییراتی را در پیکربندی دستگاه برای شبکههای Wi-Fi ایجاد کند."</string>
<string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"دریافت چندگانه Wi-Fi را مجاز میکند"</string>
@@ -522,12 +522,12 @@
<string name="permlab_nfc" msgid="4423351274757876953">"کنترل ارتباط راه نزدیک"</string>
<string name="permdesc_nfc" msgid="7120611819401789907">"به برنامه اجازه میدهد تا با تگهای ارتباط میدان نزدیک (NFC)، کارتها و فایل خوان ارتباط برقرار کند."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"غیرفعال کردن قفل صفحه شما"</string>
- <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"به برنامه امکان میدهد قفل کلید و هر گونه امنیت گذرواژه مرتبط را غیرفعال کند. به عنوان مثال تلفن هنگام دریافت یک تماس تلفنی ورودی قفل کلید را غیرفعال میکند و بعد از پایان تماس، قفل کلید را دوباره فعال میکند."</string>
- <string name="permlab_readSyncSettings" msgid="6201810008230503052">"خواندن تنظیمات همگام سازی"</string>
- <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"به برنامه اجازه میدهد تنظیمات را برای یک حساب بخواند. به عنوان مثال، این ویژگی میتواند تعیین کند آیا حساب «افراد» شما با یک حساب همگامسازی شده است."</string>
+ <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"به برنامه امکان میدهد قفل کلید و هر گونه امنیت گذرواژه مرتبط را غیرفعال کند. بهعنوان مثال تلفن هنگام دریافت یک تماس تلفنی ورودی قفل کلید را غیرفعال میکند و بعد از پایان تماس، قفل کلید را دوباره فعال میکند."</string>
+ <string name="permlab_readSyncSettings" msgid="6201810008230503052">"خواندن تنظیمات همگامسازی"</string>
+ <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"به برنامه اجازه میدهد تنظیمات را برای یک حساب بخواند. بهعنوان مثال، این ویژگی میتواند تعیین کند آیا حساب «افراد» شما با یک حساب همگامسازی شده است."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"تغییر وضعیت همگامسازی بین فعال و غیرفعال"</string>
- <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"به یک برنامه اجازه میدهد تنظیمات همگامسازی را برای یک حساب اصلاح کند. به عنوان مثال، از این ویژگی میتوان برای فعال کردن همگامسازی برنامه «افراد» با یک حساب استفاده کرد."</string>
- <string name="permlab_readSyncStats" msgid="7396577451360202448">"خواندن اطلاعات آماری همگام سازی"</string>
+ <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"به یک برنامه اجازه میدهد تنظیمات همگامسازی را برای یک حساب اصلاح کند. بهعنوان مثال، از این ویژگی میتوان برای فعال کردن همگامسازی برنامه «افراد» با یک حساب استفاده کرد."</string>
+ <string name="permlab_readSyncStats" msgid="7396577451360202448">"خواندن اطلاعات آماری همگامسازی"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"به یک برنامه اجازه میدهد وضعیت همگامسازی یک حساب را بخواند، از جمله سابقه رویدادهای همگامسازی و میزان دادههای همگامسازی شده."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"خواندن فیدهای مشترک"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"به برنامه اجازه میدهد تا جزئیات مربوط به فیدهای همگام شده کنونی را دریافت کند."</string>
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"به برنامه اجازه میدهد تا در کارت SD بنویسد."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"تغییر/حذف محتواهای حافظه رسانه داخلی"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"به برنامه اجازه میدهد تا محتویات حافظه رسانه داخلی را تغییر دهد."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"دسترسی به سیستم فایل حافظهٔ پنهان"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"به برنامه اجازه میدهد تا سیستم فایل حافظهٔ پنهان را بخواند و بنویسد."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"علامتگذاری/دریافت تماسهای اینترنتی"</string>
@@ -1089,9 +1093,9 @@
<string name="dlg_confirm_kill_storage_users_text" msgid="5100428757107469454">"در صورت فعال کردن حافظهٔ USB، برخی از برنامههایی که از آنها استفاده میکنید متوقف میشوند و تا زمانی که حافظهٔ USB را غیرفعال نکنید امکان استفاده از آنها وجود نخواهد داشت."</string>
<string name="dlg_error_title" msgid="7323658469626514207">"راهاندازی USB ناموفق بود."</string>
<string name="dlg_ok" msgid="7376953167039865701">"تأیید"</string>
- <string name="usb_mtp_notification_title" msgid="3699913097391550394">"متصل شده به عنوان دستگاه رسانهای"</string>
- <string name="usb_ptp_notification_title" msgid="1960817192216064833">"متصل شده به عنوان دوربین"</string>
- <string name="usb_cd_installer_notification_title" msgid="6774712827892090754">"متصل شده به عنوان نصب کننده"</string>
+ <string name="usb_mtp_notification_title" msgid="3699913097391550394">"متصل شده بهعنوان دستگاه رسانهای"</string>
+ <string name="usb_ptp_notification_title" msgid="1960817192216064833">"متصل شده بهعنوان دوربین"</string>
+ <string name="usb_cd_installer_notification_title" msgid="6774712827892090754">"متصل شده بهعنوان نصب کننده"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"به یک وسیله جانبی USB وصل شده است"</string>
<string name="usb_notification_message" msgid="2290859399983720271">"برای سایر گزینههای USB لمس کنید."</string>
<string name="extmedia_format_title" product="nosdcard" msgid="9020092196061007262">"حافظهٔ USB فرمت شود؟"</string>
@@ -1159,7 +1163,7 @@
<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>
<string name="input_method_binding_label" msgid="1283557179944992649">"روش ورودی"</string>
- <string name="sync_binding_label" msgid="3687969138375092423">"همگام سازی"</string>
+ <string name="sync_binding_label" msgid="3687969138375092423">"همگامسازی"</string>
<string name="accessibility_binding_label" msgid="4148120742096474641">"قابلیت دسترسی"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"تصویر زمینه"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"تغییر تصویر زمینه"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN توسط <xliff:g id="APP">%s</xliff:g> فعال شده است"</string>
<string name="vpn_text" msgid="3011306607126450322">"برای مدیریت شبکه لمس کنید."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"به <xliff:g id="SESSION">%s</xliff:g> وصل شد. برای مدیریت شبکه لمس کنید."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"در حال اتصال VPN همیشه فعال…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN همیشه فعال متصل شد"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"خطای VPN همیشه فعال"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"برای بازنشانی اتصال لمس کنید"</string>
<string name="upload_file" msgid="2897957172366730416">"انتخاب فایل"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"هیچ فایلی انتخاب نشد"</string>
<string name="reset" msgid="2448168080964209908">"بازنشانی"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index ce355ca..b9d6c06 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Antaa sovelluksen kirjoittaa SD-kortille."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"muokkaa/poista sisäisen säilytystilan sisältöä"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Antaa sovelluksen muokata sisäisen tallennustilan sisältöä."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"käytä välimuistin tiedostojärjestelmää"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Antaa sovelluksen lukea välimuistin tiedostojärjestelmää ja kirjoittaa siihen."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"soita/vastaanota internetpuheluita"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> on aktivoinut VPN-yhteyden"</string>
<string name="vpn_text" msgid="3011306607126450322">"Voit hallinnoida verkkoa koskettamalla."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Yhdistetty: <xliff:g id="SESSION">%s</xliff:g>. Hallinnoi verkkoa koskettamalla."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Yhdistetään aina käytössä olevaan VPN-verkkoon..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Yhdistetty aina käytössä olevaan VPN-verkkoon"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Aina käytössä oleva VPN: virhe"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Nollaa yhteys koskettamalla"</string>
<string name="upload_file" msgid="2897957172366730416">"Valitse tiedosto"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Ei valittua tiedostoa"</string>
<string name="reset" msgid="2448168080964209908">"Palauta"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index f9250ce..e5f7978 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permet à l\'application de modifier le contenu de la carte SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Modifier/Supprimer contenu mémoire interne"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permet à l\'application de modifier le contenu de la mémoire de stockage multimédia interne."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"accéder au système de fichiers en cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permet à l\'application d\'obtenir des droits en lecture/écriture concernant le système de fichiers du cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"effectuer/recevoir des appels Internet"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index faca09d..fcb0783 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"एप्लिकेशन को SD कार्ड पर लिखने देता है."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"आंतरिक मीडिया संग्रहण सामग्रियों को बदलें/हटाएं"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"एप्लिकेशन को आंतरिक मीडिया संग्रहण की सामग्री को संशोधित करने देता है."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"कैश फ़ाइल सिस्टम में पहंचे"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"एप्लिकेशन को संचय फ़ाइल सिस्टम पढ़ने और लिखने देता है."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"इंटरनेट कॉल करें/प्राप्त करें"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN को <xliff:g id="APP">%s</xliff:g> द्वारा सक्रिय किया गया है"</string>
<string name="vpn_text" msgid="3011306607126450322">"नेटवर्क प्रबंधित करने के लिए स्पर्श करें."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"<xliff:g id="SESSION">%s</xliff:g> से कनेक्ट किया गया. नेटवर्क प्रबंधित करने के लिए स्पर्श करें."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"हमेशा-चालू VPN कनेक्ट हो रहा है…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"हमेशा-चालू VPN कनेक्ट है"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"हमेशा-चालू VPN त्रुटि"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"कनेक्शन रीसेट करने के लिए स्पर्श करें"</string>
<string name="upload_file" msgid="2897957172366730416">"फ़ाइल चुनें"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"कोई फ़ाइल चुनी नहीं गई"</string>
<string name="reset" msgid="2448168080964209908">"रीसेट करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 928761e..01e6557 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Aplikaciji omogućuje pisanje na SD karticu."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"izmijeni/izbriši sadržaj pohranjen na internim medijima"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Aplikaciji omogućuje izmjenu sadržaja pohranjenog na internim medijima."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"pristup sustavu datoteka predmemorije"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Aplikaciji omogućuje čitanje i pisanje u datotečnom sustavu privremene memorije."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"zovi/primaj internetske pozive"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikacija <xliff:g id="APP">%s</xliff:g> aktivirala je VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dodirnite za upravljanje mrežom."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Povezan sa sesijom <xliff:g id="SESSION">%s</xliff:g>. Dodirnite za upravljanje mrežom."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Povezivanje s uvijek uključenom VPN mrežom…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Povezan s uvijek uključenom VPN mrežom"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Pogreška uvijek uključene VPN mreže"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Dodirnite za poništavanje veze"</string>
<string name="upload_file" msgid="2897957172366730416">"Odaberite datoteku"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nema odabranih datoteka"</string>
<string name="reset" msgid="2448168080964209908">"Ponovo postavi"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index fce9c5b..a48129d 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Lehetővé teszi az alkalmazás számára, hogy írjon az SD-kártyára."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"belső tár tartalmának módosítása/törlése"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Lehetővé teszi az alkalmazás számára, hogy módosítsa a belső háttértár tartalmát."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"hozzáférés a gyorsítótár fájlrendszeréhez"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Lehetővé teszi az alkalmazás számára a gyorsítótár-fájlrendszer olvasását és írását."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"internetes hívások kezdeményezése és fogadása"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"A(z) <xliff:g id="APP">%s</xliff:g> aktiválta a VPN-t"</string>
<string name="vpn_text" msgid="3011306607126450322">"Érintse meg a hálózat kezeléséhez."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Csatlakozva ide: <xliff:g id="SESSION">%s</xliff:g>. Érintse meg a hálózat kezeléséhez."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Csatlakozás a mindig bekapcsolt VPN-hez..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Csatlakozva a mindig bekapcsolt VPN-hez"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Hiba a mindig bekapcsolt VPN-nel"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Érintse meg a kapcsolat alaphelyzetbe állításához."</string>
<string name="upload_file" msgid="2897957172366730416">"Fájl kiválasztása"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nincs fájl kiválasztva"</string>
<string name="reset" msgid="2448168080964209908">"Alaphelyzet"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 0c1ff65..77333cd 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Memungkinkan apl menulis ke kartu SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"ubah/hapus konten penyimpanan media internal"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Mengizinkan apl memodifikasi konten penyimpanan media internal."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"akses sistem file cache."</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Mengizinkan apl membaca dan menulis pada sistem file cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"lakukan//terima panggilan internet"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN diaktifkan oleh <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Sentuh untuk mengelola jaringan."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Tersambung ke <xliff:g id="SESSION">%s</xliff:g>. Sentuh untuk mengelola jaringan."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Menyambungkan VPN selalu aktif..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN selalu aktif tersambung"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Kesalahan VPN selalu aktif"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Sentuh untuk menyetel ulang sambungan"</string>
<string name="upload_file" msgid="2897957172366730416">"Pilih file"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Tidak ada file yang dipilih"</string>
<string name="reset" msgid="2448168080964209908">"Setel ulang"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 432f2e4..9f1ee1c 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Consente all\'applicazione di scrivere sulla scheda SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modifica/eliminaz. contenuti archivio media int."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Consente all\'applicazione di modificare i contenuti dell\'archivio media interno."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"accesso al filesystem nella cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Consente all\'applicazione di leggere e scrivere il filesystem nella cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"effettuazione/ricezione chiamate Internet"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index ff17945..d7ce2e1 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"מאפשר ליישום לכתוב לכרטיס ה-SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"שנה/מחק תוכן של אחסון מדיה פנימי"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"מאפשר ליישום לשנות את התוכן של אמצעי האחסון הפנימי למדיה."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"גישה למערכת הקבצים בקובץ השמור"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"מאפשר ליישום לקרוא ולכתוב במערכת הקבצים של הקבצים השמורים."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"בצע/קבל שיחות אינטרנט"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 1b2be12..0e1b9eb 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"SDカードへの書き込みをアプリに許可します。"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"内部メディアストレージの内容の変更/削除"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"内部メディアストレージの内容を変更することをアプリに許可します。"</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"キャッシュファイルシステムにアクセス"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"キャッシュファイルシステムの読み書きをアプリに許可します。"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"インターネット通話の発着信"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 5549489..ae9b0c0 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"앱이 SD 카드에 쓸 수 있도록 허용합니다."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"내부 미디어 저장소 콘텐츠 수정/삭제"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"앱이 내부 미디어 저장소의 콘텐츠를 수정할 수 있도록 허용합니다."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"캐시 파일시스템 액세스"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"앱이 캐시 파일 시스템을 읽고 쓸 수 있도록 허용합니다."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"인터넷 전화 걸기/받기"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN이 <xliff:g id="APP">%s</xliff:g>에 의해 활성화됨"</string>
<string name="vpn_text" msgid="3011306607126450322">"네트워크를 관리하려면 터치하세요."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"<xliff:g id="SESSION">%s</xliff:g>에 연결되어 있습니다. 네트워크를 관리하려면 터치하세요."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"연결 유지 VPN에 연결하는 중…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"연결 유지 VPN에 연결됨"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"연결 유지 VPN 오류"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"터치하여 연결 재설정"</string>
<string name="upload_file" msgid="2897957172366730416">"파일 선택"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"파일을 선택하지 않았습니다."</string>
<string name="reset" msgid="2448168080964209908">"초기화"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 37b2f83..94c5825 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Leidžiama programai rašyti į SD kortelę."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"keisti / ištr. vid. med. atm. tur."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Leidžiama programai keisti vidinės medijos saugyklos turinį."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"pasiekti talpyklos failų sistemą"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Leidžiama programai skaityti talpyklos failų sistemą ir į ją rašyti."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"skambinti / priimti skambučius internetu"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN suaktyvino „<xliff:g id="APP">%s</xliff:g>“"</string>
<string name="vpn_text" msgid="3011306607126450322">"Palieskite, kad valdytumėte tinklą."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Prisijungta prie <xliff:g id="SESSION">%s</xliff:g>. Jei norite valdyti tinklą, palieskite."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Prisijungiama prie visada įjungto VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Prisijungta prie visada įjungto VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Visada įjungto VPN klaida"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Palieskite, kad iš naujo nustatytumėte ryšį"</string>
<string name="upload_file" msgid="2897957172366730416">"Pasirinkti failą"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nepasirinktas joks failas"</string>
<string name="reset" msgid="2448168080964209908">"Atstatyti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 38f8047..91b414b 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Ļauj lietotnei rakstīt SD kartē."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"pārv./dz.datu n.iekš.atm.sat."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Ļauj lietotnei modificēt datu nesēja iekšējās atmiņas saturu."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"piekļūt kešatmiņas failu sistēmai"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Ļauj lietotnei lasīt un rakstīt kešatmiņas failu sistēmā."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"veikt/saņemt interneta zvanus"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Lietojumprogramma <xliff:g id="APP">%s</xliff:g> aktivizēja VPN."</string>
<string name="vpn_text" msgid="3011306607126450322">"Pieskarieties, lai pārvaldītu tīklu."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Ir izveidots savienojums ar <xliff:g id="SESSION">%s</xliff:g>. Pieskarieties, lai pārvaldītu tīklu."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Notiek savienojuma izveide ar vienmēr ieslēgtu VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Izveidots savienojums ar vienmēr ieslēgtu VPN."</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Kļūda saistībā ar vienmēr ieslēgtu VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Pieskarieties, lai atiestatītu savienojumu."</string>
<string name="upload_file" msgid="2897957172366730416">"Izvēlēties failu"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Neviens fails nav izvēlēts"</string>
<string name="reset" msgid="2448168080964209908">"Atiestatīt"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index a56f6cf..b9fca83 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Membenarkan apl menulis ke kad SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"ubh suai/pdm kdg strn mdia dlm"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Membenarkan apl mengubah suai kandungan storan media dalaman."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"akses sistem fail cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Membenarkan apl membaca dan menulis cache sistem fail."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"membuat/menerima panggilan Internet"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN diaktifkan oleh <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Sentuh untuk mengurus rangkaian."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Bersambung ke <xliff:g id="SESSION">%s</xliff:g>. Sentuh untuk mengurus rangkaian."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPN sentiasa hidup sedang disambungkan..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN sentiasa hidup telah disambungkan"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Ralat VPN sentiasa hidup"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Sentuh untuk menetapkan semula sambungan"</string>
<string name="upload_file" msgid="2897957172366730416">"Pilih fail"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Tiada fail dipilih"</string>
<string name="reset" msgid="2448168080964209908">"Tetapkan semula"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index a18e63c..ef6a305 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Lar appen skrive til SD-kortet."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"endre eller slette innhold på interne medier"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Lar appen endre innholdet i det interne lagringsmediet."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"tilgang til bufrede filer"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Lar appen lese og skrive til det bufrede filsystemet."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"foreta/motta Internett-anrop"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 3ac4f86..2ac476d 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Hiermee kan de app schrijven naar de SD-kaart."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"inh. mediaopsl. wijz./verw."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Hiermee kan de app de inhoud van de interne mediaopslag aanpassen."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"het cachebestandssysteem openen"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Hiermee kan de app het cachebestandssysteem lezen en schrijven."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"internetoproepen starten/ontvangen"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index b3cb4f8..bdea11f 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Pozwala aplikacji na zapis na karcie SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modyfikowanie/usuwanie zawartości pamięci wew."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Pozwala aplikacji na modyfikowanie zawartości pamięci wewnętrznej."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"dostęp do systemu plików pamięci podręcznej"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Pozwala aplikacji na odczyt i zapis w systemie plików pamięci podręcznej."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"nawiązywanie/odbieranie połączeń przez internet"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index aab8ca9c..390faaa 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que a aplicação escreva no cartão SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modif./elim. armaz. interno"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite que a aplicação modifique o conteúdo de armazenamento de suportes internos."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"aceder ao sistema de ficheiros da cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permite à aplicação ler e escrever no sistema de ficheiros da cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"fazer/receber chamadas pela internet"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 575630f..5ec23f9 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que o aplicativo grave em seu cartão SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modificar/excluir conteúdos de armazenamento de mídia internos"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite que o aplicativo modifique o conteúdo da mídia de armazenamento interno."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"acessar o sistema de arquivos de cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permite que o aplicativo leia e grave o sistema de arquivos cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"fazer/receber chamadas pela internet"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index eafb3b4..40fad47 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -891,6 +891,10 @@
<skip />
<!-- no translation found for permdesc_mediaStorageWrite (8189160597698529185) -->
<skip />
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"access al sistem da datotecas da cache"</string>
<!-- no translation found for permdesc_cache_filesystem (5578967642265550955) -->
<skip />
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 969d3a1..52c0c42 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite aplicaţiei să scrie pe cardul SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modif./şterg. conţinutul media stocat intern"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite aplicaţiei să modifice conţinutul stocării media interne."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"accesare sistem de fişiere cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permite aplicaţiei să scrie şi să citească sistemul de fişiere cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"efectuare/primire apeluri prin internet"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN este activată de <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Atingeţi pentru a gestiona reţeaua."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Conectat la <xliff:g id="SESSION">%s</xliff:g>. Atingeţi pentru a gestiona reţeaua."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Se efectuează conectarea la reţeaua VPN activată permanent…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Conectat(ă) la reţeaua VPN activată permanent"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Eroare de reţea VPN activată permanent"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Atingeţi pentru a reseta conexiunea"</string>
<string name="upload_file" msgid="2897957172366730416">"Alegeţi un fişier"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nu au fost găsite fişiere"</string>
<string name="reset" msgid="2448168080964209908">"Resetaţi"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index a7df6c1..a9a0d0c 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Приложение сможет записывать данные на SD-карту."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"изм./удал. данных мультимедиа"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Приложение сможет изменять контент внутреннего хранилища мультимедиа."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"получать доступ к кэшу файловой системы"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Приложение сможет выполнять чтение и запись в файловую систему кэша."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"Осуществление/прием интернет-вызовов"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Сеть VPN активирована приложением <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Нажмите, чтобы открыть настройки."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Сеть VPN подключена: <xliff:g id="SESSION">%s</xliff:g>. Нажмите, чтобы открыть настройки."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Подключение…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Подключено"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Ошибка"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Нажмите, чтобы сбросить соединение"</string>
<string name="upload_file" msgid="2897957172366730416">"Выбрать файл"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Не выбран файл"</string>
<string name="reset" msgid="2448168080964209908">"Сбросить"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 28bfa5e..2bfa219 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Umožňuje aplikácii zápis na kartu SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"úprava alebo odstránenie obsahu interného ukladacieho priestoru média"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Umožňuje aplikácii zmeniť obsah interného ukladacieho priestoru média."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"pristupovať do súborového systému vyrovnávacej pamäte"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Umožňuje aplikácii čítať a zapisovať do súborového systému vyrovnávacej pamäte."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"uskutočňovať a prijímať internetové hovory"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikáciu <xliff:g id="APP">%s</xliff:g> aktivovala sieť VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotykom môžete spravovať sieť."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Pripojené k relácii <xliff:g id="SESSION">%s</xliff:g>. Po dotyku môžete sieť spravovať."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Prebieha pripájanie k vždy zapnutej sieti VPN..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Pripojenie k vždy zapnutej sieti VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Chyba vždy zapnutej siete VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Dotykom obnovíte pripojenie"</string>
<string name="upload_file" msgid="2897957172366730416">"Zvoliť súbor"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nie je vybratý žiadny súbor"</string>
<string name="reset" msgid="2448168080964209908">"Obnoviť"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 5a9f299..78c2806 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Programu omogoča pisanje na kartico SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"spreminjanje/brisanje vsebine notranje shrambe nosilca podatkov"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Programu omogoča spreminjanje vsebine notranje shrambe nosilca podatkov."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"dostop do datotečnega sistema predpomnilnika"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Programu omogoča branje in pisanje v datotečni sistem predpomnilnika."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"opravljanje/sprejemanje internetnih klicev"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN je aktiviral program <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotaknite se, če želite upravljati omrežje."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Vzpostavljena povezava s sejo <xliff:g id="SESSION">%s</xliff:g>. Dotaknite se, če želite upravljati omrežje."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Povezovanje v stalno vklopljeno navidezno zasebno omrežje ..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Stalno vklopljeno navidezno zasebno omrežje povezano"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Napaka stalno vklopljenega navideznega zasebnega omrežja"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Dotaknite se, da ponastavite povezavo"</string>
<string name="upload_file" msgid="2897957172366730416">"Izberi datoteko"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nobena datoteka ni izbrana"</string>
<string name="reset" msgid="2448168080964209908">"Ponastavi"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index eeeeb30..e7c11b6 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дозвољава апликацији да уписује податке на SD картицу."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"измена/брисање интерне меморије медија"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Дозвољава апликацији да мења садржај интерне меморије медијума."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"приступ систему датотека кеша"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Дозвољава апликацији да чита систем датотека кеша и уписује податке у њега."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"упућивање/пријем Интернет позива"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Апликација <xliff:g id="APP">%s</xliff:g> је активирала VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Додирните да бисте управљали мрежом."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Повезано са сесијом <xliff:g id="SESSION">%s</xliff:g>. Додирните да бисте управљали мрежом."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Повезивање стално укљученог VPN-а..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Стално укључени VPN је повезан"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Грешка стално укљученог VPN-а"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Додирните да бисте ресетовали везу"</string>
<string name="upload_file" msgid="2897957172366730416">"Одабери датотеку"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Није изабрана ниједна датотека"</string>
<string name="reset" msgid="2448168080964209908">"Поново постави"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 761b8df3..b1009e2 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Tillåter att appen skriver till SD-kortet."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"ändra/ta bort innehåll"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Tillåter att appen ändrar innehållet på den interna lagringsenheten."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"åtkomst till cachefilsystemet"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Tillåter att appen läser och skriver till cachefilsystemet."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"ringa/ta emot Internetsamtal"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index dcd2d81..cf489c2 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Inaruhusu programu kuandikia kadi ya SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"badilisha/futa maudhui ya hifadhi ya media ya ndani."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Inaruhusu programu kurekebisha maudhui ya hifadhi ya ndani vyombo vya habari."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"fikia faili za mfumo za kache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Inaruhusu programu kusoma na kuandika mfumo wa faili wa kache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"piga/pokea simu za mtandao"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index b9ebf43..9358870 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"อนุญาตให้แอปพลิเคชันเขียนลงบนการ์ด SD"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"แก้/ลบเนื้อหาข้อมูลสื่อภายใน"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"อนุญาตให้แอปพลิเคชันแก้ไขเนื้อหาของที่เก็บข้อมูลสื่อภายใน"</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"เข้าถึงระบบไฟล์แคช"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"อนุญาตให้แอปพลิเคชันอ่านและเขียนระบบไฟล์แคช"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"โทรออก/รับสายอินเทอร์เน็ต"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"เปิดใช้งาน VPN โดย <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"แตะเพื่อจัดการเครือข่าย"</string>
<string name="vpn_text_long" msgid="6407351006249174473">"เชื่อมต่อกับ <xliff:g id="SESSION">%s</xliff:g> แตะเพื่อจัดการเครือข่าย"</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"กำลังเชื่อมต่อ VPN แบบเปิดตลอดเวลา…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"เชื่อมต่อ VPN แบบเปิดตลอดเวลาแล้ว"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"ข้อผิดพลาดของ VPN แบบเปิดตลอดเวลา"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"แตะเพื่อรีเซ็ตการเชื่อมต่อ"</string>
<string name="upload_file" msgid="2897957172366730416">"เลือกไฟล์"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"ไม่ได้เลือกไฟล์ไว้"</string>
<string name="reset" msgid="2448168080964209908">"รีเซ็ต"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 4f222b7..1147059 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Pinapayagan ang app na magsulat sa SD card."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"baguhin/tanggalin ang mga nilalaman ng panloob na imbakan ng media"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Pinapayagan ang app na baguhin ang mga nilalaman ng panloob na media storage."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"i-access ang cache filesystem"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Pinapayagan ang app na basahin at isulat ang cache filesystem."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"gumawa/tumanggap ng mga tawag sa Internet"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Isinaaktibo ang VPN ng <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Pindutin upang pamahalaan ang network."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Nakakonekta sa <xliff:g id="SESSION">%s</xliff:g>. Pindutin upang pamahalaan ang network."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Kumukonekta ang Always-on VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Nakakonekta ang Always-on VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Error sa Always-on VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Pindutin upang i-reset ang koneksyon"</string>
<string name="upload_file" msgid="2897957172366730416">"Pumili ng file"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Walang napiling file"</string>
<string name="reset" msgid="2448168080964209908">"I-reset"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index fe80c3e..a4e7d4d 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Uygulamaya, SD karta yazma izni verir."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"dahili medya depolama birimi içeriğini değiştir/sil"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Uygulamaya, dahili medya depolama içeriğini değiştirme izni verir."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"önbellek dosya sistemine eriş"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Uygulamaya, önbellek dosya sisteminde okuma ve yazma yapma izni verir."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"İnternet çağrılar yap/alma"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN, <xliff:g id="APP">%s</xliff:g> tarafından etkinleştirildi"</string>
<string name="vpn_text" msgid="3011306607126450322">"Ağı yönetmek için dokunun."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"<xliff:g id="SESSION">%s</xliff:g> oturumuna bağlandı. Ağı yönetmek için dokunun."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Her zaman açık VPN\'ye bağlanılıyor…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Her zaman açık VPN\'ye bağlanıldı"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Her zaman açık VPN hatası"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Bağlantıyı sıfırlamak için dokunun"</string>
<string name="upload_file" msgid="2897957172366730416">"Dosya seç"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Seçili dosya yok"</string>
<string name="reset" msgid="2448168080964209908">"Sıfırla"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 730c384..5c4a1b1 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дозволяє програмі записувати на карту SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"змінювати/видаляти вміст внутр. сховища даних"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Дозволяє програмі змінювати вміст внутрішнього сховища даних."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"отр. дост. до файл. сист. кешу"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Дозволяє програмі читати з файлової системи кеш-пам’яті та писати в неї."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"здійсн./отрим. Інтернет-дзвін."</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Мережу VPN активовано програмою <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Торкніться, щоб керувати мережею."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Під’єднано до сеансу <xliff:g id="SESSION">%s</xliff:g>. Торкніться, щоб керувати мережею."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Під’єднання до постійної мережі VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Під’єднано до постійної мережі VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Помилка постійної мережі VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Торкніться, щоб скинути з’єднання"</string>
<string name="upload_file" msgid="2897957172366730416">"Виберіть файл"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Не вибрано файл"</string>
<string name="reset" msgid="2448168080964209908">"Віднов."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 6ffc84e..66d0f31 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Cho phép ứng dụng ghi vào thẻ SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"sửa đổi/xóa nội dung trên bộ nhớ phương tiện cục bộ"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Cho phép ứng dụng sửa đổi nội dung của bộ lưu trữ phương tiện nội bộ."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"truy cập hệ thống tệp bộ nhớ cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Cho phép ứng dụng đọc và ghi hệ thống tệp bộ nhớ cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"thực hiện/nhận cuộc gọi qua Internet"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN được <xliff:g id="APP">%s</xliff:g> kích hoạt"</string>
<string name="vpn_text" msgid="3011306607126450322">"Chạm để quản lý mạng."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Đã kết nối với <xliff:g id="SESSION">%s</xliff:g>. Chạm để quản lý mạng."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Đang kết nối VPN luôn bật…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Đã kết nối VPN luôn bật"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Lỗi VPN luôn bật"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Chạm để đặt lại kết nối"</string>
<string name="upload_file" msgid="2897957172366730416">"Chọn tệp"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Không có tệp nào được chọn"</string>
<string name="reset" msgid="2448168080964209908">"Đặt lại"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 12de741..4c42e4a 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"允许应用写入 SD 卡。"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"修改/删除内部媒体存储设备的内容"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"允许应用修改内部媒体存储设备的内容。"</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"访问缓存文件系统"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"允许应用读取和写入缓存文件系统。"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"拨打/接听互联网通话"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 84725cf..53f3d39 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"允許應用程式寫入 SD 卡。"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"修改/刪除內部媒體儲存裝置內容"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"允許應用程式修改內部媒體儲存空間的內容。"</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"存取快取檔案系統"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"允許應用程式讀取及寫入快取檔案系統。"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"撥打/接聽網路電話"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> 已啟用 VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"輕觸即可管理網路。"</string>
<string name="vpn_text_long" msgid="6407351006249174473">"已連線至 <xliff:g id="SESSION">%s</xliff:g>,輕觸即可管理網路。"</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"正在連線至永久連線的 VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"已連線至永久連線的 VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"永久連線的 VPN 發生錯誤"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"輕觸即可重設連線"</string>
<string name="upload_file" msgid="2897957172366730416">"選擇檔案"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"未選擇任何檔案"</string>
<string name="reset" msgid="2448168080964209908">"重設"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index e8eaaa6..232c933 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Ivumela insiza ukuthi ibhalele ekhadini le-SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"guqula/susa okuqukethwe kwisitoreji semidiya yangaphakathi"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Ivumela insiza ukuthi iguqule okuqukethwe kokulondoloza imidiya yangaphakathi."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"finyelela kunqolobane yesistimu yefayela"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Ivumela insiza ukuthi ifunde futhi ibhale isistimu yokufayila amafayela esikhashana."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"yena/thola amakholi e-Inthanethi"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3757afc..9601ad4 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2374,6 +2374,12 @@
<!-- The extra value of the subtype. This string can be any string and will be passed to
the IME when the framework calls the IME with the subtype. -->
<attr name="imeSubtypeExtraValue" format="string" />
+ <!-- The unique id for the subtype. The input method framework keeps track of enabled
+ subtypes by ID. When the IME package gets upgraded, enabled IDs will stay enabled even
+ if other attributes are different. If the ID is unspecified (by calling the other
+ constructor or 0. Arrays.hashCode(new Object[] {locale, mode, extraValue,
+ isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead. -->
+ <attr name="subtypeId" format="integer"/>
</declare-styleable>
<!-- Use <code>spell-checker</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 1c3318d..3a69937 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -639,6 +639,8 @@
physical screen size has changed such as switching to an external
display. -->
<flag name="smallestScreenSize" value="0x0800" />
+ <!-- The layout direction has changed. For example going from LTR to RTL. -->
+ <flag name="layoutDirection" value="0x2000" />
<!-- The font scaling factor has changed, that is the user has
selected a new global font size. -->
<flag name="fontScale" value="0x40000000" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 86d03ad..bba2252 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -471,6 +471,9 @@
Also, the battery stats are flushed to disk when we hit this level. -->
<integer name="config_criticalBatteryWarningLevel">4</integer>
+ <!-- Shutdown if the battery temperature exceeds (this value * 0.1) Celsius. -->
+ <integer name="config_shutdownBatteryTemperature">680</integer>
+
<!-- Display low battery warning when battery level dips to this value -->
<!-- Display low battery warning when battery level dips to this value -->
<integer name="config_lowBatteryWarningLevel">15</integer>
@@ -479,7 +482,7 @@
<integer name="config_lowBatteryCloseWarningLevel">20</integer>
<!-- Default color for notification LED. -->
- <color name="config_defaultNotificationColor">#ff00ff00</color>
+ <color name="config_defaultNotificationColor">#ffffffff</color>
<!-- Default LED on time for notification LED in milliseconds. -->
<integer name="config_defaultNotificationLedOn">500</integer>
@@ -916,6 +919,9 @@
<!-- Set to true to add links to Cell Broadcast app from Settings and MMS app. -->
<bool name="config_cellBroadcastAppLinks">false</bool>
+ <!-- The default value if the SyncStorageEngine should sync automatically or not -->
+ <bool name="config_syncstorageengine_masterSyncAutomatically">true</bool>
+
<!-- Maximum number of supported users -->
<integer name="config_multiuserMaximumUsers">10</integer>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 8e0eb15..19ed434 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -255,6 +255,7 @@
<java-symbol type="bool" name="config_sms_capable" />
<java-symbol type="bool" name="config_sms_utf8_support" />
<java-symbol type="bool" name="config_swipeDisambiguation" />
+ <java-symbol type="bool" name="config_syncstorageengine_masterSyncAutomatically" />
<java-symbol type="bool" name="config_telephony_use_own_number_for_voicemail" />
<java-symbol type="bool" name="config_ui_enableFadingMarquee" />
<java-symbol type="bool" name="config_use_strict_phone_number_comparation" />
@@ -710,6 +711,7 @@
<java-symbol type="string" name="policydesc_setGlobalProxy" />
<java-symbol type="string" name="policydesc_watchLogin" />
<java-symbol type="string" name="policydesc_wipeData" />
+ <java-symbol type="string" name="policydesc_disableKeyguardWidgets" />
<java-symbol type="string" name="policylab_disableCamera" />
<java-symbol type="string" name="policylab_encryptedStorage" />
<java-symbol type="string" name="policylab_expirePassword" />
@@ -719,6 +721,7 @@
<java-symbol type="string" name="policylab_setGlobalProxy" />
<java-symbol type="string" name="policylab_watchLogin" />
<java-symbol type="string" name="policylab_wipeData" />
+ <java-symbol type="string" name="policylab_disableKeyguardWidgets" />
<java-symbol type="string" name="postalTypeCustom" />
<java-symbol type="string" name="postalTypeHome" />
<java-symbol type="string" name="postalTypeOther" />
@@ -1293,7 +1296,7 @@
<java-symbol type="id" name="passwordEntry" />
<java-symbol type="id" name="pinDel" />
<java-symbol type="id" name="pinDisplay" />
- <java-symbol type="id" name="propertyOf" />
+ <java-symbol type="id" name="owner_info" />
<java-symbol type="id" name="pukDel" />
<java-symbol type="id" name="pukDisplay" />
<java-symbol type="id" name="right_icon" />
@@ -1544,6 +1547,7 @@
<java-symbol type="integer" name="config_screenBrightnessSettingMaximum" />
<java-symbol type="integer" name="config_screenBrightnessSettingDefault" />
<java-symbol type="integer" name="config_screenBrightnessDim" />
+ <java-symbol type="integer" name="config_shutdownBatteryTemperature" />
<java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" />
<java-symbol type="layout" name="am_compat_mode_dialog" />
<java-symbol type="layout" name="launch_warning" />
@@ -3761,5 +3765,6 @@
<public type="attr" name="listPreferredItemPaddingEnd" />
<public type="attr" name="singleUser" />
<public type="attr" name="presentationTheme" />
+ <public type="attr" name="subtypeId"/>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 381055f..d2951bf 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1693,6 +1693,10 @@
<string name="policylab_disableCamera">Disable cameras</string>
<!-- Description of policy access to disable all device cameras [CHAR LIMIT=110]-->
<string name="policydesc_disableCamera">Prevent use of all device cameras.</string>
+ <!-- Title of policy access to disable all device cameras [CHAR LIMIT=30]-->
+ <string name="policylab_disableKeyguardWidgets">Disable widgets on keyguard</string>
+ <!-- Description of policy access to disable all device cameras [CHAR LIMIT=110]-->
+ <string name="policydesc_disableKeyguardWidgets">Prevent use of some or all widgets on keyguard.</string>
<!-- The order of these is important, don't reorder without changing Contacts.java --> <skip />
<!-- Phone number types from android.provider.Contacts. This could be used when adding a new phone number for a contact, for example. -->
diff --git a/docs/html/about/dashboards/index.jd b/docs/html/about/dashboards/index.jd
index 4a75b91..c3da3bf 100644
--- a/docs/html/about/dashboards/index.jd
+++ b/docs/html/about/dashboards/index.jd
@@ -20,44 +20,43 @@
<p>The following pie chart and table is based on the number of Android devices that have accessed
Google Play within a 14-day period ending on the data collection date noted below.</p>
-<div class="col-6" style="margin-left:0">
+<div class="col-5" style="margin-left:0">
<table>
<tr>
<th>Version</th>
<th>Codename</th>
- <th>API Level</th>
+ <th>API</th>
<th>Distribution</th>
</tr>
<tr><td><a href="/about/versions/android-1.5.html">1.5</a></td><td>Cupcake</td> <td>3</td><td>0.2%</td></tr>
-<tr><td><a href="/about/versions/android-1.6.html">1.6</a></td><td>Donut</td> <td>4</td><td>0.5%</td></tr>
-<tr><td><a href="/about/versions/android-2.1.html">2.1</a></td><td>Eclair</td> <td>7</td><td>4.2%</td></tr>
-<tr><td><a href="/about/versions/android-2.2.html">2.2</a></td><td>Froyo</td> <td>8</td><td>15.5%</td></tr>
+<tr><td><a href="/about/versions/android-1.6.html">1.6</a></td><td>Donut</td> <td>4</td><td>0.4%</td></tr>
+<tr><td><a href="/about/versions/android-2.1.html">2.1</a></td><td>Eclair</td> <td>7</td><td>3.7%</td></tr>
+<tr><td><a href="/about/versions/android-2.2.html">2.2</a></td><td>Froyo</td> <td>8</td><td>14%</td></tr>
<tr><td><a href="/about/versions/android-2.3.html">2.3 - 2.3.2</a>
</td><td rowspan="2">Gingerbread</td> <td>9</td><td>0.3%</td></tr>
<tr><td><a href="/about/versions/android-2.3.3.html">2.3.3 - 2.3.7
- </a></td><!-- Gingerbread --> <td>10</td><td>60.3%</td></tr>
+ </a></td><!-- Gingerbread --> <td>10</td><td>57.2%</td></tr>
<tr><td><a href="/about/versions/android-3.1.html">3.1</a></td>
<td rowspan="2">Honeycomb</td> <td>12</td><td>0.5%</td></tr>
-<tr><td><a href="/about/versions/android-3.2.html">3.2</a></td> <!-- Honeycomb --><td>13</td><td>1.8%</td></tr>
+<tr><td><a href="/about/versions/android-3.2.html">3.2</a></td> <!-- Honeycomb --><td>13</td><td>1.6%</td></tr>
<tr><td><a href="/about/versions/android-4.0.html">4.0 - 4.0.2</a></td>
<td rowspan="2">Ice Cream Sandwich</td><td>14</td><td>0.1%</td></tr>
<tr><td><a href="/about/versions/android-4.0.3.html">4.0.3 - 4.0.4</a></td>
- <!-- ICS --> <td>15</td><td>15.8%</td></tr>
-<tr><td><a href="/about/versions/android-4.1.html">4.1</a></td> <td>Jelly Bean</td><td>16</td><td>0.8%</td></tr>
+ <!-- ICS --> <td>15</td><td>20.8%</td></tr>
+<tr><td><a href="/about/versions/android-4.1.html">4.1</a></td> <td>Jelly Bean</td><td>16</td><td>1.2%</td></tr>
</table>
-
</div>
-<div class="col-7" style="margin-right:0">
+<div class="col-8" style="margin-right:0">
<img alt=""
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x310&chd=t:0.2,0.5,4.2,15.5,0.3,60.3,0.5,1.8,0.1,15.8,0.8&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.1|Android%203.2|Android%204.0|Android%204.0.3|Android%204.1&chco=c4df9b,6fad0c&chf=bg,s,00000000" />
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x245&chd=t:4.3,14,57.5,2.1,20.9,1.2&chl=Eclair%20%26%20older|Froyo|Gingerbread|Honeycomb|Ice%20Cream%20Sandwich|Jelly%20Bean&chco=c4df9b,6fad0c&chf=bg,s,00000000" />
</div><!-- end dashboard-panel -->
-<p style="clear:both"><em>Data collected during a 14-day period ending on August 1, 2012</em></p>
+<p style="clear:both"><em>Data collected during a 14-day period ending on September 4, 2012</em></p>
<!--
<p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p>
-->
@@ -82,9 +81,9 @@
Google Play within a 14-day period ending on the date indicated on the x-axis.</p>
<img alt="" height="250" width="660"
-src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chf=bg,s,00000000&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C02/01%7C02/15%7C03/01%7C03/15%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C1%3A%7C2012%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C2012%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:98.6,98.4,98.4,98.6,98.5,98.6,98.8,98.7,98.9,99.1,99.1,99.2,98.6|97.6,97.5,97.6,97.8,97.8,97.9,98.1,98.1,98.3,98.5,98.6,98.7,98.1|89.9,90.3,90.8,91.4,91.8,92.1,92.5,92.7,93.1,93.5,93.9,94.2,93.9|62.0,63.7,65.2,66.8,68.6,69.9,71.5,72.6,74.0,75.2,76.5,77.8,78.4|4.0,4.1,4.3,4.6,5.5,6.5,7.6,8.2,9.4,11.0,12.8,15.6,18.1|2.6,3.0,3.2,3.5,4.5,5.5,6.6,7.4,8.7,10.4,12.3,15.1,17.6|0.7,0.8,1.1,1.3,2.3,3.3,4.4,5.3,6.7,8.4,10.4,13.2,15.8&chm=b,c3df9b,0,1,0|b,b6dc7d,1,2,0|tAndroid%202.2,5b831d,2,0,15,,t::-5|b,aadb5e,2,3,0|tAndroid%202.3.3,496c13,3,0,15,,t::-5|b,9ddb3d,3,4,0|b,91da1e,4,5,0|b,80c414,5,6,0|tAndroid%204.0.3,131d02,6,10,15,,t::-5|B,6fad0c,6,7,0&chg=7,25&chdl=Android%201.6|Android%202.1|Android%202.2|Android%202.3.3|Android%203.1|Android%203.2|Android%204.0.3&chco=add274,a0d155,94d134,84c323,73ad18,62960f,507d08" />
+src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chf=bg,s,00000000&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C03/01%7C03/15%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C08/15%7C09/01%7C1%3A%7C2012%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C2012%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:97.6,97.8,97.8,97.9,98.1,98.1,98.3,98.5,98.6,98.7,98.9,98.9,99.0|90.8,91.4,91.8,92.1,92.5,92.7,93.1,93.5,93.9,94.2,94.7,94.9,95.3|65.2,66.8,68.6,69.9,71.5,72.6,74.0,75.2,76.5,77.8,79.2,80.1,81.1|4.3,4.6,5.5,6.5,7.6,8.2,9.4,11.0,12.8,15.6,18.9,21.2,23.7|3.2,3.5,4.5,5.5,6.6,7.4,8.7,10.4,12.3,15.1,18.4,20.7,23.2|1.1,1.3,2.3,3.3,4.4,5.3,6.7,8.4,10.4,13.2,16.6,19.0,21.5|0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.8,0.9,1.1&chm=b,c3df9b,0,1,0|tAndroid%202.2,6c9729,1,0,15,,t::-5|b,b6dc7d,1,2,0|tAndroid%202.3.3,5b831d,2,0,15,,t::-5|b,aadb5e,2,3,0|b,9ddb3d,3,4,0|b,91da1e,4,5,0|tAndroid%204.0.3,253a06,5,8,15,,t::-5|b,80c414,5,6,0|B,6fad0c,6,7,0&chg=7,25&chdl=Android%202.1|Android%202.2|Android%202.3.3|Android%203.1|Android%203.2|Android%204.0.3|Android%204.1&chco=add274,a0d155,94d134,84c323,73ad18,62960f,507d08" />
-<p><em>Last historical dataset collected during a 14-day period ending on August 1, 2012</em></p>
+<p><em>Last historical dataset collected during a 14-day period ending on September 1, 2012</em></p>
@@ -111,6 +110,14 @@
<h2 id="Screens">Screen Sizes and Densities</h2>
+
+<img alt="" style="float:right;"
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chf=bg,s,00000000&chco=c4df9b,6fad0c&chl=Xlarge%7CLarge%7CNormal%7CSmall&chd=t%3A4.7,6.5,86,2.8" />
+
+
+<img alt="" style="float:right;clear:right"
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chf=bg,s,00000000&chco=c4df9b,6fad0c&chl=ldpi%7Cmdpi%7Chdpi%7Cxhdpi&chd=t%3A1.6,18.6,53.6,26.2" />
+
<p>This section provides data about the relative number of active devices that have a particular
screen configuration, defined by a combination of screen size and density. To simplify the way that
you design your user interfaces for different screen configurations, Android divides the range of
@@ -132,10 +139,7 @@
ending on the data collection date noted below.</p>
-<div class="col-6" style="margin-left:0">
-
-
-<table>
+<table style="width:350px">
<tr>
<th></th>
<th scope="col">ldpi</th>
@@ -144,22 +148,22 @@
<th scope="col">xhdpi</th>
</tr>
<tr><th scope="row">small</th>
-<td>1.5%</td> <!-- small/ldpi -->
+<td>1.1%</td> <!-- small/ldpi -->
<td></td> <!-- small/mdpi -->
-<td>1.2%</td> <!-- small/hdpi -->
+<td>1.7%</td> <!-- small/hdpi -->
<td></td> <!-- small/xhdpi -->
</tr>
<tr><th scope="row">normal</th>
-<td>0.5%</td> <!-- normal/ldpi -->
-<td>12.1%</td> <!-- normal/mdpi -->
-<td>55.3%</td> <!-- normal/hdpi -->
-<td>17.4%</td> <!-- normal/xhdpi -->
+<td>0.4%</td> <!-- normal/ldpi -->
+<td>11.4%</td> <!-- normal/mdpi -->
+<td>51.9%</td> <!-- normal/hdpi -->
+<td>22.3%</td> <!-- normal/xhdpi -->
</tr>
<tr><th scope="row">large</th>
<td>0.1%</td> <!-- large/ldpi -->
-<td>2.7%</td> <!-- large/mdpi -->
+<td>2.5%</td> <!-- large/mdpi -->
<td></td> <!-- large/hdpi -->
-<td>4.5%</td> <!-- large/xhdpi -->
+<td>3.9%</td> <!-- large/xhdpi -->
</tr>
<tr><th scope="row">xlarge</th>
<td></td> <!-- xlarge/ldpi -->
@@ -169,16 +173,7 @@
</tr>
</table>
-
-</div>
-
-<div class="col-7" style="margin-right:0">
-<img alt=""
-src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chf=bg,s,00000000&chco=c4df9b,6fad0c&chl=Xlarge%20/%20mdpi|Large%20/%20ldpi|Large%20/%20mdpi|Large%20/%20xhdpi|Normal%20/%20hdpi|Normal%20/%20ldpi|Normal%20/%20mdpi|Normal%20/%20xhdpi|Small%20/%20hdpi|Small%20/%20ldpi&chd=t%3A4.7,0.1,2.7,4.5,55.3,0.5,12.1,17.4,1.2,1.5" />
-
-</div>
-
-<p style="clear:both"><em>Data collected during a 7-day period ending on August 1, 2012</em></p>
+<p style="clear:both"><em>Data collected during a 7-day period ending on September 4, 2012</em></p>
@@ -196,6 +191,10 @@
support for any lower version (for example, support for version 2.0 also implies support for
1.1).</p>
+
+<img alt="" style="float:right"
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1%20only|GL%202.0%20%26%201.1&chd=t%3A9.2,90.8&chf=bg,s,00000000" />
+
<p>To declare which version of OpenGL ES your application requires, you should use the {@code
android:glEsVersion} attribute of the <a
href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}</a>
@@ -209,28 +208,21 @@
ending on the data collection date noted below.</p>
-<div class="col-6" style="margin-left:0">
-<table>
+<table style="width:350px">
<tr>
<th scope="col">OpenGL ES Version</th>
<th scope="col">Distribution</th>
</tr>
<tr>
<td>1.1 only</th>
-<td>9.3%</td>
+<td>9.2%</td>
</tr>
<tr>
<td>2.0 & 1.1</th>
-<td>90.7%</td>
+<td>90.8%</td>
</tr>
</table>
-</div>
-
-<div class="col-7" style="margin-right:0">
-<img alt=""
-src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1%20only|GL%202.0%20%26%201.1&chd=t%3A9.3,90.7&chf=bg,s,00000000" />
-
-</div>
-<p style="clear:both"><em>Data collected during a 7-day period ending on August 1, 2012</em></p>
+
+<p style="clear:both"><em>Data collected during a 7-day period ending on September 4, 2012</em></p>
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 9465f18..e812ccb 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -8,7 +8,7 @@
<ul id="nav">
<!-- Walkthrough for Developers -- quick overview of what it's like to develop on Android -->
<!--<li style="color:red">Overview</li> -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/components/index.html">
<span class="en">App Components</span>
@@ -223,7 +223,7 @@
<li><a href="<?cs var:toroot ?>guide/topics/search/adding-custom-suggestions.html">Adding Custom Suggestions</a></li>
<li><a href="<?cs var:toroot ?>guide/topics/search/searchable-config.html">Searchable Configuration</a></li>
</ul>
- </li>
+ </li>
<li><a href="<?cs var:toroot ?>guide/topics/ui/drag-drop.html">
<span class="en">Drag and Drop</span>
</a></li>
@@ -235,6 +235,9 @@
<li><a href="<?cs var:toroot ?>guide/topics/ui/accessibility/apps.html">
<span class="en">Making Applications Accessible</span>
</a></li>
+ <li><a href="<?cs var:toroot ?>guide/topics/ui/accessibility/checklist.html">
+ <span class="en">Accessibility Developer Checklist</span>
+ </a></li>
<li><a href="<?cs var:toroot ?>guide/topics/ui/accessibility/services.html">
<span class="en">Building Accessibility Services</span>
</a></li>
@@ -382,9 +385,9 @@
</a></li>
</ul>
</li><!-- end of location and sensors -->
-
-
+
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/topics/connectivity/index.html">
<span class="en">Connectivity</span>
@@ -419,10 +422,10 @@
<span class="en">SIP</span>
</a>
</li>
-
+
</ul>
</li><!-- end of connectivity -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/topics/text/index.html">
<span class="en">Text and Input</span>
@@ -439,7 +442,7 @@
</a></li>
</ul>
</li><!-- end of text and input -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/topics/data/index.html">
<span class="en">Data Storage</span>
@@ -457,7 +460,7 @@
</ul>
</li><!-- end of data storage -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot?>guide/topics/admin/index.html">
<span class="en">Administration</span>
@@ -475,7 +478,7 @@
-->
</ul>
</li><!-- end of administration -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/webapps/index.html">
<span class="en">Web Apps</span>
@@ -498,7 +501,7 @@
</a></li>
</ul>
</li><!-- end of web apps -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/practices/index.html">
<span class="en">Best Practices</span>
@@ -555,13 +558,13 @@
</ul>
</li>
-
-
+
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/google/index.html">
<span class="en">Google Services</span>
</a></div>
- <ul>
+ <ul>
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot?>guide/google/play/billing/index.html">
@@ -623,7 +626,7 @@
<li><a href="<?cs var:toroot ?>guide/google/play/expansion-files.html">
<span class="en">APK Expansion Files</span></a>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/google/gcm/index.html">
<span class="en">Google Cloud Messaging</span></a>
@@ -649,9 +652,9 @@
</ul>
</li><!-- end Google Play -->
-
-
-
+
+
+
<!-- this needs to move
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/practices/ui_guidelines/index.html">
@@ -691,9 +694,9 @@
</a></div>
</li>
</ul>
- </li>
+ </li>
</ul> -->
-
+
<!-- Remove
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/appendix/index.html">
@@ -710,7 +713,7 @@
<li><a href="<?cs var:toroot ?>guide/appendix/g-app-intents.html">
<span class="en">Intents List: Google Apps</span>
</a></li>
-
+
<li><a href="<?cs var:toroot ?>guide/appendix/glossary.html">
<span class="en">Glossary</span>
diff --git a/docs/html/guide/topics/ui/accessibility/apps.jd b/docs/html/guide/topics/ui/accessibility/apps.jd
index d23512b..13b4538 100644
--- a/docs/html/guide/topics/ui/accessibility/apps.jd
+++ b/docs/html/guide/topics/ui/accessibility/apps.jd
@@ -21,14 +21,11 @@
<li><a href="#accessibility-methods">Implementing accessibility API methods</a></li>
<li><a href="#send-events">Sending accessibility events</a></li>
<li><a href="#populate-events">Populating accessibility events</a></li>
+ <li><a href="#virtual-hierarchy">Providing a customized accessibility context</a></li>
+ <li><a href="#custom-touch-events">Handling custom touch events</a></li>
</ol>
</li>
- <li><a href="#test">Testing Accessibility</a>
- <ol>
- <li><a href="#test-audibles">Testing audible feedback</a></li>
- <li><a href="#test-navigation">Testing focus navigation</a></li>
- </ol>
- </li>
+ <li><a href="#test">Testing Accessibility</a></li>
</ol>
<h2>Key classes</h2>
@@ -42,60 +39,79 @@
<h2>See also</h2>
<ol>
- <li><a href="{@docRoot}training/accessibility/index.html">Implementing Accessibility</a></li>
- <li><a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a>
- </li>
- <li><a href="{@docRoot}design/index.html">Android Design</a></li>
+ <li><a href="checklist.html">Accessibility Developer Checklist</a><li>
+ <li><a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing Checklist</a><li>
+ <li><a href="{@docRoot}design/patterns/accessibility.html">Android Design: Accessibility</a></li>
+ <li><a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a></li>
+ <li><a href="{@docRoot}training/accessibility/index.html">Training: Implementing Accessibility</a></li>
</ol>
</div>
</div>
-<p>Applications built for Android are accessible to users with visual, physical or age-related
-disabilities when they activate accessibility features and services on a device. By default,
-these services make your application more accessible. However, there are further steps you should
-take to optimize the accessibility of your application and ensure a pleasant experience for all your
-users.</p>
+<p>Applications built for Android are more accessible to users with visual, physical or age-related
+limitations when those users activate accessibility services and features on a device. These
+services make your application more accessible even if you do not make any accessibility changes
+to your code. However, there are steps you should take to optimize the accessibility of your
+application and ensure a pleasant experience for all your users.</p>
-<p>Making sure your application is accessible to all users is relatively easy, particularly when you
-use framework-provided user interface components. If you only use these standard components for your
-application, there are just a few steps required to ensure your application is accessible:</p>
+<p>Making sure your application is accessible to all users requires only a few steps, particularly
+when you create your user interface with the components provided by the Android framework. If you
+use only the standard components for your application, the steps are:</p>
<ol>
- <li>Label your {@link android.widget.ImageButton}, {@link android.widget.ImageView}, {@link
-android.widget.EditText}, {@link android.widget.CheckBox} and other user interface controls using
-the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
- {@code android:contentDescription}</a> attribute.</li>
- <li>Make all of your user interface elements accessible with a directional controller,
- such as a trackball or D-pad.</li>
- <li>Test your application by turning on accessibility services like TalkBack and Explore by
- Touch, and try using your application using only directional controls.</li>
+ <li>Add descriptive text to user interface controls in your application using the
+ <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+ {@code android:contentDescription}</a> attribute. Pay particular attention to
+ {@link android.widget.ImageButton}, {@link android.widget.ImageView}
+ and {@link android.widget.CheckBox}.</li>
+ <li>Make sure that all user interface elements that can accept input (touches or typing) can be
+ reached with a directional controller, such as a trackball, D-pad (physical or virtual) or
+ navigation <a href="http://support.google.com/android/bin/topic.py?hl=en&topic=2492346">gestures
+ </a>.</li>
+ <li>Make sure that audio prompts are always accompanied by another visual prompt or notification,
+ to assist users who are deaf or hard of hearing.</li>
+ <li>Test your application using only accessibility navigation services and features. Turn on
+ <a href="{@docRoot}tools/testing/testing_accessibility.html#testing-talkback">TalkBack</a> and
+ <a href="{@docRoot}tools/testing/testing_accessibility.html#testing-ebt">Explore by Touch</a>,
+ and then try using your application using only directional controls. For more information on
+ testing for accessibility, see the <a href="{@docRoot}tools/testing/testing_accessibility.html">
+ Accessibility Testing Checklist</a>.</li>
</ol>
-<p>Developers who create custom controls that extend from the {@link android.view.View} class have
-some additional responsibilities for making sure their components are accessible for users. This
-document also discusses how to make custom view controls compatible with accessibility services.</p>
+<p>If you build custom controls that extend the {@link android.view.View} class, you must complete
+some additional work to make sure your components are accessible. This document discusses how to
+make custom view controls compatible with accessibility services.</p>
+
+<p class="note">
+<strong>Note:</strong> The implementation steps in this document describe the requirements for
+making your application accessible for users with blindness or low-vision. Be sure to review the
+requirements for serving users who are deaf and hard of hearing in the
+<a href="{@docRoot}guide/topics/ui/accessibility/checklist.html">Accessibility Developer
+Checklist</a></p>.
+
<h2 id="label-ui">Labeling User Interface Elements</h2>
-<p>Many user interface controls rely on visual cues to inform users of their meaning. For
+<p>Many user interface controls depend on visual cues to indicate their meaning and usage. For
example, a note-taking application might use an {@link android.widget.ImageButton} with a
-picture of a plus sign to indicate that the user can add a new note. Or, an {@link
-android.widget.EditText} component may have a label near it that indicates its purpose. When a user
-with impaired vision accesses your application, these visual cues are often useless.</p>
+picture of a plus sign to indicate that the user can add a new note. An {@link
+android.widget.EditText} component may have a label near it that indicates its purpose. A user
+with impaired vision can't see these cues well enough to follow them, which makes them useless.</p>
-<p>To provide textual information about interface controls (as an alternative to the visual cues),
-use the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
-{@code android:contentDescription}</a> attribute. The text you provide in this attribute is not
-visible on the screen, but if a user has enabled accessibility services that provide audible
-prompts, then the description in this attribute is read aloud to the user.</p>
+<p>You can make these controls more accessible with the
+<a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+{@code android:contentDescription}</a> XML layout attribute. The text in this attribute does not
+appear on screen, but if the user enables accessibility services that provide audible prompts, then
+when the user navigates to that control, the text is spoken.</p>
-<p>Set the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+<p>For this reason, set the
+<a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
{@code android:contentDescription}</a> attribute for every {@link android.widget.ImageButton},
-{@link android.widget.ImageView}, {@link android.widget.EditText}, {@link android.widget.CheckBox}
-in your application's user interface, and on any other input controls that might require additional
-information for users who are not able to see it.</p>
+{@link android.widget.ImageView}, {@link android.widget.CheckBox}
+in your application's user interface, and add descriptions to any other input controls that might
+require additional information for users who are not able to see it.</p>
<p>For example, the following {@link android.widget.ImageButton} sets the content description for
the plus button to the {@code add_note} string resource, which could be defined as “Add note" for an
@@ -108,32 +124,38 @@
android:contentDescription=”@string/add_note”/>
</pre>
-<p>By including the description, speech-based accessibility services can announce "Add note" when a
-user moves focus to this button or hovers over it.</p>
+<p>By including the description, an accessibility service that provides spoken feedback can announce
+"Add note" when a user moves focus to this button or hovers over it.</p>
<p class="note"><strong>Note:</strong> For {@link android.widget.EditText} fields, provide an
<a href="{@docRoot}reference/android/widget/TextView.html#attr_android:hint">android:hint</a>
-attribute to help users understand what content is expected.</p>
+attribute <em>instead</em> of a content description, to help users understand what content is
+expected when the text field is empty. When the field is filled, TalkBack reads the entered
+content to the user, instead of the hint text.</p>
+
<h2 id="focus-nav">Enabling Focus Navigation</h2>
<p>Focus navigation allows users with disabilities to step through user interface controls using a
-directional controller. Directional controllers can be physical, such as a clickable trackball,
-directional pad (D-pad) or arrow keys, tab key navigation with an attached keyboard or a software
-application, such as the
+directional controller. Directional controllers can be physical, such as a trackball, directional
+pad (D-pad) or arrow keys, or virtual, such as the
<a href="https://play.google.com/store/apps/details?id=com.googlecode.eyesfree.inputmethod.latin">
-Eyes-Free Keyboard</a>, that provides an on-screen directional control.</p>
+Eyes-Free Keyboard</a>, or the gestures navigation mode available in Android 4.1 and higher.
+Directional controllers are a primary means of navigation for many Android users.
+</p>
-<p>A directional controller is a primary means of navigation for many users.
-Verify that all user interface (UI) controls in your application are accessible
-without using the touchscreen and that clicking with the center button (or OK button) of a
-directional controller has the same effect as touching the controls on the touchscreen. For
-information on testing directional controls, see <a href="#test-navigation">Testing focus
-navigation</a>.</p>
+<p>To ensure that users can navigate your application using only a directional controller, verify
+that all user interface (UI) input controls in your application can be reached and activated
+without using the touchscreen. You should also verify that clicking with the center button (or OK
+button) of a directional controller has the same effect as touching a control that already has
+focus. For information on testing directional controls, see
+<a href="{@docRoot}tools/testing/testing_accessibility.html#test-navigation">Testing
+focus navigation</a>.</p>
+
<h3 id="focus-enable">Enabling view focus</h3>
-<p>A user interface element is accessible using directional controls when its
+<p>A user interface element is reachable using directional controls when its
<a href="{@docRoot}reference/android/view/View.html#attr_android:focusable">
{@code android:focusable}</a> attribute is set to {@code true}. This setting allows users to focus
on the element using the directional controls and then interact with it. The user interface controls
@@ -149,44 +171,44 @@
<li>{@link android.view.View#requestFocus requestFocus()}</li>
</ul>
-<p>When working with a view that is not focusable by default, you can make it focusable from the XML
-layout file by setting the
-<a href="{@docRoot}reference/android/view/View.html#attr_android:focusable">
-{@code android:focusable}</a> attribute to {@code true} or by using the {@link
+<p>If a view is not focusable by default, you can make it focusable in your layout file by setting
+the <a href="{@docRoot}reference/android/view/View.html#attr_android:focusable">
+{@code android:focusable}</a> attribute to {@code true} or by calling the its {@link
android.view.View#setFocusable setFocusable()} method.</p>
+
<h3 id="focus-order">Controlling focus order</h3>
<p>When users navigate in any direction using directional controls, focus is passed from one
-user interface element (View) to another, as determined by the focus ordering. The ordering of the
-focus movement is based on an algorithm that finds the nearest neighbor in a given direction. In
-rare cases, the default algorithm may not match the order that you intended for your UI. In these
-situations, you can provide explicit overrides to the ordering using the following XML attributes in
-the layout file:</p>
+user interface element (view) to another, as determined by the focus order. This order is based on
+an algorithm that finds the nearest neighbor in a given direction. In rare cases, the algorithm may
+not match the order that you intended or may not be logical for users. In these situations, you can
+provide explicit overrides to the ordering using the following XML attributes in your layout file:
+</p>
<dl>
- <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusDown"
->{@code android:nextFocusDown}</a></dt>
- <dd>Defines the next view to receive focus when the user navigates down.</dd>
- <a><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusLeft"
->{@code android:nextFocusLeft}</a></dt>
- <dd>Defines the next view to receive focus when the user navigates left.</dd>
- <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusRight"
->{@code android:nextFocusRight}</a></dt>
- <dd>Defines the next view to receive focus when the user navigates right.</dd>
- <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusUp"
->{@code android:nextFocusUp}</a></dt>
- <dd>Defines the next view to receive focus when the user navigates up.</dd>
+ <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusDown">
+ {@code android:nextFocusDown}</a></dt>
+ <dd>Defines the next view to receive focus when the user navigates down.</dd>
+ <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusLeft">
+ {@code android:nextFocusLeft}</a></dt>
+ <dd>Defines the next view to receive focus when the user navigates left.</dd>
+ <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusRight">
+ {@code android:nextFocusRight}</a></dt>
+ <dd>Defines the next view to receive focus when the user navigates right.</dd>
+ <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusUp">
+ {@code android:nextFocusUp}</a></dt>
+ <dd>Defines the next view to receive focus when the user navigates up.</dd>
</dl>
-<p>The following example XML layout shows two focusable user interface elements where the <a
-href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusDown"
->{@code android:nextFocusDown}</a> and <a
-href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusUp"
->{@code android:nextFocusUp}</a> attributes have been explicitly set. The {@link android.widget.TextView} is
+<p>The following example XML layout shows two focusable user interface elements where the
+<a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusDown">{@code
+android:nextFocusDown}</a> and
+<a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusUp">{@code
+android:nextFocusUp}</a> attributes have been explicitly set. The {@link android.widget.TextView} is
located to the right of the {@link android.widget.EditText}. However, since these properties have
been set, the {@link android.widget.TextView} element can now be reached by pressing the down arrow
-when focus is on the {@link android.widget.EditText} element: </p>
+when focus is on the {@link android.widget.EditText} element:</p>
<pre>
<LinearLayout android:orientation="horizontal"
@@ -218,8 +240,9 @@
<ul>
<li>Handle directional controller clicks</li>
- <li>Implement Accessibility API methods</li>
- <li>Send {@link android.view.accessibility.AccessibilityEvent} objects specific to your custom view</li>
+ <li>Implement accessibility API methods</li>
+ <li>Send {@link android.view.accessibility.AccessibilityEvent} objects specific to your custom
+ view</li>
<li>Populate {@link android.view.accessibility.AccessibilityEvent} and {@link
android.view.accessibility.AccessibilityNodeInfo} for your view</li>
</ul>
@@ -243,9 +266,9 @@
<p>Accessibility events are messages about users interaction with visual interface components in
your application. These messages are handled by <a href="services.html">Accessibility Services</a>,
-which use the information in these events to produce supplemental feedback and prompts when users
-have enabled accessibility services. As of Android 4.0 (API Level 14) and higher, the methods for
-generating accessibility events have been expanded to provide more detailed information beyond the
+which use the information in these events to produce supplemental feedback and prompts. In
+Android 4.0 (API Level 14) and higher, the methods for
+generating accessibility events have been expanded to provide more detailed information than the
{@link android.view.accessibility.AccessibilityEventSource} interface introduced in Android 1.6 (API
Level 4). The expanded accessibility methods are part of the {@link android.view.View} class as well
as the {@link android.view.View.AccessibilityDelegate} class. The methods are as follows:</p>
@@ -262,12 +285,12 @@
<dd>(API Level 4) This method is used when the calling code needs to directly control the check
for accessibility being enabled on the device ({@link
android.view.accessibility.AccessibilityManager#isEnabled AccessibilityManager.isEnabled()}). If
-you do implement this method, you must assume that the calling method has already checked that
-accessibility is enabled and the result is {@code true}. You typically do not need to implement this
-method for a custom view.</dd>
+you do implement this method, you must perform the call as if accessibility is enabled, regardless
+of the actual system setting. You typically do not need to implement this method for a custom view.
+</dd>
<dt>{@link android.view.View#dispatchPopulateAccessibilityEvent
-dispatchPopulateAccessibilityEvent()} </dt>
+dispatchPopulateAccessibilityEvent()}</dt>
<dd>(API Level 4) The system calls this method when your custom view generates an
accessibility event. As of API Level 14, the default implementation of this method calls {@link
android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()} for this view and
@@ -276,21 +299,21 @@
accessibility services on revisions of Android <em>prior</em> to 4.0 (API Level 14) you
<em>must</em> override this method and populate {@link
android.view.accessibility.AccessibilityEvent#getText} with descriptive text for your custom
-view.</dd>
+view, which is spoken by accessibility services, such as TalkBack.</dd>
<dt>{@link android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()}</dt>
- <dd>(API Level 14) This method sets the text output of an {@link
+ <dd>(API Level 14) This method sets the spoken text prompt of the {@link
android.view.accessibility.AccessibilityEvent} for your view. This method is also called if the
view is a child of a view which generates an accessibility event.
<p class="note"><strong>Note:</strong> Modifying additional attributes beyond the text within
-this method potentially overwrites properties set by other methods. So, while you are able modify
+this method potentially overwrites properties set by other methods. While you can modify
attributes of the accessibility event with this method, you should limit these changes
-to text content only and use the {@link android.view.View#onInitializeAccessibilityEvent
+to text content, and use the {@link android.view.View#onInitializeAccessibilityEvent
onInitializeAccessibilityEvent()} method to modify other properties of the event.</p>
- <p class="note"><strong>Note:</strong> If your implementation of this event calls for completely
-overiding the output text without allowing other parts of your layout to modify its content, then
+ <p class="note"><strong>Note:</strong> If your implementation of this event completely
+overrides the output text without allowing other parts of your layout to modify its content, then
do not call the super implementation of this method in your code.</p>
</dd>
@@ -306,7 +329,7 @@
<dt>{@link android.view.View#onInitializeAccessibilityNodeInfo
onInitializeAccessibilityNodeInfo()}</dt>
<dd>(API Level 14) This method provides accessibility services with information about the state of
-the view. The default {@link android.view.View} implementation sets a standard set of view
+the view. The default {@link android.view.View} implementation has a standard set of view
properties, but if your custom view provides interactive control beyond a simple {@link
android.widget.TextView} or {@link android.widget.Button}, you should override this method and set
the additional information about your view into the {@link
@@ -315,7 +338,7 @@
<dt>{@link android.view.ViewGroup#onRequestSendAccessibilityEvent
onRequestSendAccessibilityEvent()}</dt>
<dd>(API Level 14) The system calls this method when a child of your view has generated an
-{@link android.view.accessibility.AccessibilityEvent}. This step allows the the parent view to amend
+{@link android.view.accessibility.AccessibilityEvent}. This step allows the parent view to amend
the accessibility event with additional information. You should implement this method only if your
custom view can have child views and if the parent view can provide context information to the
accessibility event that would be useful to accessibility services.</dd>
@@ -333,7 +356,7 @@
{@link android.support.v4.view.ViewCompat#setAccessibilityDelegate
ViewCompat.setAccessibilityDelegate()} method to implement the accessibility methods
above. For an example of this approach, see the Android Support Library (revision 5 or higher)
-sample {@code AccessibilityDelegateSupportActivity} in
+sample {@code AccessibilityDelegateSupportActivity} in
({@code <sdk>/extras/android/support/v4/samples/Support4Demos/})
</li>
</ul>
@@ -446,20 +469,20 @@
}
</pre>
-<p>On Android 4.0 (API Level 14) and higher, the {@link
+<p>For Android 4.0 (API Level 14) and higher, use the {@link
android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()} and
{@link android.view.View#onInitializeAccessibilityEvent onInitializeAccessibilityEvent()}
-methods are the recommended way to populate or modify the information in an {@link
-android.view.accessibility.AccessibilityEvent}. Use the
+methods to populate or modify the information in an {@link
+android.view.accessibility.AccessibilityEvent}. Use the
{@link android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()} method
specifically for adding or modifying the text content of the event, which is turned into audible
prompts by accessibility services such as TalkBack. Use the
{@link android.view.View#onInitializeAccessibilityEvent onInitializeAccessibilityEvent()} method for
populating additional information about the event, such as the selection state of the view.</p>
-<p>In addition, you should also implement the
+<p>In addition, implement the
{@link android.view.View#onInitializeAccessibilityNodeInfo onInitializeAccessibilityNodeInfo()}
-method. {@link android.view.accessibility.AccessibilityNodeInfo} objects populated by this method
+method. The {@link android.view.accessibility.AccessibilityNodeInfo} objects populated by this method
are used by accessibility services to investigate the view hierarchy that generated an accessibility
event after receiving that event, to obtain a more detailed context information and provide
appropriate feedback to users.</p>
@@ -467,8 +490,8 @@
<p>The example code below shows how override these three methods by using
{@link android.support.v4.view.ViewCompat#setAccessibilityDelegate
ViewCompat.setAccessibilityDelegate()}. Note that this sample code requires that the Android
-<a href="{@docRoot}tools/extras/support-library.html">Support Library</a> for API Level 4 (revision 5
-or higher) is added to your project.</p>
+<a href="{@docRoot}tools/extras/support-library.html">Support Library</a> for API Level 4 (revision
+5 or higher) is added to your project.</p>
<pre>
ViewCompat.setAccessibilityDelegate(new AccessibilityDelegateCompat() {
@@ -509,10 +532,10 @@
}
</pre>
-<p>On applications targeting Android 4.0 (API Level 14) and higher, these methods can be implemented
+<p>In applications targeting Android 4.0 (API Level 14) and higher, you can implement these methods
directly in your custom view class. For another example of this approach, see the Android
-<a href="{@docRoot}tools/extras/support-library.html">Support Library</a> (revision 5 or higher) sample
-{@code AccessibilityDelegateSupportActivity} in
+<a href="{@docRoot}tools/extras/support-library.html">Support Library</a> (revision 5 or higher)
+sample {@code AccessibilityDelegateSupportActivity} in
({@code <sdk>/extras/android/support/v4/samples/Support4Demos/}).</p>
<p class="note"><strong>Note:</strong> You may find information on implementing accessibility for
@@ -525,50 +548,141 @@
methods.</p>
+<h3 id="virtual-hierarchy">Providing a customized accessibility context</h3>
+
+<p>In Android 4.0 (API Level 14), the framework was enhanced to allow accessibility services to
+ inspect the containing view hierarchy of a user interface component that generates an
+ accessibility event. This enhancement allows accessibility services to provide a much richer set
+ of contextual information with which to aid users.</p>
+
+<p>There are some cases where accessibility services cannot get adequate information
+ from the view hierarchy. An example of this is a custom interface control that has two or more
+ separately clickable areas, such as a calendar control. In this case, the services cannot get
+ adequate information because the clickable subsections are not part of the view hierarchy.</p>
+
+<img src="calendar.png" alt="" id="figure1" />
+<p class="img-caption">
+ <strong>Figure 1.</strong> A custom calendar view with selectable day elements.
+</p>
+
+<p>In the example shown in Figure 1, the entire calendar is implemented as a single view, so if you
+ do not do anything else, accessibility services do not receive enough information about the
+ content of the view and the user's selection within the view. For example, if a user clicks on the
+ day containing <strong>17</strong>, the accessibility framework only receives the description
+ information for the whole calendar control. In this case, the TalkBack accessibility service would
+ simply announce "Calendar" or, only slightly better, "April Calendar" and the user would be left
+ to wonder what day was selected.</p>
+
+<p>To provide adequate context information for accessibility services in situations like this,
+ the framework provides a way to specify a virtual view hierarchy. A <em>virtual view
+ hierarchy</em> is a way for application developers to provide a complementary view hierarchy
+ to accessibility services that more closely matches the actual information on screen. This
+ approach allows accessibility services to provide more useful context information to users.</p>
+
+<p>Another situation where a virtual view hierarchy may be needed is a user interface containing
+ a set of controls (views) that have closely related functions, where an action on one control
+ affects the contents of one or more elements, such as a number picker with separate up and down
+ buttons. In this case, accessibility services cannot get adequate information because action on
+ one control changes content in another and the relationship of those controls may not be apparent
+ to the service. To handle this situation, group the related controls with a containing view and
+ provide a virtual view hierarchy from this container to clearly represent the information and
+ behavior provided by the controls.</p>
+
+<p>In order to provide a virtual view hierarchy for a view, override the {@link
+ android.view.View#getAccessibilityNodeProvider} method in your custom view or view group and
+ return an implementation of {@link android.view.accessibility.AccessibilityNodeProvider}. For an
+ example implementation of this accessibility feature, see
+ {@code AccessibilityNodeProviderActivity} in the ApiDemos sample project. You can implement a
+ virtual view hierarchy that is compatible with Android 1.6 and later by using the
+ <a href="{@docRoot}tools/extras/support-library.html">Support Library</a> with the
+ {@link android.support.v4.view.ViewCompat#getAccessibilityNodeProvider
+ ViewCompat.getAccessibilityNodeProvider()} method and providing an implementation with
+ {@link android.support.v4.view.accessibility.AccessibilityNodeProviderCompat}.</p>
+
+
+<h3 id="custom-touch-events">Handling custom touch events</h3>
+
+<p>Custom view controls may require non-standard touch event behavior. For example, a custom
+control may use the {@link android.view.View#onTouchEvent} listener method to detect the
+{@link android.view.MotionEvent#ACTION_DOWN} and {@link android.view.MotionEvent#ACTION_UP} events
+and trigger a special click event. In order to maintain compatibility with accessibility services,
+the code that handles this custom click event must do the following:</p>
+
+<ol>
+ <li>Generate an appropriate {@link android.view.accessibility.AccessibilityEvent} for the
+ interpreted click action.</li>
+ <li>Enable accessibility services to perform the custom click action for users who are not able to
+ use a touch screen.</li>
+</ol>
+
+<p>To handle these requirements in an efficient way, your code should override the
+{@link android.view.View#performClick} method, which must call the super implementation of this
+method and then execute whatever actions are required by the click event. When the custom click
+action is detected, that code should then call your {@code performClick()} method. The following
+code example demonstrates this pattern.</p>
+
+<pre>
+class CustomTouchView extends View {
+
+ public CustomTouchView(Context context) {
+ super(context);
+ }
+
+ boolean mDownTouch = false;
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+
+ // Listening for the down and up touch events
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mDownTouch = true;
+ return true;
+
+ case MotionEvent.ACTION_UP:
+ if (mDownTouch) {
+ mDownTouch = false;
+ performClick(); // Call this method to handle the response, and
+ // thereby enable accessibility services to
+ // perform this action for a user who cannot
+ // click the touchscreen.
+ return true;
+ }
+ }
+ return false; // Return false for other touch events
+ }
+
+ @Override
+ public boolean performClick() {
+ // Calls the super implementation, which generates an AccessibilityEvent
+ // and calls the onClick() listener on the view, if any
+ super.performClick();
+
+ // Handle the action for the custom click here
+
+ return true;
+ }
+}
+</pre>
+
+<p>The pattern shown above makes sure that the custom click event is compatible with
+accessibility services by using the {@link android.view.View#performClick} method to both generate
+an accessibility event and provide an entry point for accessibility services to act on behalf of a
+user to perform this custom click event.</p>
+
+<p class="note"><strong>Note:</strong> If your custom view has distinct clickable regions, such as
+a custom calendar view, you must implement a <a href="#virtual-hierarchy">virtual view
+hierarchy</a> by overriding {@link android.view.View#getAccessibilityNodeProvider} in your custom
+view in order to be compatible with accessibility services.</p>
+
+
<h2 id="test">Testing Accessibility</h2>
<p>Testing the accessibility of your application is an important part of ensuring your users have a
-great experience. You can test the most important parts of accessibility by testing your application
-with audible feedback enabled and testing navigation within your application using directional
-controls.</p>
+great experience. You can test the most important accessibility features by using your application
+with audible feedback enabled and navigating within your application using only directional
+controls. For more information on testing accessibility in your application, see the
+<a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing Checklist</a>.
+</p>
-<h3 id="test-audibles">Testing audible feedback</h3>
-<p>You can simulate the experience for many users by enabling an accessibility service that speaks
-as you move around the screen. The Explore by Touch accessibility service, which is available on
-devices with Android 4.0 and later. The <a
-href="https://play.google.com/store/apps/details?id=com.google.android.marvin.talkback">TalkBack</a>
-accessibility service, by the <a href="http://code.google.com/p/eyes-free/">Eyes-Free
-Project</a> comes preinstalled on many Android devices.</p>
-
-<p>To enable TalkBack on revisions of Android prior to Android 4.0:</p>
-<ol>
- <li>Launch the Settings application.</li>
- <li>Navigate to the <strong>Accessibility</strong> category and select it.</li>
- <li>Select <strong>Accessibility</strong> to enable it.</li>
- <li>Select <strong>TalkBack</strong> to enable it.</li>
-</ol>
-
-<p class="note"><strong>Note:</strong> If the TalkBack accessibility service is not available, you
-can install it for free from <a href="http://play.google.com">Google Play</a>.</p>
-
-<p>To enable Explore by Touch on Android 4.0 and later:</p>
-<ol>
- <li>Launch the Settings application.</li>
- <li>Navigate to the <strong>Accessibility</strong> category and select it.</li>
- <li>Select the <strong>TalkBack</strong> to enable it.</li>
- <li>Return to the <strong>Accessibility</strong> category and select <strong>Explore by
-Touch</strong> to enable it.
- <p class="note"><strong>Note:</strong> You must turn on TalkBack <em>first</em>, otherwise this
-option is not available.</p>
- </li>
-</ol>
-
-<h3 id="test-navigation">Testing focus navigation</h3>
-
-<p>As part of your accessibility testing, you can test navigation of your application using focus,
-even if your test devices does not have a directional controller. The <a
-href="{@docRoot}tools/help/emulator.html">Android Emulator</a> provides a
-simulated directional controller that you can easily use to test navigation. You can also use a
-software-based directional controller, such as the one provided by the
-<a href="https://play.google.com/store/apps/details?id=com.googlecode.eyesfree.inputmethod.latin">
-Eyes-Free Keyboard</a> to simulate use of a D-pad.</p>
diff --git a/docs/html/guide/topics/ui/accessibility/calendar.png b/docs/html/guide/topics/ui/accessibility/calendar.png
new file mode 100644
index 0000000..ca5c44f
--- /dev/null
+++ b/docs/html/guide/topics/ui/accessibility/calendar.png
Binary files differ
diff --git a/docs/html/guide/topics/ui/accessibility/checklist.jd b/docs/html/guide/topics/ui/accessibility/checklist.jd
new file mode 100644
index 0000000..9473d1b
--- /dev/null
+++ b/docs/html/guide/topics/ui/accessibility/checklist.jd
@@ -0,0 +1,173 @@
+page.title=Accessibility Developer Checklist
+parent.title=Accessibility
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#requirements">Accessibility Requirements</a></li>
+ <li><a href="#recommendations">Accessibility Recommendations</a></li>
+ <li><a href="#special-cases">Special Cases and Considerations</a></li>
+ </ol>
+
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}design/patterns/accessibility.html">Android Design: Accessibility</a></li>
+ <li><a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing Checklist</a></li>
+ <li><a href="{@docRoot}training/accessibility/index.html">Training: Implementing Accessibility</a></li>
+ <li><a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a></li>
+ </ol>
+
+</div>
+</div>
+
+<p>Making an application accessible is about a deep commitment to usability, getting the
+details right and delighting your users. This document provides a checklist of accessibility
+requirements, recommendations and considerations to help you make sure your application is
+accessible. Following this checklist does not guarantee your application is accessible, but it's a
+good place to start.</p>
+
+<p>Creating an accessible application is not just the responsibility of developers. Involve your
+design and testing folks as well, and make them are aware of the guidelines for these other stages
+of development:</p>
+
+<ul>
+ <li><a href="{@docRoot}design/patterns/accessibility.html">Android Design: Accessibility</a></li>
+ <li><a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing
+ Checklist</a></li>
+</ul>
+
+<p>In most cases, creating an accessible Android application does not require extensive code
+restructuring. Rather, it means working through the subtle details of how users interact with your
+application, so you can provide them with feedback they can sense and understand. This checklist
+helps you focus on the key development issues to get the details of accessibility right.</p>
+
+
+<h2 id="requirements">Accessibility Requirements</h2>
+
+<p>The following steps must be completed in order to ensure a minimum level of application
+ accessibility.</p>
+
+<ol>
+ <li><strong>Describe user interface controls:</strong> Provide content
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#label-ui">descriptions</a> for user
+ interface components that do not have visible text, particularly
+ {@link android.widget.ImageButton}, {@link android.widget.ImageView}
+ and {@link android.widget.CheckBox} components. Use the
+ <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+ {@code android:contentDescription}</a> XML layout attribute or the {@link
+ android.view.View#setContentDescription} method to provide this information for accessibility
+ services. (Exception: <a href="#decorative">decorative graphics</a>)</li>
+ <li><strong>Enable focus-based navigation:</strong> Make sure
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#focus-nav">users can navigate</a>
+ your screen layouts using hardware-based or software directional controls (D-pads, trackballs,
+ keyboards and navigation gestures). In a few cases, you may need to make user interface components
+ <a href="{@docRoot}reference/android/view/View.html#attr_android:focusable">focusable</a>
+ or change the <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#focus-order">focus
+ order</a> to be more logical for user actions.</li>
+ <li><strong>Custom view controls:</strong> If you build
+ <a href="{@docRoot}guide/topics/ui/custom-components.html">custom interface controls</a> for
+ your application, <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#custom-views">
+ implement accessibility interfaces</a> for your custom views and provide content descriptions.
+ For custom controls that are intended to be compatible with versions of Android back to 1.6,
+ use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a> to implement
+ the latest accessibility features.</li>
+ <li><strong>No audio-only feedback:</strong> Audio feedback must always have a secondary
+ feedback mechanism to support users who are deaf or hard of hearing. For example, a sound alert
+ for the arrival of a message must be accompanied by a system
+ {@link android.app.Notification}, haptic feedback (if available) or other visual alert.</li>
+ <li><strong>Test:</strong> Test accessibility by navigating your application
+ using directional controls, and using eyes-free navigation with TalkBack enabled.
+ For more accessibility testing information, see the
+ <a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing
+ Checklist</a>.</li>
+</ol>
+
+
+<h2 id="recommendations">Accessibility Recommendations</h2>
+
+<p>The following steps are recommended for ensuring the accessibility of your application. If you
+ do not take these actions, it may impact the overall accessibility and quality of your
+ application.</p>
+
+<ol>
+ <li><strong>Android Design Accessibility Guidelines:</strong> Before building your layouts,
+ review and follow the accessibility guidelines provided in the
+ <a href="{@docRoot}design/patterns/accessibility.html">Design guidelines</a>.</li>
+ <li><strong>Framework-provided controls:</strong> Use Android's built-in user interface
+ controls whenever possible, as these components provide accessibility support by default.</li>
+ <li><strong>Temporary or self-hiding controls and notifications:</strong> Avoid having user
+ interface controls that fade out or disappear after a certain amount of time. If this behavior
+ is important to your application, provide an alternative interface for these functions.</li>
+</ol>
+
+
+<h2 id="special-cases">Special Cases and Considerations</h2>
+
+<p>The following list describes specific situations where action should be taken to ensure an
+ accessible app. Review this list to see if any of these special cases and considerations apply to
+ your application, and take the appropriate action.</p>
+
+<ol>
+ <li><strong>Text field hints:</strong> For {@link android.widget.EditText} fields, provide an
+ <a href="{@docRoot}reference/android/widget/TextView.html#attr_android:hint">android:hint</a>
+ attribute <em>instead</em> of a content description, to help users understand what content is
+ expected when the text field is empty and allow the contents of the field to be spoken when
+ it is filled.</li>
+ <li><strong>Custom controls with high visual context:</strong> If your application contains a
+ <a href="{@docRoot}guide/topics/ui/custom-components.html">custom control</a> with a high degree
+ of visual context (such as a calendar control), default accessibility services processing may
+ not provide adequate descriptions for users, and you should consider providing a
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#virtual-hierarchy">virtual
+ view hierarchy</a> for your control using
+ {@link android.view.accessibility.AccessibilityNodeProvider}.</li>
+ <li><strong>Custom controls and click handling:</strong> If a custom control in your
+ application performs specific handling of user touch interaction, such as listening with
+ {@link android.view.View#onTouchEvent} for {@link android.view.MotionEvent#ACTION_DOWN
+ MotionEvent.ACTION_DOWN} and {@link android.view.MotionEvent#ACTION_UP MotionEvent.ACTION_UP}
+ and treating it as a click event, you must trigger an {@link
+ android.view.accessibility.AccessibilityEvent} equivalent to a click and provide a way for
+ accessibility services to perform this action for users. For more information, see
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#custom-touch-events">Handling custom
+ touch events</a>.</li>
+ <li><strong>Controls that change function:</strong> If you have buttons or other controls
+ that change function during the normal activity of a user in your application (for example, a
+ button that changes from <strong>Play</strong> to <strong>Pause</strong>), make sure you also
+ change the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+ {@code android:contentDescription}</a> of the button appropriately.</li>
+ <li><strong>Prompts for related controls:</strong> Make sure sets of controls which provide a
+ single function, such as the {@link android.widget.DatePicker}, provide useful audio feedback
+ when an user interacts with the individual controls.</li>
+ <li><strong>Video playback and captioning:</strong> If your application provides video
+ playback, it must support captioning and subtitles to assist users who are deaf or hard of
+ hearing. Your video playback controls must also clearly indicate if captioning is available for
+ a video and provide a clear way of enabling captions.</li>
+ <li><strong>Supplemental accessibility audio feedback:</strong> Use only the Android accessibility
+ framework to provide accessibility audio feedback for your app. Accessibility services such as
+ <a href="https://play.google.com/store/apps/details?id=com.google.android.marvin.talkback"
+ >TalkBack</a> should be the only way your application provides accessibility audio prompts to
+ users. Provide the prompting information with a
+ <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">{@code
+ android:contentDescription}</a> XML layout attribute or dynamically add it using accessibility
+ framework APIs. For example, if your application takes action that you want to announce to a
+ user, such as automatically turning the page of a book, use the {@link
+ android.view.View#announceForAccessibility} method to have accessibility services speak this
+ information to the user.</li>
+ <li><strong>Custom controls with complex visual interactions:</strong> For custom controls that
+ provide complex or non-standard visual interactions, provide a
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#virtual-hierarchy">virtual view
+ hierarchy</a> for your control using {@link android.view.accessibility.AccessibilityNodeProvider}
+ that allows accessibility services to provide a simplified interaction model for the user. If
+ this approach is not feasible, consider providing an alternate view that is accessible.</li>
+ <li><strong>Sets of small controls:</strong> If you have controls that are smaller than
+ the minimum recommended touch size in your application screens, consider grouping these controls
+ together using a {@link android.view.ViewGroup} and providing a
+ <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">{@code
+ android:contentDescription}</a> for the group.</li>
+ <li id="decorative"><strong>Decorative images and graphics:</strong> Elements in application
+ screens that are purely decorative and do not provide any content or enable a user action should
+ not have accessibility content descriptions.</li>
+</ol>
diff --git a/docs/html/guide/topics/ui/accessibility/index.jd b/docs/html/guide/topics/ui/accessibility/index.jd
index 6fd71e2..6cdbde4 100644
--- a/docs/html/guide/topics/ui/accessibility/index.jd
+++ b/docs/html/guide/topics/ui/accessibility/index.jd
@@ -8,47 +8,67 @@
<h2>Topics</h2>
<ol>
- <li><a href="{@docRoot}guide/topics/ui/accessibility/apps.html">Making Applications Accessible</a>
- </li>
- <li><a href="{@docRoot}guide/topics/ui/accessibility/services.html">Building Accessibility
- Services</a></li>
+ <li><a href="apps.html">
+ Making Applications Accessible</a></li>
+ <li><a href="checklist.html">
+ Accessibility Developer Checklist</a></li>
+ <li><a href="services.html">
+ Building Accessibility Services</a></li>
</ol>
- <h2>Key classes</h2>
- <ol>
- <li>{@link android.view.accessibility.AccessibilityEvent}</li>
- <li>{@link android.accessibilityservice.AccessibilityService}</li>
- </ol>
-
<h2>See also</h2>
<ol>
- <li><a href="{@docRoot}training/accessibility/index.html">Implementing Accessibility</a></li>
+ <li><a href="{@docRoot}design/patterns/accessibility.html">Android Design: Accessibility</a></li>
+ <li><a href="{@docRoot}training/accessibility/index.html">Training: Implementing Accessibility</a></li>
+ <li><a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing Checklist</a></li>
+ </ol>
+
+ <h2>Related Videos</h2>
+ <ol>
+ <li>
+ <iframe title="Google I/O 2012 - Making Android Apps Accessible"
+ width="210" height="160"
+ src="http://www.youtube.com/embed/q3HliaMjL38?rel=0&hd=1"
+ frameborder="0" allowfullscreen>
+ </iframe>
+ <li>
</ol>
</div>
</div>
-<p>Many Android users have disabilities that require them to interact with their Android devices in
-different ways. These include users who have visual, physical or age-related disabilities that
-prevent them from fully seeing or using a touchscreen.</p>
+<p>Many Android users have different abilities that require them to interact with their Android
+devices in different ways. These include users who have visual, physical or age-related limitations
+that prevent them from fully seeing or using a touchscreen, and users with hearing loss who may not
+be able to perceive audible information and alerts.</p>
<p>Android provides accessibility features and services for helping these users navigate their
-devices more easily, including text-to-speech, haptic feedback, trackball and D-pad navigation that
-augment their experience. Android application developers can take advantage of these services to
-make their applications more accessible and also build their own accessibility services.</p>
+devices more easily, including text-to-speech, haptic feedback, gesture navigation, trackball and
+directional-pad navigation. Android application developers can take advantage of these services to
+make their applications more accessible.</p>
+
+<p>Android developers can also build their own accessibility services, which can provide
+enhanced usability features such as audio prompting, physical feedback, and alternative navigation
+modes. Accessibility services can provide these enhancements for all applications, a set of
+applications or just a single app.</p>
<p>The following topics show you how to use the Android framework to make applications more
accessible.</p>
<dl>
- <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/apps.html">Making Applications
-Accessible</a></strong>
+ <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/apps.html">
+ Making Applications Accessible</a></strong>
</dt>
<dd>Development practices and API features to ensure your application is accessible to users with
disabilities.</dd>
- <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/services.html">Building Accessibility
-Services</a></strong>
+ <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/checklist.html">
+ Accessibility Developer Checklist</a></strong>
+ </dt>
+ <dd>A checklist to help developers ensure that their applications are accessible.</dd>
+
+ <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/services.html">
+ Building Accessibility Services</a></strong>
</dt>
<dd>How to use API features to build services that make other applications more accessible for
users.</dd>
diff --git a/docs/html/guide/topics/ui/accessibility/services.jd b/docs/html/guide/topics/ui/accessibility/services.jd
index 7d36181..2a6fe7a 100644
--- a/docs/html/guide/topics/ui/accessibility/services.jd
+++ b/docs/html/guide/topics/ui/accessibility/services.jd
@@ -14,8 +14,16 @@
<li><a href="#service-config">Accessibility service configuration</a></li>
</ol>
</li>
+ <li><a href="#register">Registering for Accessibility Events</a></li>
<li><a href="#methods">AccessibilityService Methods</a></li>
<li><a href="#event-details">Getting Event Details</a></li>
+ <li><a href="#act-for-users">Taking Action for Users</a>
+ <ol>
+ <li><a href="#detect-gestures">Listening for gestures</a></li>
+ <li><a href="#using-actions">Using accessibility actions</a></li>
+ <li><a href="#focus-types">Using focus types</a></li>
+ </ol>
+ </li>
<li><a href="#examples">Example Code</a></li>
</ol>
@@ -30,7 +38,7 @@
<h2>See also</h2>
<ol>
- <li><a href="{@docRoot}training/accessibility/index.html">Implementing Accessibility</a></li>
+ <li><a href="{@docRoot}training/accessibility/index.html">Training: Implementing Accessibility</a></li>
</ol>
</div>
@@ -45,20 +53,20 @@
create and distribute their own services. This document explains the basics of building an
accessibility service.</p>
-<p>The ability for you to build and deploy accessibility services was introduced with Android
-1.6 (API Level 4) and received significant improvements with Android 4.0 (API Level 14). The Android
-Support Library was also updated with the release of Android 4.0 to provide support for these
-enhanced accessibility features back to Android 1.6. Developers aiming for widely compatible
-accessibility services are encouraged to use the
-<a href="{@docRoot}tools/extras/support-library.html">Support Library</a> and develop for the more
-advanced accessibility features introduced in Android 4.0.</p>
+<p>The ability for you to build and deploy accessibility services was introduced with Android 1.6
+ (API Level 4) and received significant improvements with Android 4.0 (API Level 14). The Android
+ <a href="{@docRoot}tools/extras/support-library.html">Support Library</a> was also updated with
+ the release of Android 4.0 to provide support for these enhanced accessibility features back to
+ Android 1.6. Developers aiming for widely compatible accessibility services are encouraged to use
+ the Support Library and develop for the more advanced accessibility features introduced in
+ Android 4.0.</p>
<h2 id="manifest">Manifest Declarations and Permissions</h2>
<p>Applications that provide accessibility services must include specific declarations in their
- application manifests in order to be treated as an accessibility service by an Android system.
- This section explains the required and optional settings for accessibility services.</p>
+ application manifests to be treated as an accessibility service by the Android system. This
+ section explains the required and optional settings for accessibility services.</p>
<h3 id="service-declaration">Accessibility service declaration</h3>
@@ -66,7 +74,9 @@
<p>In order to be treated as an accessibility service, your application must include the
{@code service} element (rather than the {@code activity} element) within the {@code application}
element in its manifest. In addition, within the {@code service} element, you must also include an
-accessibility service intent filter, as shown in the following sample:</p>
+accessibility service intent filter. For compatiblity with Android 4.1 and higher, the manifest
+must also request the {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission
+as shown in the following sample:</p>
<pre>
<application>
@@ -76,6 +86,7 @@
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
</service>
+ <uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
</application>
</pre>
@@ -123,27 +134,6 @@
/>
</pre>
-<p>One of the most important functions of the accessibility service configuration parameters is to
-allow you to specify what types of accessibility events your service can handle. Being able to
-specify this information enables accessibility services to cooperate with each other, and allows you
-as a developer the flexibility to handle only specific events types from specific applications. The
-event filtering can include the following criteria:</p>
-
-<ul>
- <li><strong>Package Names</strong> - Specify the package names of applications whose accessibility
-events you want your service to handle. If this parameter is omitted, your accessibility service is
-considered available to service accessibility events for any application. This parameter can be set
-in the accessibility service configuration files with the {@code android:packageNames} attribute as
-a comma-separated list, or set using the {@link
-android.accessibilityservice.AccessibilityServiceInfo#packageNames
-AccessibilityServiceInfo.packageNames} member.</li>
- <li><strong>Event Types</strong> - Specify the types of accessibility events you want your service
-to handle. This parameter can be set in the accessibility service configuration files with the
-{@code android:accessibilityEventTypes} attribute as a comma-separated list, or set using the
-{@link android.accessibilityservice.AccessibilityServiceInfo#eventTypes
-AccessibilityServiceInfo.eventTypes} member. </li>
-</ul>
-
<p>For more information about the XML attributes which can be used in the accessibility service
configuration file, follow these links to the reference documentation:</p>
@@ -162,9 +152,45 @@
the {@link android.accessibilityservice.AccessibilityServiceInfo} reference documentation.</p>
+<h2 id="register">Registering for Accessibility Events</h2>
+
+<p>One of the most important functions of the accessibility service configuration parameters is to
+allow you to specify what types of accessibility events your service can handle. Being able to
+specify this information enables accessibility services to cooperate with each other, and allows you
+as a developer the flexibility to handle only specific events types from specific applications. The
+event filtering can include the following criteria:</p>
+
+<ul>
+ <li><strong>Package Names</strong> - Specify the package names of applications whose accessibility
+events you want your service to handle. If this parameter is omitted, your accessibility service is
+considered available to service accessibility events for any application. This parameter can be set
+in the accessibility service configuration files with the {@code android:packageNames} attribute as
+a comma-separated list, or set using the {@link
+android.accessibilityservice.AccessibilityServiceInfo#packageNames
+AccessibilityServiceInfo.packageNames} member.</li>
+ <li><strong>Event Types</strong> - Specify the types of accessibility events you want your service
+to handle. This parameter can be set in the accessibility service configuration files with the
+{@code android:accessibilityEventTypes} attribute as a list separated by the {@code |} character
+(for example {@code accessibilityEventTypes="typeViewClicked|typeViewFocused"}), or set using the
+{@link android.accessibilityservice.AccessibilityServiceInfo#eventTypes
+AccessibilityServiceInfo.eventTypes} member. </li>
+</ul>
+
+<p>When setting up your accessibility service, carefully consider what events your service is able
+to handle and only register for those events. Since users can activate more than one accessibility
+services at a time, your service must not consume events that it is not able to handle. Remember
+that other services may handle those events in order to improve a user's experience.</p>
+
+<p class="note"><strong>Note:</strong> The Android framework dispatches accessibility events to
+more than one accessibility service if the services provide different
+<a href="{@docRoot}reference/android/R.styleable.html#AccessibilityService_accessibilityFeedbackType">
+feedback types</a>. However, if two or more services provide the same feedback type, then only the
+first registered service receives the event.</p>
+
+
<h2 id="methods">AccessibilityService Methods</h2>
-<p>An application that provides accessibility service must extend the {@link
+<p>An accessibility service must extend the {@link
android.accessibilityservice.AccessibilityService} class and override the following methods from
that class. These methods are presented in the order in which they are called by the Android system,
from when the service is started
@@ -188,15 +214,15 @@
{@link android.view.accessibility.AccessibilityEvent} that matches the event filtering parameters
specified by your accessibility service. For example, when the user clicks a button or focuses on a
user interface control in an application for which your accessibility service is providing feedback.
-When this happens, the system calls this method of your service with the associated {@link
-android.view.accessibility.AccessibilityEvent}, which you can then interpret and provide feedback to
-the user. This method may be called many times over the lifecycle of your service.</li>
+When this happens, the system calls this method, passing the associated {@link
+android.view.accessibility.AccessibilityEvent}, which the service can then interpret and use to
+provide feedback to the user. This method may be called many times over the lifecycle of your
+service.</li>
<li>{@link android.accessibilityservice.AccessibilityService#onInterrupt onInterrupt()} -
(required) This method is called when the system wants to interrupt the feedback your service is
-providing, usually in response to a user taking action, such as moving focus to a different user
-interface control than the one for which you are currently providing feedback. This method may be
-called many times over the lifecycle of your service.</li>
+providing, usually in response to a user action such as moving focus to a different control. This
+method may be called many times over the lifecycle of your service.</li>
<li>{@link android.accessibilityservice.AccessibilityService#onUnbind onUnbind()} - (optional)
This method is called when the system is about to shutdown the accessibility service. Use this
@@ -206,7 +232,9 @@
<p>These callback methods provide the basic structure for your accessibility service. It is up to
you to decide on how to process data provided by the Android system in the form of {@link
-android.view.accessibility.AccessibilityEvent} objects and provide feedback to the user.</p>
+android.view.accessibility.AccessibilityEvent} objects and provide feedback to the user. For more
+information about getting information from an accessibility event, see the
+<a href="{@docRoot}training/accessibility/service.html">Implementing Accessibility</a> training.</p>
<h2 id="event-details">Getting Event Details</h2>
@@ -214,15 +242,15 @@
<p>The Android system provides information to accessibility services about the user interface
interaction through {@link android.view.accessibility.AccessibilityEvent} objects. Prior to Android
4.0, the information available in an accessibility event, while providing a significant amount of
-detail about a user interface control selected by the user, typically provided limited contextual
+detail about a user interface control selected by the user, offered limited contextual
information. In many cases, this missing context information might be critical to understanding the
meaning of the selected control.</p>
-<p>A typical example of an interface where context is of critical importance is a calendar or day
-planner. If a user selects a 4:00 PM time slot in a Monday to Friday day list and the accessibility
-service announces “4 PM”, but fails to indicate this is a Friday a Monday, the month or day, this is
-hardly ideal feedback for the user. In this case, the context of a user interface control is of
-critical importance to a user who wants to schedule a meeting.</p>
+<p>An example of an interface where context is critical is a calendar or day planner. If the
+user selects a 4:00 PM time slot in a Monday to Friday day list and the accessibility service
+announces “4 PM”, but does not announce the weekday name, the day of the month, or the month name,
+the resulting feedback is confusing. In this case, the context of a user interface control is
+critical to a user who wants to schedule a meeting.</p>
<p>Android 4.0 significantly extends the amount of information that an accessibility service can
obtain about an user interface interaction by composing accessibility events based on the view
@@ -245,26 +273,167 @@
AccessibilityEvent.getRecordCount()} and {@link
android.view.accessibility.AccessibilityEvent#getRecord getRecord(int)} - These methods allow you to
retrieve the set of {@link android.view.accessibility.AccessibilityRecord} objects which contributed
-to the {@link android.view.accessibility.AccessibilityEvent} passed to you by the system, which can
-provide more context for your accessibility service.</li>
+to the {@link android.view.accessibility.AccessibilityEvent} passed to you by the system. This level
+of detail provides more context for the event that triggered your accessibility service.</li>
<li>{@link android.view.accessibility.AccessibilityEvent#getSource
AccessibilityEvent.getSource()} - This method returns an {@link
-android.view.accessibility.AccessibilityNodeInfo} object. This object allows you to request the
-parents and children of the component that originated the accessibility event and investigate their
-contents and state in order to provide
+android.view.accessibility.AccessibilityNodeInfo} object. This object allows you to request view
+layout hierarchy (parents and children) of the component that originated the accessibility event.
+This feature allows an accessibility service to investigate the full context of an event, including
+the content and state of any enclosing views or child views.
- <p class="caution"><strong>Important:</strong> The ability to investigate the full view
+<p class="caution"><strong>Important:</strong> The ability to investigate the view
hierarchy from an {@link android.view.accessibility.AccessibilityEvent} potentially exposes private
user information to your accessibility service. For this reason, your service must request this
level of access through the accessibility <a href="#service-config">service configuration XML</a>
file, by including the {@code canRetrieveWindowContent} attribute and setting it to {@code true}. If
you do not include this setting in your service configuration xml file, calls to {@link
android.view.accessibility.AccessibilityEvent#getSource getSource()} fail.</p>
+
+<p class="note"><strong>Note:</strong> In Android 4.1 (API Level 16) and higher, the
+{@link android.view.accessibility.AccessibilityEvent#getSource getSource()} method,
+as well as {@link android.view.accessibility.AccessibilityNodeInfo#getChild
+AccessibilityNodeInfo.getChild()} and
+{@link android.view.accessibility.AccessibilityNodeInfo#getParent getParent()}, return only
+view objects that are considered important for accessibility (views that draw content or respond to
+user actions). If your service requires all views, it can request them by setting the
+{@link android.accessibilityservice.AccessibilityServiceInfo#flags flags} member of the service's
+{@link android.accessibilityservice.AccessibilityServiceInfo} instance to
+{@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS}.</p>
</li>
</ul>
+<h2 id="act-for-users">Taking Action for Users</h2>
+
+<p>Starting with Android 4.0 (API Level 14), accessibility services can act on behalf
+ of users, including changing the input focus and selecting (activating) user interface elements.
+ In Android 4.1 (API Level 16) the range of actions has been expanded to include scrolling lists
+ and interacting with text fields. Accessibility services can
+ also take global actions, such as navigating to the Home screen, pressing the Back button, opening
+ the notifications screen and recent applications list. Android 4.1 also includes a new type of
+ focus, <em>Accessibilty Focus</em>, which makes all visible elements selectable by an
+ accessibility service.</p>
+
+<p>These new capabilities make it possible for developers of accessibility services to create
+ alternative navigation modes such as
+ <a href="{@docRoot}tools/testing/testing_accessibility.html#test-gestures">gesture navigation</a>,
+ and give users with disabilities improved control of their Android devices.</p>
+
+
+<h3 id="detect-gestures">Listening for gestures</h3>
+
+<p>Accessibility services can listen for specific gestures and respond by taking action on behalf
+ of a user. This feature, added in Android 4.1 (API Level 16), and requires that your
+ accessibility service request activation of the Explore by Touch feature. Your service can
+ request this activation by setting the
+ {@link android.accessibilityservice.AccessibilityServiceInfo#flags flags} member of the service’s
+ {@link android.accessibilityservice.AccessibilityServiceInfo} instance to
+ {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE},
+ as shown in the following example.
+ </p>
+
+<pre>
+public class MyAccessibilityService extends AccessibilityService {
+ @Override
+ public void onCreate() {
+ getServiceInfo().flags = AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
+ }
+ ...
+}
+</pre>
+
+<p>Once your service has requested activation of Explore by Touch, the user must allow the
+ feature to be turned on, if it is not already active. When this feature is active, your service
+ receives notification of accessibility gestures through your service's
+ {@link android.accessibilityservice.AccessibilityService#onGesture onGesture()} callback method
+ and can respond by taking actions for the user.</p>
+
+
+<h3 id="using-actions">Using accessibility actions</h3>
+
+<p>Accessibility services can take action on behalf of users to make interacting with applications
+ simpler and more productive. The ability of accessibility services to perform actions was added
+ in Android 4.0 (API Level 14) and significantly expanded with Android 4.1 (API Level 16).</p>
+
+<p>In order to take actions on behalf of users, your accessibility service must
+ <a href="#register">register</a> to receive events from a few or many applications and request
+ permission to view the content of applications by setting the
+ <a href="{@docRoot}reference/android/R.styleable.html#AccessibilityService_canRetrieveWindowContent">
+ {@code android:canRetrieveWindowContent}</a> to {@code true} in the
+ <a href="#service-config">service configuration file</a>. When events are received by your
+ service, it can then retrieve the
+ {@link android.view.accessibility.AccessibilityNodeInfo} object from the event using
+ {@link android.view.accessibility.AccessibilityEvent#getSource getSource()}.
+ With the {@link android.view.accessibility.AccessibilityNodeInfo} object, your service can then
+ explore the view hierarchy to determine what action to take and then act for the user using
+ {@link android.view.accessibility.AccessibilityNodeInfo#performAction performAction()}.</p>
+
+<pre>
+public class MyAccessibilityService extends AccessibilityService {
+
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ // get the source node of the event
+ AccessibilityNodeInfo nodeInfo = event.getSource();
+
+ // Use the event and node information to determine
+ // what action to take
+
+ // take action on behalf of the user
+ nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+
+ // recycle the nodeInfo object
+ nodeInfo.recycle();
+ }
+ ...
+}
+</pre>
+
+<p>The {@link android.view.accessibility.AccessibilityNodeInfo#performAction performAction()} method
+ allows your service to take action within an application. If your service needs to perform a
+ global action such as navigating to the Home screen, pressing the Back button, opening the
+ notifications screen or recent applications list, then use the
+ {@link android.accessibilityservice.AccessibilityService#performGlobalAction performGlobalAction()}
+ method.</p>
+
+
+<h3 id="focus-types">Using focus types</h3>
+
+<p>Android 4.1 (API Level 16) introduces a new type of user interface focus called <em>Accessibility
+ Focus</em>. This type of focus can be used by accessibility services to select any visible user
+ interface element and act on it. This focus type is different from the more well known <em>Input
+ Focus</em>, which determines what on-screen user interface element receives input when a user
+ types characters, presses <strong>Enter</strong> on a keyboard or pushes the center button of a
+ D-pad control.</p>
+
+<p>Accessibility Focus is completely separate and independent from Input Focus. In fact, it is
+ possible for one element in a user interface to have Input Focus while another element has
+ Accessibility Focus. The purpose of Accessibility Focus is to provide accessibility services with
+ a method of interacting with any visible element on a screen, regardless of whether or not the
+ element is input-focusable from a system perspective. You can see accessibility focus in action by
+ testing accessibility gestures. For more information about testing this feature, see
+ <a href="{@docRoot}tools/testing/testing_accessibility.html#test-gestures">Testing gesture
+ navigation</a>.</p>
+
+<p class="note">
+ <strong>Note:</strong> Accessibility services that use Accessibility Focus are responsible for
+ synchronizing the current Input Focus when an element is capable of this type of focus. Services
+ that do not synchronize Input Focus with Accessibility Focus run the risk of causing problems in
+ applications that expect input focus to be in a specific location when certain actions are taken.
+ </p>
+
+<p>An accessibility service can determine what user interface element has Input Focus or
+ Accessibility Focus using the {@link android.view.accessibility.AccessibilityNodeInfo#findFocus
+ AccessibilityNodeInfo.findFocus()} method. You can also search for elements that can be selected
+ with Input Focus using the
+ {@link android.view.accessibility.AccessibilityNodeInfo#focusSearch focusSearch()} method.
+ Finally, your accessibility service can set Accessibility Focus using the
+ {@link android.view.accessibility.AccessibilityNodeInfo#performAction
+ performAction(AccessibilityNodeInfo.ACTION_SET_ACCESSIBILITY_FOCUS)} method.</p>
+
+
<h2 id="examples">Example Code</h2>
<p>The API Demo project contains two samples which can be used as a starting point for generating
diff --git a/docs/html/tools/testing/testing_accessibility.jd b/docs/html/tools/testing/testing_accessibility.jd
new file mode 100644
index 0000000..daf9b36
--- /dev/null
+++ b/docs/html/tools/testing/testing_accessibility.jd
@@ -0,0 +1,257 @@
+page.title=Accessibility Testing Checklist
+parent.title=Testing
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+ <div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#goals">Testing Goals</a></li>
+ <li><a href="#requirements">Testing Requirements</a></li>
+ <li><a href="#recommendations">Testing Recommendations</a></li>
+ <li><a href="#special-cases">Special Cases and Considerations</a></li>
+ <li><a href="#how-to">Testing Accessibility Features</a>
+ <ol>
+ <li><a href="#test-audibles">Testing audible feedback</a></li>
+ <li><a href="#test-navigation">Testing focus navigation</a></li>
+ <li><a href="#test-gestures">Testing gesture navigation</a></li>
+ </ol>
+ </li>
+ </ol>
+
+ <h2>See Also</h2>
+ <ol>
+ <li>
+ <a href="{@docRoot}guide/topics/ui/accessibility/checklist.html">
+ Accessibility Developer Checklist</a>
+ </li>
+ <li>
+ <a href="{@docRoot}design/patterns/accessibility.html">
+ Android Design: Accessibility</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html">
+ Making Applications Accessible</a>
+ </li>
+ </ol>
+ </div>
+</div>
+<p>
+ Testing is an important part of making your application accessible to users with varying
+ abilities. Following <a href="{@docRoot}design/patterns/accessibility.html">design</a> and
+ <a href="{@docRoot}guide/topics/ui/accessibility/checklist.html">development</a> guidelines for
+ accessibility are important steps toward that goal, but testing for accessibility can uncover
+ problems with user interaction that are not obvious during design and development.</p>
+
+<p>This accessibility testing checklist guides you through the important aspects of
+ accessibility testing, including overall goals, required testing steps, recommended testing and
+ special considerations. This document also discusses how to enable accessibility features on
+ Android devices for testing purposes.</p>
+
+
+<h2 id="goals">Testing Goals</h2>
+
+<p>Your accessibility testing should have the following, high level goals:</p>
+
+<ul>
+ <li>Set up and use the application without sighted assistance</li>
+ <li>All task workflows in the application can be easily navigated using directional controls and
+ provide clear and appropriate feedback</li>
+</ul>
+
+
+<h2 id="requirements">Testing Requirements</h2>
+
+<p>The following tests must be completed in order to ensure a minimum level of application
+ accessibility.</p>
+
+<ol>
+ <li><strong>Directional controls:</strong> Verify that the application can be operated
+ without the use of a touch screen. Attempt to use only directional controls to accomplish the
+ primary tasks in the application. Use the keyboard and directional-pad (D-Pad) controls in the
+ Android <a href="{@docRoot}tools/devices/emulator.html">Emulator</a> or use
+ <a href="http://support.google.com/nexus/bin/answer.py?hl=en&answer=2700718">gesture
+ navigation</a> on devices with Android 4.1 (API Level 16) or higher.
+ <p class="note"><strong>Note:</strong> Keyboards and D-pads provide different navigation paths
+ than accessibility gestures. While gestures allow users to focus on nearly any on-screen
+ content, keyboard and D-pad navigation only allow focus on input fields and buttons.</p>
+ </li>
+ <li><strong>TalkBack audio prompts:</strong> Verify that user interface controls that provide
+ information (graphics or text) or allow user action have clear and accurate audio descriptions
+ when <a href="#testing-talkback">TalkBack is enabled</a> and controls are focused. Use
+ directional controls to move focus between application layout elements.</li>
+ <li><strong>Explore by Touch prompts:</strong> Verify that user interface controls that
+ provide information (graphics or text) or allow user action have appropriate audio descriptions
+ when <a href="#testing-ebt">Explore by Touch is enabled</a>. There should be no
+ regions where contents or controls do not provide an audio description.</li>
+ <li><strong>Touchable control sizes:</strong> All controls where a user can select or take an
+ action must be a minimum of 48 dp (approximately 9mm) in length and width, as recommended by
+ <a href="{@docRoot}design/patterns/accessibility.html">Android Design</a>.</li>
+ <li><strong>Gestures work with TalkBack enabled:</strong> Verify that app-specific gestures,
+ such as zooming images, scrolling lists, swiping between pages or navigating carousel controls
+ continue to work when <a href="#testing-talkback">TalkBack is enabled</a>. If these gestures do
+ not function, then an alternative interface for these actions must be provided.</li>
+ <li><strong>No audio-only feedback:</strong> Audio feedback must always have a secondary
+ feedback mechanism to support users who are deaf or hard of hearing, for example: A sound alert
+ for the arrival of a message should also be accompanied by a system
+ {@link android.app.Notification}, haptic feedback (if available) or another visual alert.</li>
+</ol>
+
+
+<h2 id="recommendations">Testing Recommendations</h2>
+
+<p>The following tests are recommended for ensuring the accessibility of your application. If you
+ do not test these items, it may impact the overall accessibility and quality of your
+ application.</p>
+
+<ol>
+ <li><strong>Repetitive audio prompting:</strong> Check that closely related controls (such as
+ items with multiple components in a list) do not simply repeat the same audio prompt. For
+ example, in a contacts list that contains a contact picture, written name and title, the prompts
+ should not simply repeat “Bob Smith” for each item.</li>
+ <li><strong>Audio prompt overloading or underloading:</strong> Check that closely related
+ controls provide an appropriate level of audio information that enables users to understand and
+ act on a screen element. Too little or too much prompting can make it difficult to understand
+ and use a control.</li>
+</ol>
+
+
+<h2 id="special-cases">Special Cases and Considerations</h2>
+
+<p>The following list describes specific situations that should be tested to ensure an
+ accessible app. Some, none or all of the cases described here may apply to your application. Be
+ sure to review this list to find out if these special cases apply and take appropriate action.</p>
+
+<ol>
+ <li><strong>Review developer special cases and considerations:</strong> Review the list of
+ <a href="{@docRoot}guide/topics/ui/accessibility/checklist.html#special-cases">special cases</a>
+ for accessibility development and test your application for the cases that apply.</li>
+ <li><strong>Prompts for controls that change function:</strong> Buttons or other controls
+ that change function due to application context or workflow must provide audio prompts
+ appropriate to their current function. For example, a button that changes function from play
+ video to pause video should provide an audio prompt which is appropriate to its current state.</li>
+ <li><strong>Video playback and captioning:</strong> If the application provides video
+ playback, verify that it supports captioning and subtitles to assist users who are deaf or hard
+ of hearing. The video playback controls must clearly indicate if captioning is available for a
+ video and provide a clear way of enabling captions.</li>
+</ol>
+
+
+<h2 id="how-to">Testing Accessibility Features</h2>
+
+<p>Testing of accessibility features such as TalkBack, Explore by Touch and accessibility Gestures
+requires setup of your testing device. This section describes how to enable these features for
+accessibility testing.</p>
+
+
+<h3 id="test-audibles">Testing audible feedback</h3>
+
+<p>Audible accessibility feedback features on Android devices provide audio prompts that speaks
+ the screen content as you move around an application. By enabling these features on an Android
+ device, you can test the experience of users with blindness or low-vision using your application.
+</p>
+
+<p>Audible feedback for users on Android is typically provided by TalkBack accessibility service and
+the Explore by Touch system feature. The TalkBack accessibility service comes preinstalled on most
+Android devices and can also be downloaded for free from
+<a href="https://play.google.com/store/apps/details?id=com.google.android.marvin.talkback">Google
+Play</a>. The Explore by Touch system feature is available on devices running Android 4.0 and later.
+</p>
+
+<h4 id="testing-talkback">Testing with TalkBack</h4>
+
+<p>The <em>TalkBack</em> accessibility service works by speaking the contents of user interface
+controls as the user moves focus onto controls. This service should be enabled as part of testing
+focus navigation and audible prompts.</p>
+
+<p>To enable the TalkBack accessibility service:</p>
+<ol>
+ <li>Launch the <strong>Settings</strong> application.</li>
+ <li>Navigate to the <strong>Accessibility</strong> category and select it.</li>
+ <li>Select <strong>Accessibility</strong> to enable it.</li>
+ <li>Select <strong>TalkBack</strong> to enable it.</li>
+</ol>
+
+<p class="note">
+ <strong>Note:</strong> While TalkBack is the most available Android accessibility service for
+ users with disabilities, other accessibility services are available and may be installed by users.
+</p>
+
+<p>For more information about using TalkBack, see
+<a href="http://support.google.com/nexus/bin/answer.py?hl=en&answer=2700928">Use TalkBack</a>.</p>
+
+<h4 id="testing-ebt">Testing with Explore by Touch</h4>
+
+<p>The <em>Explore by Touch</em> system feature is available on devices running Android 4.0 and
+ later, and works by enabling a special accessibility mode that allows users to drag a finger
+ around the interface of an application and hear the contents of the screen spoken. This feature
+ does not require screen elements to be focused using an directional controller, but listens for
+ hover events over user interface controls.
+</p>
+
+<p>To enable Explore by Touch on Android 4.0 and later:</p>
+<ol>
+ <li>Launch the <strong>Settings</strong> application.</li>
+ <li>Navigate to the <strong>Accessibility</strong> category and select it.</li>
+ <li>Select the <strong>TalkBack</strong> to enable it.
+ <p class="note"><strong>Note:</strong> On Android 4.1 (API Level 16) and higher, the system
+ provides a popup message to enable Explore by Touch. On older versions, you must follow the
+ step below.</p>
+ </li>
+ <li>Return to the <strong>Accessibility</strong> category and select <strong>Explore by
+Touch</strong> to enable it.
+ <p class="note"><strong>Note:</strong> You must turn on TalkBack <em>first</em>, otherwise this
+option is not available.</p>
+ </li>
+</ol>
+
+<p>For more information about using the Explore by Touch features, see
+<a href="http://support.google.com/nexus/bin/answer.py?hl=en&answer=2700722">Use Explore by
+Touch</a>.</p>
+
+<h3 id="test-navigation">Testing focus navigation</h3>
+
+<p>Focus navigation is the use of directional controls to navigate between the individual user
+ interface elements of an application in order to operate it. Users with limited vision or limited
+ manual dexterity often use this mode of navigation instead of touch navigation. As part of
+ accessibility testing, you should verify that your application can be operated using only
+ directional controls.</p>
+
+<p>You can test navigation of your application using only focus controls, even if your test devices
+ does not have a directional controller. The <a href="{@docRoot}tools/help/emulator.html">Android
+ Emulator</a> provides a simulated directional controller that you can use to test navigation. You
+ can also use a software-based directional controller, such as the one provided by the
+ <a href="https://play.google.com/store/apps/details?id=com.googlecode.eyesfree.inputmethod.latin"
+ >Eyes-Free Keyboard</a> to simulate use of a D-pad on a test device that does not have a physical
+ D-pad.</p>
+
+
+<h3 id="test-gestures">Testing gesture navigation</h3>
+
+<p>Gesture navigation is an accessibility navigation mode that allows users to navigate Android
+ devices and applications using specific
+ <a href="http://support.google.com/nexus/bin/answer.py?hl=en&answer=2700718">gestures</a>. This
+ navigation mode is available on Android 4.1 (API Level 16) and higher.</p>
+
+<p class="note"><strong>Note:</strong> Accessibility gestures provide a different navigation path
+than keyboards and D-pads. While gestures allow users to focus on nearly any on-screen
+content, keyboard and D-pad navigation only allow focus on input fields and buttons.</p>
+
+<p>To enable gesture navigation on Android 4.1 and later:</p>
+<ul>
+ <li>Enable both TalkBack and the Explore by Touch feature as described in the
+ <a href="#testing-ebt">Testing with Explore by Touch</a>. When <em>both</em> of these
+ features are enabled, accessibility gestures are automatically enabled.</li>
+ <li>You can change gesture settings using <strong>Settings > Accessibility > TalkBack >
+ Settings > Manage shortcut gestures</strong>.
+</ul>
+
+<p>For more information about using Explore by Touch accessibility gestures, see
+<a href="http://support.google.com/android/bin/topic.py?hl=en&topic=2492346">Accessibility
+gestures</a>.</p>
+
+<p class="note">
+ <strong>Note:</strong> Accessibility services other than TalkBack may map accessibility gestures
+ to different user actions. If gestures are not producing the expected actions during testing, try
+ disabling other accessibility services before proceeding.</p>
\ No newline at end of file
diff --git a/docs/html/tools/tools_toc.cs b/docs/html/tools/tools_toc.cs
index 850e0ec..f3936b8 100644
--- a/docs/html/tools/tools_toc.cs
+++ b/docs/html/tools/tools_toc.cs
@@ -5,7 +5,7 @@
<a href="<?cs var:toroot ?>tools/index.html"><span class="en">Developer Tools</span></a>
</div>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot
?>sdk/index.html"><span class="en">Download</span></a></div>
@@ -29,7 +29,7 @@
</li>
</ul>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header">
<a href="/tools/workflow/index.html"><span class="en">Workflow</span></a>
@@ -39,8 +39,8 @@
<div class="nav-section-header"><a href="/tools/devices/index.html"><span class="en">Setting Up Virtual Devices</span></a></div>
<ul>
<li><a href="/tools/devices/managing-avds.html"><span class="en">With AVD Manager</span></a></li>
- <li><a href="/tools/devices/managing-avds-cmdline.html"><span class="en">From the Command Line</span></a></li>
- <li><a href="/tools/devices/emulator.html"><span class="en">Using the Android Emulator</span></a></li>
+ <li><a href="/tools/devices/managing-avds-cmdline.html"><span class="en">From the Command Line</span></a></li>
+ <li><a href="/tools/devices/emulator.html"><span class="en">Using the Android Emulator</span></a></li>
</ul>
</li>
<li><a href="/tools/device.html"><span class="en">Using Hardware Devices</span></a></li>
@@ -48,16 +48,16 @@
<div class="nav-section-header"><a href="/tools/projects/index.html"><span class="en">Setting Up Projects</span></a></div>
<ul>
<li><a href="/tools/projects/projects-eclipse.html"><span class="en">From Eclipse with ADT</span></a></li>
- <li><a href="/tools/projects/projects-cmdline.html"><span class="en">From the Command Line</span></a></li>
+ <li><a href="/tools/projects/projects-cmdline.html"><span class="en">From the Command Line</span></a></li>
</ul>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="/tools/building/index.html"><span class="en">Building and Running</span></a></div>
<ul>
<li><a href="/tools/building/building-eclipse.html"><span class="en">From Eclipse with ADT</span></a></li>
- <li><a href="/tools/building/building-cmdline.html"><span class="en">From the Command Line</span></a></li>
+ <li><a href="/tools/building/building-cmdline.html"><span class="en">From the Command Line</span></a></li>
</ul>
</li>
@@ -76,7 +76,7 @@
</li>
<li><a href="<?cs var:toroot ?>tools/testing/testing_otheride.html">
<span class="en">From Other IDEs</span></a>
- </li>
+ </li>
<li>
<a href="<?cs var:toroot?>tools/testing/activity_testing.html">
<span class="en">Activity Testing</span></a>
@@ -90,6 +90,10 @@
<span class="en">Content Provider Testing</span></a>
</li>
<li>
+ <a href="<?cs var:toroot?>tools/testing/testing_accessibility.html">
+ <span class="en">Accessibility Testing</span></a>
+ </li>
+ <li>
<a href="<?cs var:toroot ?>tools/testing/what_to_test.html">
<span class="en">What To Test</span></a>
</li>
@@ -160,7 +164,7 @@
<li><a href="<?cs var:toroot ?>tools/help/zipalign.html">zipalign</a></li>
</ul>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot
?>tools/revisions/index.html"><span class="en">Revisions</span></a></div>
@@ -178,8 +182,8 @@
class="en">Platforms</span></a></li>
</ul>
</li>
-
-
+
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot
?>tools/extras/index.html"><span class="en">Extras</span></a></div>
@@ -192,13 +196,13 @@
</ul>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header empty"><a href="<?cs var:toroot
?>tools/samples/index.html"><span class="en">Samples</span></a></div>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header">
<a href="<?cs var:toroot ?>tools/adk/index.html">
@@ -217,7 +221,7 @@
</li>
</ul>
</li>
-
+
</ul><!-- nav -->
<script type="text/javascript">
diff --git a/docs/html/training/basics/activity-lifecycle/recreating.jd b/docs/html/training/basics/activity-lifecycle/recreating.jd
index 3bbf0bb..8c7126a 100644
--- a/docs/html/training/basics/activity-lifecycle/recreating.jd
+++ b/docs/html/training/basics/activity-lifecycle/recreating.jd
@@ -51,7 +51,7 @@
the foreground activity because the screen configuration has changed and your activity might need to
load alternative resources (such as the layout).</p>
-<p>By default, the system uses the {@link android.os.Bundle} instance state to saves information
+<p>By default, the system uses the {@link android.os.Bundle} instance state to save information
about each {@link android.view.View} object in your activity layout (such as the text value entered
into an {@link android.widget.EditText} object). So, if your activity instance is destroyed and
recreated, the state of the layout is automatically restored to its previous state. However, your
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 23bca3e..48f5bf3 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -957,6 +957,13 @@
SCREENLONG_ANY = ACONFIGURATION_SCREENLONG_ANY << SHIFT_SCREENLONG,
SCREENLONG_NO = ACONFIGURATION_SCREENLONG_NO << SHIFT_SCREENLONG,
SCREENLONG_YES = ACONFIGURATION_SCREENLONG_YES << SHIFT_SCREENLONG,
+
+ // screenLayout bits for layout direction.
+ MASK_LAYOUTDIR = 0xC0,
+ SHIFT_LAYOUTDIR = 6,
+ LAYOUTDIR_ANY = ACONFIGURATION_LAYOUTDIR_ANY << SHIFT_LAYOUTDIR,
+ LAYOUTDIR_LTR = ACONFIGURATION_LAYOUTDIR_LTR << SHIFT_LAYOUTDIR,
+ LAYOUTDIR_RTL = ACONFIGURATION_LAYOUTDIR_RTL << SHIFT_LAYOUTDIR,
};
enum {
@@ -1020,7 +1027,8 @@
CONFIG_SMALLEST_SCREEN_SIZE = ACONFIGURATION_SMALLEST_SCREEN_SIZE,
CONFIG_VERSION = ACONFIGURATION_VERSION,
CONFIG_SCREEN_LAYOUT = ACONFIGURATION_SCREEN_LAYOUT,
- CONFIG_UI_MODE = ACONFIGURATION_UI_MODE
+ CONFIG_UI_MODE = ACONFIGURATION_UI_MODE,
+ CONFIG_LAYOUTDIR = ACONFIGURATION_LAYOUTDIR,
};
// Compare two configuration, returning CONFIG_* flags set for each value
@@ -1061,7 +1069,7 @@
* There should be one of these chunks for each resource type.
*
* This structure is followed by an array of integers providing the set of
- * configuation change flags (ResTable_config::CONFIG_*) that have multiple
+ * configuration change flags (ResTable_config::CONFIG_*) that have multiple
* resources for that configuration. In addition, the high bit is set if that
* resource has been made public.
*/
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 8cce191..069dfa3 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1470,6 +1470,9 @@
if (country[1] != o.country[1]) {
return country[1] < o.country[1] ? -1 : 1;
}
+ if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) {
+ return (screenLayout & MASK_LAYOUTDIR) < (o.screenLayout & MASK_LAYOUTDIR) ? -1 : 1;
+ }
if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
return smallestScreenWidthDp < o.smallestScreenWidthDp ? -1 : 1;
}
@@ -1558,6 +1561,13 @@
}
}
+ if (screenLayout || o.screenLayout) {
+ if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0) {
+ if (!(screenLayout & MASK_LAYOUTDIR)) return false;
+ if (!(o.screenLayout & MASK_LAYOUTDIR)) return true;
+ }
+ }
+
if (smallestScreenWidthDp || o.smallestScreenWidthDp) {
if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
if (!smallestScreenWidthDp) return false;
@@ -1683,6 +1693,15 @@
}
}
+ if (screenLayout || o.screenLayout) {
+ if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0
+ && (requested->screenLayout & MASK_LAYOUTDIR)) {
+ int myLayoutDir = screenLayout & MASK_LAYOUTDIR;
+ int oLayoutDir = o.screenLayout & MASK_LAYOUTDIR;
+ return (myLayoutDir > oLayoutDir);
+ }
+ }
+
if (smallestScreenWidthDp || o.smallestScreenWidthDp) {
// The configuration closest to the actual size is best.
// We assume that larger configs have already been filtered
@@ -1906,6 +1925,12 @@
}
}
if (screenConfig != 0) {
+ const int layoutDir = screenLayout&MASK_LAYOUTDIR;
+ const int setLayoutDir = settings.screenLayout&MASK_LAYOUTDIR;
+ if (layoutDir != 0 && layoutDir != setLayoutDir) {
+ return false;
+ }
+
const int screenSize = screenLayout&MASK_SCREENSIZE;
const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE;
// Any screen sizes for larger screens than the setting do not
@@ -2032,6 +2057,21 @@
if (res.size() > 0) res.append("-");
res.append(country, 2);
}
+ if ((screenLayout&MASK_LAYOUTDIR) != 0) {
+ if (res.size() > 0) res.append("-");
+ switch (screenLayout&ResTable_config::MASK_LAYOUTDIR) {
+ case ResTable_config::LAYOUTDIR_LTR:
+ res.append("ltr");
+ break;
+ case ResTable_config::LAYOUTDIR_RTL:
+ res.append("rtl");
+ break;
+ default:
+ res.appendFormat("layoutDir=%d",
+ dtohs(screenLayout&ResTable_config::MASK_LAYOUTDIR));
+ break;
+ }
+ }
if (smallestScreenWidthDp != 0) {
if (res.size() > 0) res.append("-");
res.appendFormat("sw%ddp", dtohs(smallestScreenWidthDp));
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index c3a07a1..c0f79df 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -6,6 +6,8 @@
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SRC_FILES:= \
utils/SortedListImpl.cpp \
+ font/CacheTexture.cpp \
+ font/Font.cpp \
FontRenderer.cpp \
GammaFontRenderer.cpp \
Caches.cpp \
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 95ccdfc..86667ee 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -25,585 +25,12 @@
#include "Caches.h"
#include "Debug.h"
#include "FontRenderer.h"
-#include "Caches.h"
+#include "Rect.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#define DEFAULT_TEXT_SMALL_CACHE_WIDTH 1024
-#define DEFAULT_TEXT_SMALL_CACHE_HEIGHT 256
-#define DEFAULT_TEXT_LARGE_CACHE_WIDTH 2048
-#define DEFAULT_TEXT_LARGE_CACHE_HEIGHT 512
-#define CACHE_BLOCK_ROUNDING_SIZE 4
-
-#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
-
-///////////////////////////////////////////////////////////////////////////////
-// CacheBlock
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
- * order, except for the final block (the remainder space at the right, since we fill from the
- * left).
- */
-CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock *newBlock) {
-#if DEBUG_FONT_RENDERER
- ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
- newBlock, newBlock->mX, newBlock->mY,
- newBlock->mWidth, newBlock->mHeight);
-#endif
- CacheBlock *currBlock = head;
- CacheBlock *prevBlock = NULL;
- while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
- if (newBlock->mWidth < currBlock->mWidth) {
- newBlock->mNext = currBlock;
- newBlock->mPrev = prevBlock;
- currBlock->mPrev = newBlock;
- if (prevBlock) {
- prevBlock->mNext = newBlock;
- return head;
- } else {
- return newBlock;
- }
- }
- prevBlock = currBlock;
- currBlock = currBlock->mNext;
- }
- // new block larger than all others - insert at end (but before the remainder space, if there)
- newBlock->mNext = currBlock;
- newBlock->mPrev = prevBlock;
- if (currBlock) {
- currBlock->mPrev = newBlock;
- }
- if (prevBlock) {
- prevBlock->mNext = newBlock;
- return head;
- } else {
- return newBlock;
- }
-}
-
-CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock *blockToRemove) {
-#if DEBUG_FONT_RENDERER
- ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
- blockToRemove, blockToRemove->mX, blockToRemove->mY,
- blockToRemove->mWidth, blockToRemove->mHeight);
-#endif
- CacheBlock* newHead = head;
- CacheBlock* nextBlock = blockToRemove->mNext;
- CacheBlock* prevBlock = blockToRemove->mPrev;
- if (prevBlock) {
- prevBlock->mNext = nextBlock;
- } else {
- newHead = nextBlock;
- }
- if (nextBlock) {
- nextBlock->mPrev = prevBlock;
- }
- delete blockToRemove;
- return newHead;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// CacheTexture
-///////////////////////////////////////////////////////////////////////////////
-
-bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
- if (glyph.fHeight + TEXTURE_BORDER_SIZE > mHeight) {
- return false;
- }
-
- uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
- uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
- // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
- // This columns for glyphs that are close but not necessarily exactly the same size. It trades
- // off the loss of a few pixels for some glyphs against the ability to store more glyphs
- // of varying sizes in one block.
- uint16_t roundedUpW =
- (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
- CacheBlock *cacheBlock = mCacheBlocks;
- while (cacheBlock) {
- // Store glyph in this block iff: it fits the block's remaining space and:
- // it's the remainder space (mY == 0) or there's only enough height for this one glyph
- // or it's within ROUNDING_SIZE of the block width
- if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
- (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
- (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
- if (cacheBlock->mHeight - glyphH < glyphH) {
- // Only enough space for this glyph - don't bother rounding up the width
- roundedUpW = glyphW;
- }
- *retOriginX = cacheBlock->mX;
- *retOriginY = cacheBlock->mY;
- // If this is the remainder space, create a new cache block for this column. Otherwise,
- // adjust the info about this column.
- if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
- uint16_t oldX = cacheBlock->mX;
- // Adjust remainder space dimensions
- cacheBlock->mWidth -= roundedUpW;
- cacheBlock->mX += roundedUpW;
- if (mHeight - glyphH >= glyphH) {
- // There's enough height left over to create a new CacheBlock
- CacheBlock *newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE,
- roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE);
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
- newBlock, newBlock->mX, newBlock->mY,
- newBlock->mWidth, newBlock->mHeight);
-#endif
- mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
- }
- } else {
- // Insert into current column and adjust column dimensions
- cacheBlock->mY += glyphH;
- cacheBlock->mHeight -= glyphH;
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
- cacheBlock, cacheBlock->mX, cacheBlock->mY,
- cacheBlock->mWidth, cacheBlock->mHeight);
-#endif
- }
- if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
- // If remaining space in this block is too small to be useful, remove it
- mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
- }
- mDirty = true;
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: current block list:");
- mCacheBlocks->output();
-#endif
- ++mNumGlyphs;
- return true;
- }
- cacheBlock = cacheBlock->mNext;
- }
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
-#endif
- return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Font
-///////////////////////////////////////////////////////////////////////////////
-
-Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle, uint32_t scaleX,
- SkPaint::Style style, uint32_t strokeWidth) :
- mState(state), mFontId(fontId), mFontSize(fontSize),
- mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
- mStyle(style), mStrokeWidth(mStrokeWidth) {
-}
-
-
-Font::~Font() {
- for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
- if (mState->mActiveFonts[ct] == this) {
- mState->mActiveFonts.removeAt(ct);
- break;
- }
- }
-
- for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
- delete mCachedGlyphs.valueAt(i);
- }
-}
-
-void Font::invalidateTextureCache(CacheTexture *cacheTexture) {
- for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
- CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
- if (cacheTexture == NULL || cachedGlyph->mCacheTexture == cacheTexture) {
- cachedGlyph->mIsValid = false;
- }
- }
-}
-
-void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
- uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
- int nPenX = x + glyph->mBitmapLeft;
- int nPenY = y + glyph->mBitmapTop;
-
- int width = (int) glyph->mBitmapWidth;
- int height = (int) glyph->mBitmapHeight;
-
- if (bounds->bottom > nPenY) {
- bounds->bottom = nPenY;
- }
- if (bounds->left > nPenX) {
- bounds->left = nPenX;
- }
- if (bounds->right < nPenX + width) {
- bounds->right = nPenX + width;
- }
- if (bounds->top < nPenY + height) {
- bounds->top = nPenY + height;
- }
-}
-
-void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
- uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
- int nPenX = x + glyph->mBitmapLeft;
- int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
-
- float u1 = glyph->mBitmapMinU;
- float u2 = glyph->mBitmapMaxU;
- float v1 = glyph->mBitmapMinV;
- float v2 = glyph->mBitmapMaxV;
-
- int width = (int) glyph->mBitmapWidth;
- int height = (int) glyph->mBitmapHeight;
-
- mState->appendMeshQuad(nPenX, nPenY, u1, v2,
- nPenX + width, nPenY, u2, v2,
- nPenX + width, nPenY - height, u2, v1,
- nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
-}
-
-void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
- uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
- int nPenX = x + glyph->mBitmapLeft;
- int nPenY = y + glyph->mBitmapTop;
-
- uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
- uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
-
- CacheTexture *cacheTexture = glyph->mCacheTexture;
- uint32_t cacheWidth = cacheTexture->mWidth;
- const uint8_t* cacheBuffer = cacheTexture->mTexture;
-
- uint32_t cacheX = 0, cacheY = 0;
- int32_t bX = 0, bY = 0;
- for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
- for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
-#if DEBUG_FONT_RENDERER
- if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
- ALOGE("Skipping invalid index");
- continue;
- }
-#endif
- uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
- bitmap[bY * bitmapW + bX] = tempCol;
- }
- }
-}
-
-void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
- SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
- const float halfWidth = glyph->mBitmapWidth * 0.5f;
- const float height = glyph->mBitmapHeight;
-
- vOffset += glyph->mBitmapTop + height;
-
- SkPoint destination[4];
- measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
-
- // Move along the tangent and offset by the normal
- destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
- -tangent->fY * halfWidth + tangent->fX * vOffset);
- destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
- tangent->fY * halfWidth + tangent->fX * vOffset);
- destination[2].set(destination[1].fX + tangent->fY * height,
- destination[1].fY - tangent->fX * height);
- destination[3].set(destination[0].fX + tangent->fY * height,
- destination[0].fY - tangent->fX * height);
-
- const float u1 = glyph->mBitmapMinU;
- const float u2 = glyph->mBitmapMaxU;
- const float v1 = glyph->mBitmapMinV;
- const float v2 = glyph->mBitmapMaxV;
-
- mState->appendRotatedMeshQuad(
- position->fX + destination[0].fX,
- position->fY + destination[0].fY, u1, v2,
- position->fX + destination[1].fX,
- position->fY + destination[1].fY, u2, v2,
- position->fX + destination[2].fX,
- position->fY + destination[2].fY, u2, v1,
- position->fX + destination[3].fX,
- position->fY + destination[3].fY, u1, v1,
- glyph->mCacheTexture);
-}
-
-CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) {
- CachedGlyphInfo* cachedGlyph = NULL;
- ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
- if (index >= 0) {
- cachedGlyph = mCachedGlyphs.valueAt(index);
- } else {
- cachedGlyph = cacheGlyph(paint, textUnit, precaching);
- }
-
- // Is the glyph still in texture cache?
- if (!cachedGlyph->mIsValid) {
- const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
- updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
- }
-
- return cachedGlyph;
-}
-
-void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
- if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
- render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
- bitmapW, bitmapH, NULL, NULL);
- } else {
- render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
- 0, 0, NULL, NULL);
- }
-}
-
-void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, const float* positions) {
- render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
- 0, 0, NULL, positions);
-}
-
-void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, SkPath* path, float hOffset, float vOffset) {
- if (numGlyphs == 0 || text == NULL || len == 0) {
- return;
- }
-
- text += start;
-
- int glyphsCount = 0;
- SkFixed prevRsbDelta = 0;
-
- float penX = 0.0f;
-
- SkPoint position;
- SkVector tangent;
-
- SkPathMeasure measure(*path, false);
- float pathLength = SkScalarToFloat(measure.getLength());
-
- if (paint->getTextAlign() != SkPaint::kLeft_Align) {
- float textWidth = SkScalarToFloat(paint->measureText(text, len));
- float pathOffset = pathLength;
- if (paint->getTextAlign() == SkPaint::kCenter_Align) {
- textWidth *= 0.5f;
- pathOffset *= 0.5f;
- }
- penX += pathOffset - textWidth;
- }
-
- while (glyphsCount < numGlyphs && penX < pathLength) {
- glyph_t glyph = GET_GLYPH(text);
-
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
- penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
- prevRsbDelta = cachedGlyph->mRsbDelta;
-
- if (cachedGlyph->mIsValid) {
- drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
- }
-
- penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
-
- glyphsCount++;
- }
-}
-
-void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, Rect *bounds, const float* positions) {
- if (bounds == NULL) {
- ALOGE("No return rectangle provided to measure text");
- return;
- }
- bounds->set(1e6, -1e6, -1e6, 1e6);
- render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
-}
-
-void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
-
- if (numGlyphs == 0 || text == NULL) {
- return;
- }
- int glyphsCount = 0;
-
- while (glyphsCount < numGlyphs) {
- glyph_t glyph = GET_GLYPH(text);
-
- // Reached the end of the string
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph, true);
-
- glyphsCount++;
- }
-}
-
-void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
- uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
- if (numGlyphs == 0 || text == NULL || len == 0) {
- return;
- }
-
- static RenderGlyph gRenderGlyph[] = {
- &android::uirenderer::Font::drawCachedGlyph,
- &android::uirenderer::Font::drawCachedGlyphBitmap,
- &android::uirenderer::Font::measureCachedGlyph
- };
- RenderGlyph render = gRenderGlyph[mode];
-
- text += start;
- int glyphsCount = 0;
-
- if (CC_LIKELY(positions == NULL)) {
- SkFixed prevRsbDelta = 0;
-
- float penX = x + 0.5f;
- int penY = y;
-
- while (glyphsCount < numGlyphs) {
- glyph_t glyph = GET_GLYPH(text);
-
- // Reached the end of the string
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
- penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
- prevRsbDelta = cachedGlyph->mRsbDelta;
-
- // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
- if (cachedGlyph->mIsValid) {
- (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
- bitmap, bitmapW, bitmapH, bounds, positions);
- }
-
- penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
-
- glyphsCount++;
- }
- } else {
- const SkPaint::Align align = paint->getTextAlign();
-
- // This is for renderPosText()
- while (glyphsCount < numGlyphs) {
- glyph_t glyph = GET_GLYPH(text);
-
- // Reached the end of the string
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
-
- // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
- if (cachedGlyph->mIsValid) {
- int penX = x + positions[(glyphsCount << 1)];
- int penY = y + positions[(glyphsCount << 1) + 1];
-
- switch (align) {
- case SkPaint::kRight_Align:
- penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
- penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
- break;
- case SkPaint::kCenter_Align:
- penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
- penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
- default:
- break;
- }
-
- (*this.*render)(cachedGlyph, penX, penY,
- bitmap, bitmapW, bitmapH, bounds, positions);
- }
-
- glyphsCount++;
- }
- }
-}
-
-void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
- bool precaching) {
- glyph->mAdvanceX = skiaGlyph.fAdvanceX;
- glyph->mAdvanceY = skiaGlyph.fAdvanceY;
- glyph->mBitmapLeft = skiaGlyph.fLeft;
- glyph->mBitmapTop = skiaGlyph.fTop;
- glyph->mLsbDelta = skiaGlyph.fLsbDelta;
- glyph->mRsbDelta = skiaGlyph.fRsbDelta;
-
- uint32_t startX = 0;
- uint32_t startY = 0;
-
- // Get the bitmap for the glyph
- paint->findImage(skiaGlyph);
- mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
-
- if (!glyph->mIsValid) {
- return;
- }
-
- uint32_t endX = startX + skiaGlyph.fWidth;
- uint32_t endY = startY + skiaGlyph.fHeight;
-
- glyph->mStartX = startX;
- glyph->mStartY = startY;
- glyph->mBitmapWidth = skiaGlyph.fWidth;
- glyph->mBitmapHeight = skiaGlyph.fHeight;
-
- uint32_t cacheWidth = glyph->mCacheTexture->mWidth;
- uint32_t cacheHeight = glyph->mCacheTexture->mHeight;
-
- glyph->mBitmapMinU = startX / (float) cacheWidth;
- glyph->mBitmapMinV = startY / (float) cacheHeight;
- glyph->mBitmapMaxU = endX / (float) cacheWidth;
- glyph->mBitmapMaxV = endY / (float) cacheHeight;
-
- mState->mUploadTexture = true;
-}
-
-CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
- CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
- mCachedGlyphs.add(glyph, newGlyph);
-
- const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
- newGlyph->mGlyphIndex = skiaGlyph.fID;
- newGlyph->mIsValid = false;
-
- updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
-
- return newGlyph;
-}
-
-Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle, uint32_t scaleX,
- SkPaint::Style style, uint32_t strokeWidth) {
- Vector<Font*> &activeFonts = state->mActiveFonts;
-
- for (uint32_t i = 0; i < activeFonts.size(); i++) {
- Font* font = activeFonts[i];
- if (font->mFontId == fontId && font->mFontSize == fontSize &&
- font->mFlags == flags && font->mItalicStyle == italicStyle &&
- font->mScaleX == scaleX && font->mStyle == style &&
- (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
- return font;
- }
- }
-
- Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
- scaleX, style, strokeWidth);
- activeFonts.push(newFont);
- return newFont;
-}
-
-///////////////////////////////////////////////////////////////////////////////
// FontRenderer
///////////////////////////////////////////////////////////////////////////////
@@ -619,7 +46,7 @@
mMaxNumberOfQuads = 1024;
mCurrentQuadIndex = 0;
- mTextMeshPtr = NULL;
+ mTextMesh = NULL;
mCurrentCacheTexture = NULL;
mLastCacheTexture = NULL;
@@ -636,20 +63,25 @@
if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
mSmallCacheWidth = atoi(property);
}
+
if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
mSmallCacheHeight = atoi(property);
}
+
if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
mLargeCacheWidth = atoi(property);
}
+
if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
mLargeCacheHeight = atoi(property);
}
- GLint maxTextureSize = Caches::getInstance().maxTextureSize;
- mSmallCacheWidth = (mSmallCacheWidth > maxTextureSize) ? maxTextureSize : mSmallCacheWidth;
- mSmallCacheHeight = (mSmallCacheHeight > maxTextureSize) ? maxTextureSize : mSmallCacheHeight;
- mLargeCacheWidth = (mLargeCacheWidth > maxTextureSize) ? maxTextureSize : mLargeCacheWidth;
- mLargeCacheHeight = (mLargeCacheHeight > maxTextureSize) ? maxTextureSize : mLargeCacheHeight;
+
+ uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
+ mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
+ mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
+ mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
+ mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
+
if (sLogFontRendererCreate) {
INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
mSmallCacheWidth, mSmallCacheHeight,
@@ -672,7 +104,7 @@
Caches::getInstance().unbindIndicesBuffer();
glDeleteBuffers(1, &mIndexBufferID);
- delete[] mTextMeshPtr;
+ delete[] mTextMesh;
}
Vector<Font*> fontsToDereference = mActiveFonts;
@@ -695,68 +127,34 @@
mCacheTextures[i]->init();
}
- #if DEBUG_FONT_RENDERER
+#if DEBUG_FONT_RENDERER
uint16_t totalGlyphs = 0;
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
- totalGlyphs += mCacheTextures[i]->mNumGlyphs;
+ totalGlyphs += mCacheTextures[i]->getGlyphCount();
// Erase caches, just as a debugging facility
- if (mCacheTextures[i]->mTexture) {
- memset(mCacheTextures[i]->mTexture, 0,
- mCacheTextures[i]->mWidth * mCacheTextures[i]->mHeight);
+ if (mCacheTextures[i]->getTexture()) {
+ memset(mCacheTextures[i]->getTexture(), 0,
+ mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
}
}
ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
#endif
}
-void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
- if (cacheTexture && cacheTexture->mTexture) {
- glDeleteTextures(1, &cacheTexture->mTextureId);
- delete[] cacheTexture->mTexture;
- cacheTexture->mTexture = NULL;
- cacheTexture->mTextureId = 0;
- }
-}
-
void FontRenderer::flushLargeCaches() {
// Start from 1; don't deallocate smallest/default texture
for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
CacheTexture* cacheTexture = mCacheTextures[i];
- if (cacheTexture->mTexture != NULL) {
+ if (cacheTexture->getTexture()) {
cacheTexture->init();
for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
mActiveFonts[j]->invalidateTextureCache(cacheTexture);
}
- deallocateTextureMemory(cacheTexture);
+ cacheTexture->releaseTexture();
}
}
}
-void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
- int width = cacheTexture->mWidth;
- int height = cacheTexture->mHeight;
-
- cacheTexture->mTexture = new uint8_t[width * height];
-
- if (!cacheTexture->mTextureId) {
- glGenTextures(1, &cacheTexture->mTextureId);
- }
-
- Caches::getInstance().activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- // Initialize texture dimensions
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
- GL_ALPHA, GL_UNSIGNED_BYTE, 0);
-
- const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-}
-
CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
uint32_t* startX, uint32_t* startY) {
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
@@ -774,7 +172,7 @@
cachedGlyph->mIsValid = false;
// If the glyph is too tall, don't cache it
if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
- mCacheTextures[mCacheTextures.size() - 1]->mHeight) {
+ mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
ALOGE("Font size too large to fit in cache. width, height = %i, %i",
(int) glyph.fWidth, (int) glyph.fHeight);
return;
@@ -808,14 +206,15 @@
uint32_t endX = startX + glyph.fWidth;
uint32_t endY = startY + glyph.fHeight;
- uint32_t cacheWidth = cacheTexture->mWidth;
+ uint32_t cacheWidth = cacheTexture->getWidth();
- if (!cacheTexture->mTexture) {
+ if (!cacheTexture->getTexture()) {
+ Caches::getInstance().activeTexture(0);
// Large-glyph texture memory is allocated only as needed
- allocateTextureMemory(cacheTexture);
+ cacheTexture->allocateTexture();
}
- uint8_t* cacheBuffer = cacheTexture->mTexture;
+ uint8_t* cacheBuffer = cacheTexture->getTexture();
uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
unsigned int stride = glyph.rowBytes();
@@ -855,7 +254,8 @@
CacheTexture* cacheTexture = new CacheTexture(width, height);
if (allocate) {
- allocateTextureMemory(cacheTexture);
+ Caches::getInstance().activeTexture(0);
+ cacheTexture->allocateTexture();
}
return cacheTexture;
@@ -905,7 +305,7 @@
uint32_t uvSize = 2;
uint32_t vertsPerQuad = 4;
uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
- mTextMeshPtr = new float[vertexBufferSize];
+ mTextMesh = new float[vertexBufferSize];
}
// We don't want to allocate anything unless we actually draw text
@@ -930,16 +330,16 @@
// Iterate over all the cache textures and see which ones need to be updated
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
CacheTexture* cacheTexture = mCacheTextures[i];
- if (cacheTexture->mDirty && cacheTexture->mTexture != NULL) {
+ if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
uint32_t xOffset = 0;
- uint32_t width = cacheTexture->mWidth;
- uint32_t height = cacheTexture->mHeight;
- void* textureData = cacheTexture->mTexture;
+ uint32_t width = cacheTexture->getWidth();
+ uint32_t height = cacheTexture->getHeight();
+ void* textureData = cacheTexture->getTexture();
- if (cacheTexture->mTextureId != lastTextureId) {
+ if (cacheTexture->getTextureId() != lastTextureId) {
+ lastTextureId = cacheTexture->getTextureId();
caches.activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
- lastTextureId = cacheTexture->mTextureId;
+ glBindTexture(GL_TEXTURE_2D, lastTextureId);
}
#if DEBUG_FONT_RENDERER
ALOGD("glTextSubimage for cacheTexture %d: xOff, width height = %d, %d, %d",
@@ -948,18 +348,14 @@
glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, 0, width, height,
GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
- cacheTexture->mDirty = false;
+ cacheTexture->setDirty(false);
}
}
caches.activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
- if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
- const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
- mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
- }
+ glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->getTextureId());
+
+ mCurrentCacheTexture->setLinearFiltering(mLinearFiltering, false);
mLastCacheTexture = mCurrentCacheTexture;
mUploadTexture = false;
@@ -971,7 +367,7 @@
Caches& caches = Caches::getInstance();
caches.bindIndicesBuffer(mIndexBufferID);
if (!mDrawn) {
- float* buffer = mTextMeshPtr;
+ float* buffer = mTextMesh;
int offset = 2;
bool force = caches.unbindMeshBuffer();
@@ -1000,7 +396,7 @@
const uint32_t vertsPerQuad = 4;
const uint32_t floatsPerVert = 4;
- float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
+ float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
(*currentPos++) = x1;
(*currentPos++) = y1;
@@ -1213,6 +609,19 @@
return mDrawn;
}
+void FontRenderer::removeFont(const Font* font) {
+ for (uint32_t ct = 0; ct < mActiveFonts.size(); ct++) {
+ if (mActiveFonts[ct] == font) {
+ mActiveFonts.removeAt(ct);
+ break;
+ }
+ }
+
+ if (mCurrentFont == font) {
+ mCurrentFont = NULL;
+ }
+}
+
void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
// Compute gaussian weights for the blur
// e is the euler's number
@@ -1300,7 +709,6 @@
float currentPixel = 0.0f;
for (int32_t y = 0; y < height; y ++) {
-
uint8_t* output = dest + y * width;
for (int32_t x = 0; x < width; x ++) {
@@ -1334,7 +742,7 @@
}
}
*output = (uint8_t) blurredPixel;
- output ++;
+ output++;
}
}
}
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 241b73e..405db09 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -17,268 +17,22 @@
#ifndef ANDROID_HWUI_FONT_RENDERER_H
#define ANDROID_HWUI_FONT_RENDERER_H
-#include <utils/String8.h>
-#include <utils/String16.h>
#include <utils/Vector.h>
-#include <utils/KeyedVector.h>
-#include <SkScalerContext.h>
#include <SkPaint.h>
-#include <SkPathMeasure.h>
-#include <SkPoint.h>
#include <GLES2/gl2.h>
-#include "Rect.h"
+#include "font/FontUtil.h"
+#include "font/CacheTexture.h"
+#include "font/CachedGlyphInfo.h"
+#include "font/Font.h"
#include "Properties.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#if RENDER_TEXT_AS_GLYPHS
- typedef uint16_t glyph_t;
- #define TO_GLYPH(g) g
- #define GET_METRICS(paint, glyph) paint->getGlyphMetrics(glyph)
- #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
- #define IS_END_OF_STRING(glyph) false
-#else
- typedef SkUnichar glyph_t;
- #define TO_GLYPH(g) ((SkUnichar) g)
- #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph)
- #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
- #define IS_END_OF_STRING(glyph) glyph < 0
-#endif
-
-#define TEXTURE_BORDER_SIZE 1
-
-///////////////////////////////////////////////////////////////////////////////
-// Declarations
-///////////////////////////////////////////////////////////////////////////////
-
-class FontRenderer;
-
-/**
- * CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
- * Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
- * When we add a glyph to the cache, we see if it fits within one of the existing columns that
- * have already been started (this is the case if the glyph fits vertically as well as
- * horizontally, and if its width is sufficiently close to the column width to avoid
- * sub-optimal packing of small glyphs into wide columns). If there is no column in which the
- * glyph fits, we check the final node, which is the remaining space in the cache, creating
- * a new column as appropriate.
- *
- * As columns fill up, we remove their CacheBlock from the list to avoid having to check
- * small blocks in the future.
- */
-struct CacheBlock {
- uint16_t mX;
- uint16_t mY;
- uint16_t mWidth;
- uint16_t mHeight;
- CacheBlock* mNext;
- CacheBlock* mPrev;
-
- CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height, bool empty = false):
- mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL)
- {
- }
-
- static CacheBlock* insertBlock(CacheBlock* head, CacheBlock *newBlock);
-
- static CacheBlock* removeBlock(CacheBlock* head, CacheBlock *blockToRemove);
-
- void output() {
- CacheBlock *currBlock = this;
- while (currBlock) {
- ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d",
- currBlock, currBlock->mX, currBlock->mY, currBlock->mWidth, currBlock->mHeight);
- currBlock = currBlock->mNext;
- }
- }
-};
-
-class CacheTexture {
-public:
- CacheTexture(uint16_t width, uint16_t height) :
- mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
- mLinearFiltering(false), mDirty(false), mNumGlyphs(0) {
- mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
- mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
- }
-
- ~CacheTexture() {
- if (mTexture) {
- delete[] mTexture;
- }
- if (mTextureId) {
- glDeleteTextures(1, &mTextureId);
- }
- reset();
- }
-
- void reset() {
- // Delete existing cache blocks
- while (mCacheBlocks != NULL) {
- CacheBlock* tmpBlock = mCacheBlocks;
- mCacheBlocks = mCacheBlocks->mNext;
- delete tmpBlock;
- }
- mNumGlyphs = 0;
- }
-
- void init() {
- // reset, then create a new remainder space to start again
- reset();
- mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
- mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
- }
-
- bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
-
- uint8_t* mTexture;
- GLuint mTextureId;
- uint16_t mWidth;
- uint16_t mHeight;
- bool mLinearFiltering;
- bool mDirty;
- uint16_t mNumGlyphs;
- CacheBlock* mCacheBlocks;
-};
-
-struct CachedGlyphInfo {
- // Has the cache been invalidated?
- bool mIsValid;
- // Location of the cached glyph in the bitmap
- // in case we need to resize the texture or
- // render to bitmap
- uint32_t mStartX;
- uint32_t mStartY;
- uint32_t mBitmapWidth;
- uint32_t mBitmapHeight;
- // Also cache texture coords for the quad
- float mBitmapMinU;
- float mBitmapMinV;
- float mBitmapMaxU;
- float mBitmapMaxV;
- // Minimize how much we call freetype
- uint32_t mGlyphIndex;
- uint32_t mAdvanceX;
- uint32_t mAdvanceY;
- // Values below contain a glyph's origin in the bitmap
- int32_t mBitmapLeft;
- int32_t mBitmapTop;
- // Auto-kerning
- SkFixed mLsbDelta;
- SkFixed mRsbDelta;
- CacheTexture* mCacheTexture;
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
-// Font
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Represents a font, defined by a Skia font id and a font size. A font is used
- * to generate glyphs and cache them in the FontState.
- */
-class Font {
-public:
- enum Style {
- kFakeBold = 1
- };
-
- ~Font();
-
- /**
- * Renders the specified string of text.
- * If bitmap is specified, it will be used as the render target
- */
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, uint8_t *bitmap = NULL,
- uint32_t bitmapW = 0, uint32_t bitmapH = 0);
-
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, const float* positions);
-
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, SkPath* path, float hOffset, float vOffset);
-
- /**
- * Creates a new font associated with the specified font state.
- */
- static Font* create(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle, uint32_t scaleX, SkPaint::Style style,
- uint32_t strokeWidth);
-
-protected:
- friend class FontRenderer;
- typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
- uint32_t, uint32_t, Rect*, const float*);
-
- enum RenderMode {
- FRAMEBUFFER,
- BITMAP,
- MEASURE,
- };
-
- void precache(SkPaint* paint, const char* text, int numGlyphs);
-
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
- uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
-
- void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, Rect *bounds, const float* positions);
-
- Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
- uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
-
- // Cache of glyphs
- DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
-
- void invalidateTextureCache(CacheTexture *cacheTexture = NULL);
-
- CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching);
- void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
- bool precaching);
-
- void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect* bounds, const float* pos);
- void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect* bounds, const float* pos);
- void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect* bounds, const float* pos);
- void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
- SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
-
- CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false);
-
- static glyph_t nextGlyph(const uint16_t** srcPtr) {
- const uint16_t* src = *srcPtr;
- glyph_t g = *src++;
- *srcPtr = src;
- return g;
- }
-
- FontRenderer* mState;
- uint32_t mFontId;
- float mFontSize;
- int mFlags;
- uint32_t mItalicStyle;
- uint32_t mScaleX;
- SkPaint::Style mStyle;
- uint32_t mStrokeWidth;
-};
-
-///////////////////////////////////////////////////////////////////////////////
// Renderer
///////////////////////////////////////////////////////////////////////////////
@@ -331,31 +85,24 @@
GLuint getTexture(bool linearFiltering = false) {
checkInit();
- if (linearFiltering != mCurrentCacheTexture->mLinearFiltering) {
- mCurrentCacheTexture->mLinearFiltering = linearFiltering;
- mLinearFiltering = linearFiltering;
- const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
+ mCurrentCacheTexture->setLinearFiltering(linearFiltering);
+ mLinearFiltering = linearFiltering;
- glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
- }
-
- return mCurrentCacheTexture->mTextureId;
+ return mCurrentCacheTexture->getTextureId();
}
uint32_t getCacheSize() const {
uint32_t size = 0;
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
CacheTexture* cacheTexture = mCacheTextures[i];
- if (cacheTexture != NULL && cacheTexture->mTexture != NULL) {
- size += cacheTexture->mWidth * cacheTexture->mHeight;
+ if (cacheTexture && cacheTexture->getTexture()) {
+ size += cacheTexture->getWidth() * cacheTexture->getHeight();
}
}
return size;
}
-protected:
+private:
friend class Font;
const uint8_t* mGammaTable;
@@ -389,6 +136,14 @@
float x3, float y3, float u3, float v3,
float x4, float y4, float u4, float v4, CacheTexture* texture);
+ void removeFont(const Font* font);
+
+ void checkTextureUpdate();
+
+ void setTextureDirty() {
+ mUploadTexture = true;
+ }
+
uint32_t mSmallCacheWidth;
uint32_t mSmallCacheHeight;
uint32_t mLargeCacheWidth;
@@ -402,11 +157,10 @@
CacheTexture* mCurrentCacheTexture;
CacheTexture* mLastCacheTexture;
- void checkTextureUpdate();
bool mUploadTexture;
// Pointer to vertex data to speed up frame to frame work
- float *mTextMeshPtr;
+ float* mTextMesh;
uint32_t mCurrentQuadIndex;
uint32_t mMaxNumberOfQuads;
@@ -420,12 +174,13 @@
bool mLinearFiltering;
- void computeGaussianWeights(float* weights, int32_t radius);
- void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
+ /** We should consider multi-threading this code or using Renderscript **/
+ static void computeGaussianWeights(float* weights, int32_t radius);
+ static void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
int32_t width, int32_t height);
- void verticalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
+ static void verticalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
int32_t width, int32_t height);
- void blurImage(uint8_t* image, int32_t width, int32_t height, int32_t radius);
+ static void blurImage(uint8_t* image, int32_t width, int32_t height, int32_t radius);
};
}; // namespace uirenderer
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 2f43be8..7abcc63 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1933,7 +1933,7 @@
// This value is used in the fragment shader to determine how to fill fragments.
// We will need to calculate the actual width proportion on each segment for
// scaled non-hairlines, since the boundary proportion may differ per-axis when scaled.
- float boundaryWidthProportion = 1 / (2 * halfStrokeWidth);
+ float boundaryWidthProportion = .5 - 1 / (2 * halfStrokeWidth);
setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords,
boundaryWidthProportion, widthSlot, lengthSlot);
}
@@ -1997,9 +1997,9 @@
abVector.x *= inverseScaleX;
abVector.y *= inverseScaleY;
float abLength = abVector.length();
- boundaryLengthProportion = abLength / (length + abLength);
+ boundaryLengthProportion = .5 - abLength / (length + abLength);
} else {
- boundaryLengthProportion = .5 / (length + 1);
+ boundaryLengthProportion = .5 - .5 / (length + 1);
}
abVector /= 2;
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
new file mode 100644
index 0000000..4a3af12
--- /dev/null
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/Log.h>
+
+#include "Debug.h"
+#include "CacheTexture.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// CacheBlock
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
+ * order, except for the final block (the remainder space at the right, since we fill from the
+ * left).
+ */
+CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock* newBlock) {
+#if DEBUG_FONT_RENDERER
+ ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
+ newBlock, newBlock->mX, newBlock->mY,
+ newBlock->mWidth, newBlock->mHeight);
+#endif
+
+ CacheBlock* currBlock = head;
+ CacheBlock* prevBlock = NULL;
+
+ while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
+ if (newBlock->mWidth < currBlock->mWidth) {
+ newBlock->mNext = currBlock;
+ newBlock->mPrev = prevBlock;
+ currBlock->mPrev = newBlock;
+
+ if (prevBlock) {
+ prevBlock->mNext = newBlock;
+ return head;
+ } else {
+ return newBlock;
+ }
+ }
+
+ prevBlock = currBlock;
+ currBlock = currBlock->mNext;
+ }
+
+ // new block larger than all others - insert at end (but before the remainder space, if there)
+ newBlock->mNext = currBlock;
+ newBlock->mPrev = prevBlock;
+
+ if (currBlock) {
+ currBlock->mPrev = newBlock;
+ }
+
+ if (prevBlock) {
+ prevBlock->mNext = newBlock;
+ return head;
+ } else {
+ return newBlock;
+ }
+}
+
+CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock* blockToRemove) {
+#if DEBUG_FONT_RENDERER
+ ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
+ blockToRemove, blockToRemove->mX, blockToRemove->mY,
+ blockToRemove->mWidth, blockToRemove->mHeight);
+#endif
+
+ CacheBlock* newHead = head;
+ CacheBlock* nextBlock = blockToRemove->mNext;
+ CacheBlock* prevBlock = blockToRemove->mPrev;
+
+ if (prevBlock) {
+ prevBlock->mNext = nextBlock;
+ } else {
+ newHead = nextBlock;
+ }
+
+ if (nextBlock) {
+ nextBlock->mPrev = prevBlock;
+ }
+
+ delete blockToRemove;
+
+ return newHead;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CacheTexture
+///////////////////////////////////////////////////////////////////////////////
+
+bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
+ if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mHeight) {
+ return false;
+ }
+
+ uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
+ uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
+
+ // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
+ // This columns for glyphs that are close but not necessarily exactly the same size. It trades
+ // off the loss of a few pixels for some glyphs against the ability to store more glyphs
+ // of varying sizes in one block.
+ uint16_t roundedUpW = (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
+
+ CacheBlock* cacheBlock = mCacheBlocks;
+ while (cacheBlock) {
+ // Store glyph in this block iff: it fits the block's remaining space and:
+ // it's the remainder space (mY == 0) or there's only enough height for this one glyph
+ // or it's within ROUNDING_SIZE of the block width
+ if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
+ (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
+ (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
+ if (cacheBlock->mHeight - glyphH < glyphH) {
+ // Only enough space for this glyph - don't bother rounding up the width
+ roundedUpW = glyphW;
+ }
+
+ *retOriginX = cacheBlock->mX;
+ *retOriginY = cacheBlock->mY;
+
+ // If this is the remainder space, create a new cache block for this column. Otherwise,
+ // adjust the info about this column.
+ if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
+ uint16_t oldX = cacheBlock->mX;
+ // Adjust remainder space dimensions
+ cacheBlock->mWidth -= roundedUpW;
+ cacheBlock->mX += roundedUpW;
+
+ if (mHeight - glyphH >= glyphH) {
+ // There's enough height left over to create a new CacheBlock
+ CacheBlock* newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE,
+ roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE);
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
+ newBlock, newBlock->mX, newBlock->mY,
+ newBlock->mWidth, newBlock->mHeight);
+#endif
+ mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
+ }
+ } else {
+ // Insert into current column and adjust column dimensions
+ cacheBlock->mY += glyphH;
+ cacheBlock->mHeight -= glyphH;
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
+ cacheBlock, cacheBlock->mX, cacheBlock->mY,
+ cacheBlock->mWidth, cacheBlock->mHeight);
+#endif
+ }
+
+ if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
+ // If remaining space in this block is too small to be useful, remove it
+ mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
+ }
+
+ mDirty = true;
+ mNumGlyphs++;
+
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: current block list:");
+ mCacheBlocks->output();
+#endif
+
+ return true;
+ }
+ cacheBlock = cacheBlock->mNext;
+ }
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
+#endif
+ return false;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
new file mode 100644
index 0000000..bf1f4a9
--- /dev/null
+++ b/libs/hwui/font/CacheTexture.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_CACHE_TEXTURE_H
+#define ANDROID_HWUI_CACHE_TEXTURE_H
+
+#include <GLES2/gl2.h>
+
+#include <SkScalerContext.h>
+
+#include <utils/Log.h>
+
+#include "FontUtil.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
+ * Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
+ * When we add a glyph to the cache, we see if it fits within one of the existing columns that
+ * have already been started (this is the case if the glyph fits vertically as well as
+ * horizontally, and if its width is sufficiently close to the column width to avoid
+ * sub-optimal packing of small glyphs into wide columns). If there is no column in which the
+ * glyph fits, we check the final node, which is the remaining space in the cache, creating
+ * a new column as appropriate.
+ *
+ * As columns fill up, we remove their CacheBlock from the list to avoid having to check
+ * small blocks in the future.
+ */
+struct CacheBlock {
+ uint16_t mX;
+ uint16_t mY;
+ uint16_t mWidth;
+ uint16_t mHeight;
+ CacheBlock* mNext;
+ CacheBlock* mPrev;
+
+ CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height, bool empty = false):
+ mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL) {
+ }
+
+ static CacheBlock* insertBlock(CacheBlock* head, CacheBlock* newBlock);
+
+ static CacheBlock* removeBlock(CacheBlock* head, CacheBlock* blockToRemove);
+
+ void output() {
+ CacheBlock* currBlock = this;
+ while (currBlock) {
+ ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d",
+ currBlock, currBlock->mX, currBlock->mY, currBlock->mWidth, currBlock->mHeight);
+ currBlock = currBlock->mNext;
+ }
+ }
+};
+
+class CacheTexture {
+public:
+ CacheTexture(uint16_t width, uint16_t height) :
+ mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
+ mLinearFiltering(false), mDirty(false), mNumGlyphs(0) {
+ mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
+ mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+ }
+
+ ~CacheTexture() {
+ if (mTexture) {
+ delete[] mTexture;
+ }
+ if (mTextureId) {
+ glDeleteTextures(1, &mTextureId);
+ }
+ reset();
+ }
+
+ void reset() {
+ // Delete existing cache blocks
+ while (mCacheBlocks != NULL) {
+ CacheBlock* tmpBlock = mCacheBlocks;
+ mCacheBlocks = mCacheBlocks->mNext;
+ delete tmpBlock;
+ }
+ mNumGlyphs = 0;
+ }
+
+ void init() {
+ // reset, then create a new remainder space to start again
+ reset();
+ mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
+ mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+ }
+
+ void releaseTexture() {
+ if (mTexture) {
+ glDeleteTextures(1, &mTextureId);
+ delete[] mTexture;
+ mTexture = NULL;
+ mTextureId = 0;
+ }
+ }
+
+ /**
+ * This method assumes that the proper texture unit is active.
+ */
+ void allocateTexture() {
+ int width = mWidth;
+ int height = mHeight;
+
+ mTexture = new uint8_t[width * height];
+
+ if (!mTextureId) {
+ glGenTextures(1, &mTextureId);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, mTextureId);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ // Initialize texture dimensions
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, 0);
+
+ const GLenum filtering = getLinearFiltering() ? GL_LINEAR : GL_NEAREST;
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ bool fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY);
+
+ inline uint16_t getWidth() const {
+ return mWidth;
+ }
+
+ inline uint16_t getHeight() const {
+ return mHeight;
+ }
+
+ inline uint8_t* getTexture() const {
+ return mTexture;
+ }
+
+ inline GLuint getTextureId() const {
+ return mTextureId;
+ }
+
+ inline bool isDirty() const {
+ return mDirty;
+ }
+
+ inline void setDirty(bool dirty) {
+ mDirty = dirty;
+ }
+
+ inline bool getLinearFiltering() const {
+ return mLinearFiltering;
+ }
+
+ /**
+ * This method assumes that the proper texture unit is active.
+ */
+ void setLinearFiltering(bool linearFiltering, bool bind = true) {
+ if (linearFiltering != mLinearFiltering) {
+ mLinearFiltering = linearFiltering;
+
+ const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
+ if (bind) glBindTexture(GL_TEXTURE_2D, getTextureId());
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
+ }
+ }
+
+ inline uint16_t getGlyphCount() const {
+ return mNumGlyphs;
+ }
+
+private:
+ uint8_t* mTexture;
+ GLuint mTextureId;
+ uint16_t mWidth;
+ uint16_t mHeight;
+ bool mLinearFiltering;
+ bool mDirty;
+ uint16_t mNumGlyphs;
+ CacheBlock* mCacheBlocks;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_CACHE_TEXTURE_H
diff --git a/libs/hwui/font/CachedGlyphInfo.h b/libs/hwui/font/CachedGlyphInfo.h
new file mode 100644
index 0000000..6680a00
--- /dev/null
+++ b/libs/hwui/font/CachedGlyphInfo.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_CACHED_GLYPH_INFO_H
+#define ANDROID_HWUI_CACHED_GLYPH_INFO_H
+
+#include <SkFixed.h>
+
+#include "CacheTexture.h"
+
+namespace android {
+namespace uirenderer {
+
+struct CachedGlyphInfo {
+ // Has the cache been invalidated?
+ bool mIsValid;
+ // Location of the cached glyph in the bitmap
+ // in case we need to resize the texture or
+ // render to bitmap
+ uint32_t mStartX;
+ uint32_t mStartY;
+ uint32_t mBitmapWidth;
+ uint32_t mBitmapHeight;
+ // Also cache texture coords for the quad
+ float mBitmapMinU;
+ float mBitmapMinV;
+ float mBitmapMaxU;
+ float mBitmapMaxV;
+ // Minimize how much we call freetype
+ uint32_t mGlyphIndex;
+ uint32_t mAdvanceX;
+ uint32_t mAdvanceY;
+ // Values below contain a glyph's origin in the bitmap
+ int32_t mBitmapLeft;
+ int32_t mBitmapTop;
+ // Auto-kerning
+ SkFixed mLsbDelta;
+ SkFixed mRsbDelta;
+ CacheTexture* mCacheTexture;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_CACHED_GLYPH_INFO_H
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
new file mode 100644
index 0000000..6e205b8
--- /dev/null
+++ b/libs/hwui/font/Font.cpp
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/compiler.h>
+
+#include <SkUtils.h>
+
+#include "Debug.h"
+#include "FontUtil.h"
+#include "Font.h"
+#include "FontRenderer.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
+Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
+ int flags, uint32_t italicStyle, uint32_t scaleX,
+ SkPaint::Style style, uint32_t strokeWidth) :
+ mState(state), mFontId(fontId), mFontSize(fontSize),
+ mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
+ mStyle(style), mStrokeWidth(mStrokeWidth) {
+}
+
+
+Font::~Font() {
+ mState->removeFont(this);
+
+ for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+ delete mCachedGlyphs.valueAt(i);
+ }
+}
+
+void Font::invalidateTextureCache(CacheTexture* cacheTexture) {
+ for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+ CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
+ if (!cacheTexture || cachedGlyph->mCacheTexture == cacheTexture) {
+ cachedGlyph->mIsValid = false;
+ }
+ }
+}
+
+void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
+ uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
+ int nPenX = x + glyph->mBitmapLeft;
+ int nPenY = y + glyph->mBitmapTop;
+
+ int width = (int) glyph->mBitmapWidth;
+ int height = (int) glyph->mBitmapHeight;
+
+ if (bounds->bottom > nPenY) {
+ bounds->bottom = nPenY;
+ }
+ if (bounds->left > nPenX) {
+ bounds->left = nPenX;
+ }
+ if (bounds->right < nPenX + width) {
+ bounds->right = nPenX + width;
+ }
+ if (bounds->top < nPenY + height) {
+ bounds->top = nPenY + height;
+ }
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
+ int nPenX = x + glyph->mBitmapLeft;
+ int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
+
+ float u1 = glyph->mBitmapMinU;
+ float u2 = glyph->mBitmapMaxU;
+ float v1 = glyph->mBitmapMinV;
+ float v2 = glyph->mBitmapMaxV;
+
+ int width = (int) glyph->mBitmapWidth;
+ int height = (int) glyph->mBitmapHeight;
+
+ mState->appendMeshQuad(nPenX, nPenY, u1, v2,
+ nPenX + width, nPenY, u2, v2,
+ nPenX + width, nPenY - height, u2, v1,
+ nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
+}
+
+void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
+ int nPenX = x + glyph->mBitmapLeft;
+ int nPenY = y + glyph->mBitmapTop;
+
+ uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
+ uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
+
+ CacheTexture* cacheTexture = glyph->mCacheTexture;
+ uint32_t cacheWidth = cacheTexture->getWidth();
+ const uint8_t* cacheBuffer = cacheTexture->getTexture();
+
+ uint32_t cacheX = 0, cacheY = 0;
+ int32_t bX = 0, bY = 0;
+ for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
+ for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
+#if DEBUG_FONT_RENDERER
+ if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
+ ALOGE("Skipping invalid index");
+ continue;
+ }
+#endif
+ uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
+ bitmap[bY * bitmapW + bX] = tempCol;
+ }
+ }
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
+ SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
+ const float halfWidth = glyph->mBitmapWidth * 0.5f;
+ const float height = glyph->mBitmapHeight;
+
+ vOffset += glyph->mBitmapTop + height;
+
+ SkPoint destination[4];
+ measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
+
+ // Move along the tangent and offset by the normal
+ destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
+ -tangent->fY * halfWidth + tangent->fX * vOffset);
+ destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
+ tangent->fY * halfWidth + tangent->fX * vOffset);
+ destination[2].set(destination[1].fX + tangent->fY * height,
+ destination[1].fY - tangent->fX * height);
+ destination[3].set(destination[0].fX + tangent->fY * height,
+ destination[0].fY - tangent->fX * height);
+
+ const float u1 = glyph->mBitmapMinU;
+ const float u2 = glyph->mBitmapMaxU;
+ const float v1 = glyph->mBitmapMinV;
+ const float v2 = glyph->mBitmapMaxV;
+
+ mState->appendRotatedMeshQuad(
+ position->fX + destination[0].fX,
+ position->fY + destination[0].fY, u1, v2,
+ position->fX + destination[1].fX,
+ position->fY + destination[1].fY, u2, v2,
+ position->fX + destination[2].fX,
+ position->fY + destination[2].fY, u2, v1,
+ position->fX + destination[3].fX,
+ position->fY + destination[3].fY, u1, v1,
+ glyph->mCacheTexture);
+}
+
+CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) {
+ CachedGlyphInfo* cachedGlyph = NULL;
+ ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
+ if (index >= 0) {
+ cachedGlyph = mCachedGlyphs.valueAt(index);
+ } else {
+ cachedGlyph = cacheGlyph(paint, textUnit, precaching);
+ }
+
+ // Is the glyph still in texture cache?
+ if (!cachedGlyph->mIsValid) {
+ const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
+ updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
+ }
+
+ return cachedGlyph;
+}
+
+void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+ if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
+ render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
+ bitmapW, bitmapH, NULL, NULL);
+ } else {
+ render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
+ 0, 0, NULL, NULL);
+ }
+}
+
+void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, const float* positions) {
+ render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
+ 0, 0, NULL, positions);
+}
+
+void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, SkPath* path, float hOffset, float vOffset) {
+ if (numGlyphs == 0 || text == NULL || len == 0) {
+ return;
+ }
+
+ text += start;
+
+ int glyphsCount = 0;
+ SkFixed prevRsbDelta = 0;
+
+ float penX = 0.0f;
+
+ SkPoint position;
+ SkVector tangent;
+
+ SkPathMeasure measure(*path, false);
+ float pathLength = SkScalarToFloat(measure.getLength());
+
+ if (paint->getTextAlign() != SkPaint::kLeft_Align) {
+ float textWidth = SkScalarToFloat(paint->measureText(text, len));
+ float pathOffset = pathLength;
+ if (paint->getTextAlign() == SkPaint::kCenter_Align) {
+ textWidth *= 0.5f;
+ pathOffset *= 0.5f;
+ }
+ penX += pathOffset - textWidth;
+ }
+
+ while (glyphsCount < numGlyphs && penX < pathLength) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+ penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
+ prevRsbDelta = cachedGlyph->mRsbDelta;
+
+ if (cachedGlyph->mIsValid) {
+ drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
+ }
+
+ penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
+
+ glyphsCount++;
+ }
+}
+
+void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, Rect *bounds, const float* positions) {
+ if (bounds == NULL) {
+ ALOGE("No return rectangle provided to measure text");
+ return;
+ }
+ bounds->set(1e6, -1e6, -1e6, 1e6);
+ render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
+}
+
+void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
+
+ if (numGlyphs == 0 || text == NULL) {
+ return;
+ }
+ int glyphsCount = 0;
+
+ while (glyphsCount < numGlyphs) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ // Reached the end of the string
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph, true);
+
+ glyphsCount++;
+ }
+}
+
+void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
+ uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
+ if (numGlyphs == 0 || text == NULL || len == 0) {
+ return;
+ }
+
+ static RenderGlyph gRenderGlyph[] = {
+ &android::uirenderer::Font::drawCachedGlyph,
+ &android::uirenderer::Font::drawCachedGlyphBitmap,
+ &android::uirenderer::Font::measureCachedGlyph
+ };
+ RenderGlyph render = gRenderGlyph[mode];
+
+ text += start;
+ int glyphsCount = 0;
+
+ if (CC_LIKELY(positions == NULL)) {
+ SkFixed prevRsbDelta = 0;
+
+ float penX = x + 0.5f;
+ int penY = y;
+
+ while (glyphsCount < numGlyphs) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ // Reached the end of the string
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+ penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
+ prevRsbDelta = cachedGlyph->mRsbDelta;
+
+ // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
+ if (cachedGlyph->mIsValid) {
+ (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
+ bitmap, bitmapW, bitmapH, bounds, positions);
+ }
+
+ penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
+
+ glyphsCount++;
+ }
+ } else {
+ const SkPaint::Align align = paint->getTextAlign();
+
+ // This is for renderPosText()
+ while (glyphsCount < numGlyphs) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ // Reached the end of the string
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+
+ // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
+ if (cachedGlyph->mIsValid) {
+ int penX = x + positions[(glyphsCount << 1)];
+ int penY = y + positions[(glyphsCount << 1) + 1];
+
+ switch (align) {
+ case SkPaint::kRight_Align:
+ penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
+ penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
+ break;
+ case SkPaint::kCenter_Align:
+ penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
+ penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
+ default:
+ break;
+ }
+
+ (*this.*render)(cachedGlyph, penX, penY,
+ bitmap, bitmapW, bitmapH, bounds, positions);
+ }
+
+ glyphsCount++;
+ }
+ }
+}
+
+void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
+ bool precaching) {
+ glyph->mAdvanceX = skiaGlyph.fAdvanceX;
+ glyph->mAdvanceY = skiaGlyph.fAdvanceY;
+ glyph->mBitmapLeft = skiaGlyph.fLeft;
+ glyph->mBitmapTop = skiaGlyph.fTop;
+ glyph->mLsbDelta = skiaGlyph.fLsbDelta;
+ glyph->mRsbDelta = skiaGlyph.fRsbDelta;
+
+ uint32_t startX = 0;
+ uint32_t startY = 0;
+
+ // Get the bitmap for the glyph
+ paint->findImage(skiaGlyph);
+ mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
+
+ if (!glyph->mIsValid) {
+ return;
+ }
+
+ uint32_t endX = startX + skiaGlyph.fWidth;
+ uint32_t endY = startY + skiaGlyph.fHeight;
+
+ glyph->mStartX = startX;
+ glyph->mStartY = startY;
+ glyph->mBitmapWidth = skiaGlyph.fWidth;
+ glyph->mBitmapHeight = skiaGlyph.fHeight;
+
+ uint32_t cacheWidth = glyph->mCacheTexture->getWidth();
+ uint32_t cacheHeight = glyph->mCacheTexture->getHeight();
+
+ glyph->mBitmapMinU = startX / (float) cacheWidth;
+ glyph->mBitmapMinV = startY / (float) cacheHeight;
+ glyph->mBitmapMaxU = endX / (float) cacheWidth;
+ glyph->mBitmapMaxV = endY / (float) cacheHeight;
+
+ mState->setTextureDirty();
+}
+
+CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
+ CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
+ mCachedGlyphs.add(glyph, newGlyph);
+
+ const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
+ newGlyph->mGlyphIndex = skiaGlyph.fID;
+ newGlyph->mIsValid = false;
+
+ updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
+
+ return newGlyph;
+}
+
+Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
+ int flags, uint32_t italicStyle, uint32_t scaleX,
+ SkPaint::Style style, uint32_t strokeWidth) {
+ Vector<Font*> &activeFonts = state->mActiveFonts;
+
+ for (uint32_t i = 0; i < activeFonts.size(); i++) {
+ Font* font = activeFonts[i];
+ if (font->mFontId == fontId && font->mFontSize == fontSize &&
+ font->mFlags == flags && font->mItalicStyle == italicStyle &&
+ font->mScaleX == scaleX && font->mStyle == style &&
+ (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
+ return font;
+ }
+ }
+
+ Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
+ scaleX, style, strokeWidth);
+ activeFonts.push(newFont);
+ return newFont;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
new file mode 100644
index 0000000..7cab31e
--- /dev/null
+++ b/libs/hwui/font/Font.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_FONT_H
+#define ANDROID_HWUI_FONT_H
+
+#include <utils/KeyedVector.h>
+
+#include <SkScalerContext.h>
+#include <SkPaint.h>
+#include <SkPathMeasure.h>
+
+#include "CachedGlyphInfo.h"
+#include "../Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
+class FontRenderer;
+
+/**
+ * Represents a font, defined by a Skia font id and a font size. A font is used
+ * to generate glyphs and cache them in the FontState.
+ */
+class Font {
+public:
+ enum Style {
+ kFakeBold = 1
+ };
+
+ ~Font();
+
+ /**
+ * Renders the specified string of text.
+ * If bitmap is specified, it will be used as the render target
+ */
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, uint8_t *bitmap = NULL,
+ uint32_t bitmapW = 0, uint32_t bitmapH = 0);
+
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, const float* positions);
+
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, SkPath* path, float hOffset, float vOffset);
+
+ /**
+ * Creates a new font associated with the specified font state.
+ */
+ static Font* create(FontRenderer* state, uint32_t fontId, float fontSize,
+ int flags, uint32_t italicStyle, uint32_t scaleX, SkPaint::Style style,
+ uint32_t strokeWidth);
+
+private:
+ friend class FontRenderer;
+ typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
+ uint32_t, uint32_t, Rect*, const float*);
+
+ enum RenderMode {
+ FRAMEBUFFER,
+ BITMAP,
+ MEASURE,
+ };
+
+ void precache(SkPaint* paint, const char* text, int numGlyphs);
+
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
+ uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
+
+ void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, Rect *bounds, const float* positions);
+
+ Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
+ uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
+
+ // Cache of glyphs
+ DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
+
+ void invalidateTextureCache(CacheTexture* cacheTexture = NULL);
+
+ CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching);
+ void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
+ bool precaching);
+
+ void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+ Rect* bounds, const float* pos);
+ void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+ Rect* bounds, const float* pos);
+ void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+ Rect* bounds, const float* pos);
+ void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
+ SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
+
+ CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false);
+
+ FontRenderer* mState;
+ uint32_t mFontId;
+ float mFontSize;
+ int mFlags;
+ uint32_t mItalicStyle;
+ uint32_t mScaleX;
+ SkPaint::Style mStyle;
+ uint32_t mStrokeWidth;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_FONT_H
diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h
new file mode 100644
index 0000000..12247ba
--- /dev/null
+++ b/libs/hwui/font/FontUtil.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_FONT_UTIL_H
+#define ANDROID_HWUI_FONT_UTIL_H
+
+#include <SkUtils.h>
+
+#include "Properties.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define DEFAULT_TEXT_SMALL_CACHE_WIDTH 1024
+#define DEFAULT_TEXT_SMALL_CACHE_HEIGHT 256
+#define DEFAULT_TEXT_LARGE_CACHE_WIDTH 2048
+#define DEFAULT_TEXT_LARGE_CACHE_HEIGHT 512
+
+#define TEXTURE_BORDER_SIZE 1
+
+#define CACHE_BLOCK_ROUNDING_SIZE 4
+
+#if RENDER_TEXT_AS_GLYPHS
+ typedef uint16_t glyph_t;
+ #define TO_GLYPH(g) g
+ #define GET_METRICS(paint, glyph) paint->getGlyphMetrics(glyph)
+ #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
+ #define IS_END_OF_STRING(glyph) false
+
+ static glyph_t nextGlyph(const uint16_t** srcPtr) {
+ const uint16_t* src = *srcPtr;
+ glyph_t g = *src++;
+ *srcPtr = src;
+ return g;
+ }
+#else
+ typedef SkUnichar glyph_t;
+ #define TO_GLYPH(g) ((SkUnichar) g)
+ #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph)
+ #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
+ #define IS_END_OF_STRING(glyph) glyph < 0
+#endif
+
+#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
+
+#endif // ANDROID_HWUI_FONT_UTIL_H
diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp
index 7eb51dd..74cf80e 100644
--- a/native/android/configuration.cpp
+++ b/native/android/configuration.cpp
@@ -123,6 +123,11 @@
return config->smallestScreenWidthDp;
}
+int32_t AConfiguration_getLayoutDirection(AConfiguration* config) {
+ return (config->screenLayout&ResTable_config::MASK_LAYOUTDIR)
+ >> ResTable_config::SHIFT_LAYOUTDIR;
+}
+
// ----------------------------------------------------------------------
void AConfiguration_setMcc(AConfiguration* config, int32_t mcc) {
@@ -210,6 +215,11 @@
config->smallestScreenWidthDp = value;
}
+void AConfiguration_setLayoutDirection(AConfiguration* config, int32_t value) {
+ config->screenLayout = (config->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+ | ((value<<ResTable_config::SHIFT_LAYOUTDIR)&ResTable_config::MASK_LAYOUTDIR);
+}
+
// ----------------------------------------------------------------------
int32_t AConfiguration_diff(AConfiguration* config1, AConfiguration* config2) {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b185471..7176f32 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -167,7 +167,7 @@
android:name=".BeanBag"
android:exported="true"
android:label="BeanBag"
- android:icon="@drawable/redbeandroid"
+ android:icon="@drawable/redbean2"
android:theme="@android:style/Theme.Wallpaper.NoTitleBar.Fullscreen"
android:hardwareAccelerated="true"
android:launchMode="singleInstance"
@@ -184,7 +184,9 @@
<service
android:name=".BeanBagDream"
android:exported="true"
- android:label="Beans in space">
+ android:label="@string/jelly_bean_dream_name"
+ android:enabled="false"
+ >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/packages/SystemUI/res/layout-sw600dp/carrier_label.xml b/packages/SystemUI/res/layout-sw600dp/carrier_label.xml
new file mode 100644
index 0000000..b33caf8
--- /dev/null
+++ b/packages/SystemUI/res/layout-sw600dp/carrier_label.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Space
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:visibility="gone"
+ />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/carrier_label.xml b/packages/SystemUI/res/layout/carrier_label.xml
new file mode 100644
index 0000000..41a1fff
--- /dev/null
+++ b/packages/SystemUI/res/layout/carrier_label.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/carrier_label"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Network"
+ android:layout_height="@dimen/carrier_label_height"
+ android:layout_width="match_parent"
+ android:layout_gravity="bottom"
+ android:layout_marginBottom="@dimen/close_handle_height"
+ android:gravity="center"
+ android:visibility="invisible"
+ />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_settings.xml b/packages/SystemUI/res/layout/quick_settings.xml
index c4b881e..8c6258a 100644
--- a/packages/SystemUI/res/layout/quick_settings.xml
+++ b/packages/SystemUI/res/layout/quick_settings.xml
@@ -19,6 +19,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/settings_panel"
+ android:background="#80000080"
>
<ImageView
android:layout_width="match_parent"
@@ -26,7 +27,6 @@
android:scaleType="centerInside"
android:src="@drawable/qs_coming_soon"
android:padding="4dp"
- android:background="#80000080"
/>
<LinearLayout android:id="@+id/handle"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 8eff1f4..f2e83d8 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -29,15 +29,10 @@
android:layout_marginLeft="@dimen/notification_panel_margin_left"
>
- <TextView
- android:id="@+id/carrier_label"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Network"
+ <include
+ layout="@layout/carrier_label"
android:layout_height="@dimen/carrier_label_height"
android:layout_width="match_parent"
- android:layout_gravity="bottom"
- android:layout_marginBottom="@dimen/close_handle_height"
- android:gravity="center"
- android:visibility="invisible"
/>
<LinearLayout
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index ad6b8f4..5e0d1e8 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -40,11 +40,11 @@
>
<include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
/>
<include layout="@layout/quick_settings"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
/>
</com.android.systemui.statusbar.phone.PanelHolder>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 66d65d9..a55091d 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -73,8 +73,8 @@
<string name="screenshot_failed_title" msgid="705781116746922771">"تصویر صفحه گرفته نشد."</string>
<string name="screenshot_failed_text" msgid="8134011269572415402">"تصویر صفحه ذخیره نشد. ممکن است دستگاه ذخیره در حال استفاده باشد."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"گزینههای انتقال فایل USB"</string>
- <string name="use_mtp_button_title" msgid="4333504413563023626">"نصب به عنوان دستگاه پخش رسانه (MTP)"</string>
- <string name="use_ptp_button_title" msgid="7517127540301625751">"تصب به عنوان دوربین (PTP)"</string>
+ <string name="use_mtp_button_title" msgid="4333504413563023626">"نصب بهعنوان دستگاه پخش رسانه (MTP)"</string>
+ <string name="use_ptp_button_title" msgid="7517127540301625751">"تصب بهعنوان دوربین (PTP)"</string>
<string name="installer_cd_button_title" msgid="2312667578562201583">"برنامه Android File Transfer را برای Mac نصب کنید"</string>
<string name="accessibility_back" msgid="567011538994429120">"برگشت"</string>
<string name="accessibility_home" msgid="8217216074895377641">"صفحهٔ اصلی"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index f820464..59730af 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -58,8 +58,8 @@
<string name="label_view" msgid="6304565553218192990">"הצג"</string>
<string name="always_use_device" msgid="1450287437017315906">"השתמש כברירת מחדל עבור מכשיר USB זה"</string>
<string name="always_use_accessory" msgid="1210954576979621596">"השתמש כברירת מחדל עבור אביזר USB זה"</string>
- <string name="usb_debugging_title" msgid="1114766024068112429">"האם לאפשר ניפוי באגים ב-USB?"</string>
- <string name="usb_debugging_message" msgid="719863946976291180">"האם להרשות ניפוי באגים ב-USB ממחשב זה?"\n"טביעת האצבע של מפתח ה-RSA שלך היא"\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_title" msgid="1114766024068112429">"האם לאפשר ניקוי באגים ב-USB?"</string>
+ <string name="usb_debugging_message" msgid="719863946976291180">"האם להרשות ניקוי באגים ב-USB ממחשב זה?"\n"טביעת האצבע של מפתח ה-RSA שלך היא"\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="4253099426793114693">"הרשה תמיד במחשב זה"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"הגדל תצוגה כדי למלא את המסך"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"מתח כדי למלא את המסך"</string>
diff --git a/packages/SystemUI/res/values-sw720dp/config.xml b/packages/SystemUI/res/values-sw720dp/config.xml
index de63d9f..bf01a8d 100644
--- a/packages/SystemUI/res/values-sw720dp/config.xml
+++ b/packages/SystemUI/res/values-sw720dp/config.xml
@@ -29,5 +29,8 @@
<!-- Whether recents thumbnails should stretch in both x and y to fill their
ImageView -->
<bool name="config_recents_thumbnail_image_fits_to_xy">true</bool>
+
+ <!-- Min alpha % that recent items will fade to while being dismissed -->
+ <integer name="config_recent_item_min_alpha">0</integer>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 13622e6..34e58a3 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -78,5 +78,8 @@
<integer name="navigation_bar_deadzone_decay">333</integer>
<bool name="config_dead_zone_flash">false</bool>
+
+ <!-- Min alpha % that recent items will fade to while being dismissed -->
+ <integer name="config_recent_item_min_alpha">3</integer>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5747f22..609d63b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -397,4 +397,7 @@
<!-- Description of the button in the phone-style notification panel that controls auto-rotation, when auto-rotation is off. [CHAR LIMIT=NONE] -->
<string name="accessibility_rotation_lock_on_portrait">Screen is locked in portrait orientation.</string>
+
+ <!-- Name of the Jelly Bean platlogo screensaver -->
+ <string name="jelly_bean_dream_name">BeanFlinger</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/BeanBag.java b/packages/SystemUI/src/com/android/systemui/BeanBag.java
index 616d72f..f5a90ca 100644
--- a/packages/SystemUI/src/com/android/systemui/BeanBag.java
+++ b/packages/SystemUI/src/com/android/systemui/BeanBag.java
@@ -24,6 +24,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.Bitmap;
@@ -40,6 +41,7 @@
import android.graphics.RectF;
import android.os.Handler;
import android.os.SystemClock;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Pair;
@@ -402,6 +404,11 @@
public void onStart() {
super.onStart();
+ // ACHIEVEMENT UNLOCKED
+ PackageManager pm = getPackageManager();
+ pm.setComponentEnabledSetting(new ComponentName(this, BeanBagDream.class),
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 2512c02..0671e44 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -30,10 +30,8 @@
import android.renderscript.Matrix4f;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
-import android.view.Display;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
-import android.view.WindowManager;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index 2a225d9..6d84350 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -30,7 +30,6 @@
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Process;
-import android.os.UserHandle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
@@ -254,10 +253,9 @@
public boolean onTouch(View v, MotionEvent ev) {
int action = ev.getAction() & MotionEvent.ACTION_MASK;
if (action == MotionEvent.ACTION_DOWN) {
- mHandler.post(mPreloadTasksRunnable);
+ preloadRecentTasksList();
} else if (action == MotionEvent.ACTION_CANCEL) {
- cancelLoadingThumbnailsAndIcons();
- mHandler.removeCallbacks(mPreloadTasksRunnable);
+ cancelPreloadingRecentTasksList();
} else if (action == MotionEvent.ACTION_UP) {
// Remove the preloader if we haven't called it yet
mHandler.removeCallbacks(mPreloadTasksRunnable);
@@ -269,6 +267,15 @@
return false;
}
+ public void preloadRecentTasksList() {
+ mHandler.post(mPreloadTasksRunnable);
+ }
+
+ public void cancelPreloadingRecentTasksList() {
+ cancelLoadingThumbnailsAndIcons();
+ mHandler.removeCallbacks(mPreloadTasksRunnable);
+ }
+
public void cancelLoadingThumbnailsAndIcons(RecentsPanelView caller) {
// Only oblige this request if it comes from the current RecentsPanel
// (eg when you rotate, the old RecentsPanel request should be ignored)
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
index a4c8e64..baacde01 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
@@ -136,6 +136,8 @@
mRecentsPanel.setOnTouchListener(new TouchOutsideListener(mRecentsPanel));
mRecentsPanel.setRecentTasksLoader(recentTasksLoader);
recentTasksLoader.setRecentsPanel(mRecentsPanel, mRecentsPanel);
+ mRecentsPanel.setMinSwipeAlpha(
+ getResources().getInteger(R.integer.config_recent_item_min_alpha) / 100f);
handleIntent(getIntent());
mIntentFilter = new IntentFilter();
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index c3ecdb5..730d350 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -38,15 +38,11 @@
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.Display;
-import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
@@ -60,7 +56,6 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.tablet.StatusBarPanel;
import com.android.systemui.statusbar.tablet.TabletStatusBar;
@@ -72,7 +67,6 @@
static final String TAG = "RecentsPanelView";
static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
private Context mContext;
- private BaseStatusBar mBar;
private PopupMenu mPopup;
private View mRecentsScrim;
private View mRecentsNoApps;
@@ -164,13 +158,6 @@
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = createView(parent);
- if (convertView.getParent() != null) {
- throw new RuntimeException("Recycled child has parent");
- }
- } else {
- if (convertView.getParent() != null) {
- throw new RuntimeException("Recycled child has parent");
- }
}
ViewHolder holder = (ViewHolder) convertView.getTag();
@@ -366,11 +353,6 @@
return mShowing;
}
- public void setBar(BaseStatusBar bar) {
- mBar = bar;
-
- }
-
public void setStatusBarView(View statusBarView) {
if (mStatusBarTouchProxy != null) {
mStatusBarTouchProxy.setStatusBar(statusBarView);
@@ -600,6 +582,9 @@
usingDrawingCache = true;
}
+ if (bm == null) {
+ throw new RuntimeException("Recents thumbnail is null");
+ }
ActivityOptions opts = ActivityOptions.makeThumbnailScaleUpAnimation(
holder.thumbnailViewImage, bm, 0, 0, null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 106ce7e..97034bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -488,11 +488,18 @@
.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_width);
float thumbHeight = res
.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height);
+ if (first == null) {
+ throw new RuntimeException("Recents thumbnail is null");
+ }
if (first.getWidth() != thumbWidth || first.getHeight() != thumbHeight) {
first = Bitmap.createScaledBitmap(first, (int) thumbWidth, (int) thumbHeight,
true);
+ if (first == null) {
+ throw new RuntimeException("Recents thumbnail is null");
+ }
}
+
DisplayMetrics dm = new DisplayMetrics();
mDisplay.getMetrics(dm);
// calculate it here, but consider moving it elsewhere
@@ -521,8 +528,7 @@
+ thumbBgPadding + thumbLeftMargin);
y = (int) (dm.heightPixels
- res.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height) - thumbBgPadding);
- } else { // if (config.orientation ==
- // Configuration.ORIENTATION_LANDSCAPE) {
+ } else { // if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
float thumbTopMargin = res
.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_top_margin);
float thumbBgPadding = res
@@ -588,25 +594,11 @@
break;
case MSG_PRELOAD_RECENT_APPS:
if (DEBUG) Slog.d(TAG, "preloading recents");
- {
- // TODO:
- // need to implement this
- //final RecentsPanelView recentsPanel = getRecentsPanel();
- //if (recentsPanel != null) {
- //recentsPanel.preloadRecentTasksList();
- //}
- }
+ getRecentTasksLoader().preloadRecentTasksList();
break;
case MSG_CANCEL_PRELOAD_RECENT_APPS:
if (DEBUG) Slog.d(TAG, "cancel preloading recents");
- {
- // TODO:
- // need to implement this
- //final RecentsPanelView recentsPanel = getRecentsPanel();
- //if (recentsPanel != null) {
- //recentsPanel.clearRecentTasksList();
- //}
- }
+ getRecentTasksLoader().cancelPreloadingRecentTasksList();
break;
case MSG_OPEN_SEARCH_PANEL:
if (DEBUG) Slog.d(TAG, "opening search panel");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 33f467f..2f551e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -5,7 +5,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.util.AttributeSet;
-import android.util.Log;
+import android.util.Slog;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -18,7 +18,7 @@
public static final String TAG = PanelView.class.getSimpleName();
public final void LOG(String fmt, Object... args) {
if (!DEBUG) return;
- Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
+ Slog.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
}
public static final boolean BRAKES = false;
@@ -175,6 +175,12 @@
event.offsetLocation(-deltaX, -deltaY);
}
+ // Pass all touches along to the handle, allowing the user to drag the panel closed from its interior
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return mHandleView.dispatchTouchEvent(event);
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 41541d6..5646c55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -109,8 +109,6 @@
public static final String ACTION_STATUSBAR_START
= "com.android.internal.policy.statusbar.START";
- private static final boolean SHOW_CARRIER_LABEL = false; // XXX: doesn't work with rubberband panels right now
-
private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
private static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
// 1020-1030 reserved for BaseStatusBar
@@ -189,6 +187,9 @@
private boolean mCarrierLabelVisible = false;
private int mCarrierLabelHeight;
private TextView mEmergencyCallLabel;
+ private int mNotificationHeaderHeight;
+
+ private boolean mShowCarrierInPanel = false;
// position
int[] mPositionTmp = new int[2];
@@ -310,14 +311,6 @@
mStatusBarView.setPanelHolder(holder);
mNotificationPanel = (PanelView) mStatusBarWindow.findViewById(R.id.notification_panel);
- // don't allow clicks on the panel to pass through to the background where they will cause the panel to close
- View.OnTouchListener clickStopper = new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- return true;
- }
- };
- mNotificationPanel.setOnTouchListener(clickStopper);
mNotificationPanelIsFullScreenWidth =
(mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
mNotificationPanel.setSystemUiVisibility(
@@ -326,7 +319,6 @@
// quick settings (WIP)
mSettingsPanel = (PanelView) mStatusBarWindow.findViewById(R.id.settings_panel);
- mSettingsPanel.setOnTouchListener(clickStopper);
if (!ActivityManager.isHighEndGfx()) {
mStatusBarWindow.setBackground(null);
@@ -419,8 +411,10 @@
}});
}
- if (SHOW_CARRIER_LABEL) {
- mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
+ mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
+ mShowCarrierInPanel = (mCarrierLabel != null);
+ Slog.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel);
+ if (mShowCarrierInPanel) {
mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
// for mobile devices, we always show mobile connection info here (SPN/PLMN)
@@ -510,19 +504,6 @@
return lp;
}
- /*
- protected void updateRecentsPanel() {
- super.updateRecentsPanel(R.layout.status_bar_recent_panel);
- // Make .03 alpha the minimum so you always see the item a bit-- slightly below
- // .03, the item disappears entirely (as if alpha = 0) and that discontinuity looks
- // a bit jarring
- mRecentsPanel.setMinSwipeAlpha(0.03f);
- if (mNavigationBarView != null) {
- mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPanel);
- }
- }
- */
-
@Override
protected void updateSearchPanel() {
super.updateSearchPanel();
@@ -884,7 +865,7 @@
}
protected void updateCarrierLabelVisibility(boolean force) {
- if (!SHOW_CARRIER_LABEL) return;
+ if (!mShowCarrierInPanel) return;
// The idea here is to only show the carrier label when there is enough room to see it,
// i.e. when there aren't enough notifications to fill the panel.
if (DEBUG) {
@@ -895,7 +876,7 @@
final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null;
final boolean makeVisible =
!(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
- && mPile.getHeight() < (mScrollView.getHeight() - mCarrierLabelHeight);
+ && mPile.getHeight() < (mNotificationPanel.getHeight() - mCarrierLabelHeight - mNotificationHeaderHeight);
if (force || mCarrierLabelVisible != makeVisible) {
mCarrierLabelVisible = makeVisible;
@@ -1648,6 +1629,8 @@
lp.gravity = mSettingsPanelGravity;
lp.rightMargin = mNotificationPanelMarginPx;
mSettingsPanel.setLayoutParams(lp);
+
+ updateCarrierLabelVisibility(false);
}
// called by makeStatusbar and also by PhoneStatusBarView
@@ -1922,6 +1905,7 @@
+ res.getDimensionPixelSize(R.dimen.close_handle_underlap);
mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
+ mNotificationHeaderHeight = res.getDimensionPixelSize(R.dimen.notification_panel_header_height);
if (false) Slog.v(TAG, "updateResources");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 8ca3a9c..7153ec7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -355,7 +355,7 @@
mWindowManager.addView(mCompatModePanel, lp);
- //mRecentButton.setOnTouchListener(mRecentsPanel); //TODO: plumb this
+ mRecentButton.setOnTouchListener(getRecentTasksLoader());
mPile = (NotificationRowLayout)mNotificationPanel.findViewById(R.id.content);
mPile.removeAllViews();
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 3e96f9bc..e761847 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -2841,7 +2841,7 @@
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
mDecor.setLayoutDirection(
- getContext().getResources().getConfiguration().layoutDirection);
+ getContext().getResources().getConfiguration().getLayoutDirection());
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index 2551c04e..e0ba211 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -172,7 +172,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mAppWidgetHost.startListening();
- populateWidgets();
+ maybePopulateWidgets();
}
@Override
@@ -581,7 +581,12 @@
addWidget(view);
}
- private void populateWidgets() {
+ private void maybePopulateWidgets() {
+ if (mLockPatternUtils.getDevicePolicyManager().getKeyguardWidgetsDisabled(null)
+ != DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_NONE) {
+ Log.v(TAG, "Keyguard widgets disabled because of device policy admin");
+ return;
+ }
SharedPreferences prefs = mContext.getSharedPreferences(
KEYGUARD_WIDGET_PREFS, Context.MODE_PRIVATE);
for (String key : prefs.getAll().keySet()) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
index 6938561..b2ce73e 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
@@ -256,12 +256,10 @@
private void verifyPasswordAndUnlock() {
String entry = mPasswordEntry.getText().toString();
- boolean wrongPassword = true;
if (mLockPatternUtils.checkPassword(entry)) {
mCallback.reportSuccessfulUnlockAttempt();
KeyStore.getInstance().password(entry);
mCallback.dismiss(true);
- wrongPassword = false;
} else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
// to avoid accidental lockout, only count attempts that are long enough to be a
// real password. This may require some tweaking.
@@ -271,9 +269,9 @@
long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
handleAttemptLockout(deadline);
}
+ mNavigationManager.setMessage(
+ mIsAlpha ? R.string.kg_wrong_password : R.string.kg_wrong_pin);
}
- mNavigationManager.setMessage(wrongPassword ?
- (mIsAlpha ? R.string.kg_wrong_password : R.string.kg_wrong_pin) : 0);
mPasswordEntry.setText("");
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
index 294ea5c..4861b78 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
@@ -91,8 +91,6 @@
}
});
}
-
- setFocusableInTouchMode(true);
reset();
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
index 06ed88a..20fad0b 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
@@ -119,7 +119,7 @@
mDateView = (TextView) findViewById(R.id.date);
mStatus1View = (TextView) findViewById(R.id.status1);
mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
- mOwnerInfoView = (TextView) findViewById(R.id.propertyOf);
+ mOwnerInfoView = (TextView) findViewById(R.id.owner_info);
mDigitalClock = (DigitalClock) findViewById(R.id.time);
// Registering this callback immediately updates the battery state, among other things.
diff --git a/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java
index 409f87b..b6ffde0 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java
@@ -179,7 +179,7 @@
mDateView = (TextView) findViewById(R.id.date);
mStatus1View = (TextView) findViewById(R.id.status1);
mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
- mOwnerInfoView = (TextView) findViewById(R.id.propertyOf);
+ mOwnerInfoView = (TextView) findViewById(R.id.owner_info);
mTransportView = (TransportControlView) findViewById(R.id.transport);
mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton);
mEmergencyCallButtonEnabledInScreen = emergencyButtonEnabledInScreen;
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index b869abd..343e70d 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -27,7 +27,6 @@
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
-import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -151,13 +150,13 @@
// Register for the boot completed broadcast, so we can send the
// ENABLE broacasts. If we try to send them now, they time out,
// because the system isn't ready to handle them yet.
- mContext.registerReceiver(mBroadcastReceiver,
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
// Register for configuration changes so we can update the names
// of the widgets when the locale changes.
- mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(
- Intent.ACTION_CONFIGURATION_CHANGED), null, null);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+ new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
// Register for broadcasts about package install, etc., so we can
// update the provider list.
@@ -166,12 +165,14 @@
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
- mContext.registerReceiver(mBroadcastReceiver, filter);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+ filter, null, null);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+ sdFilter, null, null);
IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_REMOVED);
@@ -181,6 +182,15 @@
onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
}
}, userFilter);
+
+ IntentFilter userStopFilter = new IntentFilter();
+ userStopFilter.addAction(Intent.ACTION_USER_STOPPED);
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserStopped(getSendingUserId());
+ }
+ }, UserHandle.ALL, userFilter, null, null);
}
@Override
@@ -254,6 +264,9 @@
}
}
+ public void onUserStopped(int userId) {
+ }
+
private AppWidgetServiceImpl getImplForUser(int userId) {
AppWidgetServiceImpl service = mAppWidgetServices.get(userId);
if (service == null) {
@@ -370,9 +383,17 @@
service.onConfigurationChanged();
}
} else {
- for (int i = 0; i < mAppWidgetServices.size(); i++) {
- AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
- service.onBroadcastReceived(intent);
+ int sendingUser = getSendingUserId();
+ if (sendingUser == UserHandle.USER_ALL) {
+ for (int i = 0; i < mAppWidgetServices.size(); i++) {
+ AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
+ service.onBroadcastReceived(intent);
+ }
+ } else {
+ AppWidgetServiceImpl service = mAppWidgetServices.get(sendingUser);
+ if (service != null) {
+ service.onBroadcastReceived(intent);
+ }
}
}
}
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index f0989e7..fe8529b 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -116,6 +116,7 @@
private int mLowBatteryWarningLevel;
private int mLowBatteryCloseWarningLevel;
+ private int mShutdownBatteryTemperature;
private int mPlugType;
private int mLastPlugType = -1; // Extra state so we can detect first run
@@ -138,6 +139,8 @@
com.android.internal.R.integer.config_lowBatteryWarningLevel);
mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
+ mShutdownBatteryTemperature = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_shutdownBatteryTemperature);
mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply");
@@ -228,9 +231,11 @@
}
private final void shutdownIfOverTemp() {
- // shut down gracefully if temperature is too high (> 68.0C)
- // wait until the system has booted before attempting to display the shutdown dialog.
- if (mBatteryTemperature > 680 && ActivityManagerNative.isSystemReady()) {
+ // shut down gracefully if temperature is too high (> 68.0C by default)
+ // wait until the system has booted before attempting to display the
+ // shutdown dialog.
+ if (mBatteryTemperature > mShutdownBatteryTemperature
+ && ActivityManagerNative.isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -259,7 +264,7 @@
} else {
mPlugType = BATTERY_PLUGGED_NONE;
}
-
+
// Let the battery stats keep track of the current level.
try {
mBatteryStats.setBatteryState(mBatteryStatus, mBatteryHealth,
@@ -268,7 +273,7 @@
} catch (RemoteException e) {
// Should never happen.
}
-
+
shutdownIfNoPower();
shutdownIfOverTemp();
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 04991bb..cbbfda1 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -1852,8 +1852,13 @@
Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE);
intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType);
intent.putExtra(ConnectivityManager.EXTRA_IS_ACTIVE, active);
- mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
- RECEIVE_DATA_ACTIVITY_CHANGE, null, null, 0, null, null);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
+ RECEIVE_DATA_ACTIVITY_CHANGE, null, null, 0, null, null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
/**
@@ -1927,7 +1932,12 @@
log("sendStickyBroadcast: action=" + intent.getAction());
}
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
@@ -2467,7 +2477,12 @@
* Connectivity events can happen before boot has completed ...
*/
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
// Caller must grab mDnsLock.
@@ -3112,7 +3127,12 @@
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy);
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
private static class SettingsObserver extends ContentObserver {
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 77b062c..61517b1 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -177,6 +177,9 @@
static final long DEF_PASSWORD_EXPIRATION_DATE = 0;
long passwordExpirationDate = DEF_PASSWORD_EXPIRATION_DATE;
+ static final int DEF_KEYGUARD_WIDGET_DISABLED = 0; // none
+ int disableKeyguardWidgets = DEF_KEYGUARD_WIDGET_DISABLED;
+
boolean encryptionRequested = false;
boolean disableCamera = false;
@@ -286,6 +289,11 @@
out.attribute(null, "value", Boolean.toString(disableCamera));
out.endTag(null, "disable-camera");
}
+ if (disableKeyguardWidgets != DEF_KEYGUARD_WIDGET_DISABLED) {
+ out.startTag(null, "disable-keyguard-widgets");
+ out.attribute(null, "value", Integer.toString(disableKeyguardWidgets));
+ out.endTag(null, "disable-keyguard-widgets");
+ }
}
void readFromXml(XmlPullParser parser)
@@ -2093,6 +2101,46 @@
}
}
+ /**
+ * Selectively disable keyguard widgets.
+ */
+ public void setKeyguardWidgetsDisabled(ComponentName who, int which) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_WIDGETS);
+ if ((ap.disableKeyguardWidgets & which) != which) {
+ ap.disableKeyguardWidgets |= which;
+ saveSettingsLocked();
+ }
+ syncDeviceCapabilitiesLocked();
+ }
+ }
+
+ /**
+ * Gets the disabled state for widgets in keyguard for the given admin,
+ * or the aggregate of all active admins if who is null.
+ */
+ public int getKeyguardWidgetsDisabled(ComponentName who) {
+ synchronized (this) {
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+ return (admin != null) ? admin.disableKeyguardWidgets : 0;
+ }
+
+ // Determine whether or not keyguard widgets are disabled for any active admins.
+ final int N = mAdminList.size();
+ int which = 0;
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ which |= admin.disableKeyguardWidgets;
+ }
+ return which;
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 747cf0b..c685473 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -3579,7 +3579,7 @@
private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
private final AtomicFile mAdditionalInputMethodSubtypeFile;
private final HashMap<String, InputMethodInfo> mMethodMap;
- private final HashMap<String, List<InputMethodSubtype>> mSubtypesMap =
+ private final HashMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
new HashMap<String, List<InputMethodSubtype>>();
public InputMethodFileManager(HashMap<String, InputMethodInfo> methodMap) {
if (methodMap == null) {
@@ -3595,18 +3595,19 @@
mAdditionalInputMethodSubtypeFile = new AtomicFile(subtypeFile);
if (!subtypeFile.exists()) {
// If "subtypes.xml" doesn't exist, create a blank file.
- writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile,
- methodMap);
+ writeAdditionalInputMethodSubtypes(
+ mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, methodMap);
} else {
- readAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile);
+ readAdditionalInputMethodSubtypes(
+ mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile);
}
}
private void deleteAllInputMethodSubtypes(String imiId) {
synchronized (mMethodMap) {
- mSubtypesMap.remove(imiId);
- writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile,
- mMethodMap);
+ mAdditionalSubtypesMap.remove(imiId);
+ writeAdditionalInputMethodSubtypes(
+ mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap);
}
}
@@ -3619,17 +3620,20 @@
final InputMethodSubtype subtype = additionalSubtypes[i];
if (!subtypes.contains(subtype)) {
subtypes.add(subtype);
+ } else {
+ Slog.w(TAG, "Duplicated subtype definition found: "
+ + subtype.getLocale() + ", " + subtype.getMode());
}
}
- mSubtypesMap.put(imi.getId(), subtypes);
- writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile,
- mMethodMap);
+ mAdditionalSubtypesMap.put(imi.getId(), subtypes);
+ writeAdditionalInputMethodSubtypes(
+ mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap);
}
}
public HashMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() {
synchronized (mMethodMap) {
- return mSubtypesMap;
+ return mAdditionalSubtypesMap;
}
}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 5993f32..37dae35 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.database.Cursor;
import android.location.Address;
import android.location.Criteria;
@@ -87,7 +88,7 @@
* The service class that manages LocationProviders and issues location
* updates and alerts.
*/
-public class LocationManagerService extends ILocationManager.Stub implements Observer, Runnable {
+public class LocationManagerService extends ILocationManager.Stub implements Runnable {
private static final String TAG = "LocationManagerService";
public static final boolean D = false;
@@ -207,24 +208,30 @@
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
mPackageManager = mContext.getPackageManager();
+ mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
+ mBlacklist.init();
+ mLocationFudger = new LocationFudger();
+
synchronized (mLock) {
loadProvidersLocked();
}
- mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
- mBlacklist.init();
+
mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
- mLocationFudger = new LocationFudger();
// listen for settings changes
- ContentResolver resolver = mContext.getContentResolver();
- Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
- "(" + NameValueTable.NAME + "=?)",
- new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED}, null);
- ContentQueryMap query = new ContentQueryMap(settingsCursor, NameValueTable.NAME, true,
- mLocationHandler);
- settingsCursor.close();
- query.addObserver(this);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true,
+ new ContentObserver(mLocationHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mLock) {
+ updateProvidersLocked();
+ }
+ }
+ });
mPackageMonitor.register(mContext, Looper.myLooper(), true);
+
+ updateProvidersLocked();
}
private void loadProvidersLocked() {
@@ -299,8 +306,6 @@
if (mGeocodeProvider == null) {
Slog.e(TAG, "no geocoder provider found");
}
-
- updateProvidersLocked();
}
/**
@@ -544,14 +549,6 @@
}
}
- /** Settings Observer callback */
- @Override
- public void update(Observable o, Object arg) {
- synchronized (mLock) {
- updateProvidersLocked();
- }
- }
-
private void addProviderLocked(LocationProviderInterface provider) {
mProviders.add(provider);
mProvidersByName.put(provider.getName(), provider);
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index 2c5038e..9edfad6 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -26,7 +26,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.os.Build;
import android.os.Debug;
import android.os.Handler;
import android.os.Message;
@@ -40,8 +39,6 @@
import android.util.Slog;
import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
@@ -432,10 +429,11 @@
}
// If we got here, that means that the system is most likely hung.
+ // First collect stack traces from all threads of the system process.
+ // Then kill this process so that the system will restart.
final String name = (mCurrentMonitor != null) ?
mCurrentMonitor.getClass().getName() : "null";
- Slog.w(TAG, "WATCHDOG PROBLEM IN SYSTEM SERVER: " + name);
EventLog.writeEvent(EventLogTags.WATCHDOG, name);
ArrayList<Integer> pids = new ArrayList<Integer>();
@@ -470,15 +468,11 @@
dropboxThread.join(2000); // wait up to 2 seconds for it to return.
} catch (InterruptedException ignored) {}
- // Only kill/crash the process if the debugger is not attached.
+ // Only kill the process if the debugger is not attached.
if (!Debug.isDebuggerConnected()) {
Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + name);
- if (!Build.TYPE.equals("user")) {
- forceCrashDump();
- } else {
- Process.killProcess(Process.myPid());
- System.exit(10);
- }
+ Process.killProcess(Process.myPid());
+ System.exit(10);
} else {
Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");
}
@@ -487,50 +481,6 @@
}
}
- private void forceCrashDump() {
- /* Sync file system to flash the data which is written just before the
- * crash.
- */
- java.lang.Process p = null;
- try {
- p = Runtime.getRuntime().exec("sync");
- if (p != null) {
- // It is not necessary to check the exit code, here.
- // 'sync' command always succeeds, and this function returns 0.
- p.waitFor();
- } else {
- Slog.e(TAG, "Failed to execute 'sync' command. (no process handle)");
- }
- } catch (Exception e) {
- // This code is an emergency path to crash MUT. The system already
- // caused fatal error, and then calls this function to create a
- // crash dump. This function must run the code below to force a
- // crash, even if the sync command failed.
- // Therefore, ignore all exceptions, here.
- Slog.e(TAG, "Failed to execute 'sync' command prior to forcing crash: " + e);
- } finally {
- if (p != null) {
- p.destroy();
- }
- }
-
- FileWriter out = null;
- try {
- out = new FileWriter("/proc/sysrq-trigger");
- out.write("c");
- } catch (IOException e) {
- Slog.e(TAG, "Failed to write to sysrq-trigger while triggering crash: " + e);
- } finally {
- if (out != null) {
- try {
- out.close();
- } catch (IOException e) {
- Slog.e(TAG, "Failed to close sysrq-trigger while triggering crash: " + e);
- }
- }
- }
- }
-
private File dumpKernelStackTraces() {
String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
if (tracesPath == null || tracesPath.length() == 0) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 590e9d9..614b93a 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -116,6 +116,7 @@
import android.provider.Settings;
import android.text.format.Time;
import android.util.EventLog;
+import android.util.LocaleUtil;
import android.util.Log;
import android.util.Pair;
import android.util.PrintWriterPrinter;
@@ -153,7 +154,6 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
@@ -495,7 +495,8 @@
@Override
protected BroadcastFilter newResult(BroadcastFilter filter, int match, int userId) {
- if (userId == UserHandle.USER_ALL || userId == filter.owningUserId) {
+ if (userId == UserHandle.USER_ALL || filter.owningUserId == UserHandle.USER_ALL
+ || userId == filter.owningUserId) {
return super.newResult(filter, match, userId);
}
return null;
@@ -1530,7 +1531,8 @@
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
mConfiguration.setToDefaults();
- mConfiguration.locale = Locale.getDefault();
+ mConfiguration.setLocale(Locale.getDefault());
+
mConfigurationSeq = mConfiguration.seq = 1;
mProcessStats.init();
@@ -3570,15 +3572,32 @@
public void closeSystemDialogs(String reason) {
enforceNotIsolatedCaller("closeSystemDialogs");
+ final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
- synchronized (this) {
- closeSystemDialogsLocked(uid, reason);
+ try {
+ synchronized (this) {
+ // Only allow this from foreground processes, so that background
+ // applications can't abuse it to prevent system UI from being shown.
+ if (uid >= Process.FIRST_APPLICATION_UID) {
+ ProcessRecord proc;
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pid);
+ }
+ if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+ Slog.w(TAG, "Ignoring closeSystemDialogs " + reason
+ + " from background process " + proc);
+ return;
+ }
+ }
+ closeSystemDialogsLocked(reason);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
- Binder.restoreCallingIdentity(origId);
}
- void closeSystemDialogsLocked(int callingUid, String reason) {
+ void closeSystemDialogsLocked(String reason) {
Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
if (reason != null) {
@@ -3594,14 +3613,9 @@
}
}
- final long origId = Binder.clearCallingIdentity();
- try {
- broadcastIntentLocked(null, null, intent, null,
- null, 0, null, null, null, false, false, -1,
- callingUid, UserHandle.USER_ALL);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ broadcastIntentLocked(null, null, intent, null,
+ null, 0, null, null, null, false, false, -1,
+ Process.SYSTEM_UID, UserHandle.USER_ALL);
}
public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids)
@@ -7549,7 +7563,7 @@
finisher = new IIntentReceiver.Stub() {
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
- boolean sticky) {
+ boolean sticky, int sendingUser) {
// The raw IIntentReceiver interface is called
// with the AM lock held, so redispatch to
// execute our code without the lock.
@@ -10607,7 +10621,9 @@
restart = true;
} else {
removeDyingProviderLocked(app, cpr, true);
+ // cpr should have been removed from mLaunchingProviders
NL = mLaunchingProviders.size();
+ i--;
}
}
}
@@ -11038,9 +11054,10 @@
}
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
- IIntentReceiver receiver, IntentFilter filter, String permission) {
+ IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
enforceNotIsolatedCaller("registerReceiver");
int callingUid;
+ int callingPid;
synchronized(this) {
ProcessRecord callerApp = null;
if (caller != null) {
@@ -11057,11 +11074,16 @@
+ " is not running in process " + callerApp);
}
callingUid = callerApp.info.uid;
+ callingPid = callerApp.pid;
} else {
callerPackage = null;
callingUid = Binder.getCallingUid();
+ callingPid = Binder.getCallingPid();
}
+ userId = this.handleIncomingUserLocked(callingPid, callingUid, userId,
+ true, true, "registerReceiver", callerPackage);
+
List allSticky = null;
// Look for any matching sticky broadcasts...
@@ -11095,9 +11117,8 @@
ReceiverList rl
= (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
- rl = new ReceiverList(this, callerApp,
- Binder.getCallingPid(),
- Binder.getCallingUid(), receiver);
+ rl = new ReceiverList(this, callerApp, callingPid, callingUid,
+ userId, receiver);
if (rl.app != null) {
rl.app.receivers.add(rl);
} else {
@@ -11109,9 +11130,21 @@
rl.linkedToDeath = true;
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
+ } else if (rl.uid != callingUid) {
+ throw new IllegalArgumentException(
+ "Receiver requested to register for uid " + callingUid
+ + " was previously registered for uid " + rl.uid);
+ } else if (rl.pid != callingPid) {
+ throw new IllegalArgumentException(
+ "Receiver requested to register for pid " + callingPid
+ + " was previously registered for pid " + rl.pid);
+ } else if (rl.userId != userId) {
+ throw new IllegalArgumentException(
+ "Receiver requested to register for user " + userId
+ + " was previously registered for user " + rl.userId);
}
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
- permission, callingUid);
+ permission, callingUid, userId);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadast");
@@ -13834,7 +13867,7 @@
final IIntentReceiver resultReceiver = new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode, String data,
- Bundle extras, boolean ordered, boolean sticky) {
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
finishUserStop(uss);
}
};
diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java
index b0ccd8f..07440b5 100644
--- a/services/java/com/android/server/am/BroadcastFilter.java
+++ b/services/java/com/android/server/am/BroadcastFilter.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import android.content.IntentFilter;
-import android.os.UserHandle;
import android.util.PrintWriterPrinter;
import android.util.Printer;
@@ -32,13 +31,13 @@
final int owningUserId;
BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
- String _packageName, String _requiredPermission, int _owningUid) {
+ String _packageName, String _requiredPermission, int _owningUid, int _userId) {
super(_filter);
receiverList = _receiverList;
packageName = _packageName;
requiredPermission = _requiredPermission;
owningUid = _owningUid;
- owningUserId = UserHandle.getUserId(owningUid);
+ owningUserId = _userId;
}
public void dump(PrintWriter pw, String prefix) {
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index 34dec3a..1b6ff7a 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -222,7 +222,7 @@
mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
- r.resultCode, r.resultData, r.resultExtras, r.ordered);
+ r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId);
if (DEBUG_BROADCAST) Slog.v(TAG,
"Process cur broadcast " + r + " DELIVERED for app " + app);
started = true;
@@ -357,15 +357,16 @@
private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
- boolean ordered, boolean sticky) throws RemoteException {
+ boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null && app.thread != null) {
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
- data, extras, ordered, sticky);
+ data, extras, ordered, sticky, sendingUser);
} else {
- receiver.performReceive(intent, resultCode, data, extras, ordered, sticky);
+ receiver.performReceive(intent, resultCode, data, extras, ordered,
+ sticky, sendingUser);
}
}
@@ -428,8 +429,8 @@
+ " (seq=" + seq + "): " + r);
}
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
- new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, r.ordered, r.initialSticky);
+ new Intent(r.intent), r.resultCode, r.resultData,
+ r.resultExtras, r.ordered, r.initialSticky, r.userId);
if (ordered) {
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
}
@@ -579,7 +580,7 @@
}
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, false, false);
+ r.resultData, r.resultExtras, false, false, r.userId);
// Set this to null so that the reference
// (local and remote) isnt kept in the mBroadcastHistory.
r.resultTo = null;
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index 37e9017..0f72409 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -285,7 +285,7 @@
if (sendFinish) {
try {
finishedReceiver.performReceive(new Intent(finalIntent), 0,
- null, null, false, false);
+ null, null, false, false, key.userId);
} catch (RemoteException e) {
}
}
diff --git a/services/java/com/android/server/am/ReceiverList.java b/services/java/com/android/server/am/ReceiverList.java
index 32c24c6..9b6701e 100644
--- a/services/java/com/android/server/am/ReceiverList.java
+++ b/services/java/com/android/server/am/ReceiverList.java
@@ -39,18 +39,20 @@
public final ProcessRecord app;
public final int pid;
public final int uid;
+ public final int userId;
BroadcastRecord curBroadcast = null;
boolean linkedToDeath = false;
String stringName;
ReceiverList(ActivityManagerService _owner, ProcessRecord _app,
- int _pid, int _uid, IIntentReceiver _receiver) {
+ int _pid, int _uid, int _userId, IIntentReceiver _receiver) {
owner = _owner;
receiver = _receiver;
app = _app;
pid = _pid;
uid = _uid;
+ userId = _userId;
}
// Want object identity, not the array identity we are inheriting.
@@ -67,8 +69,9 @@
}
void dumpLocal(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("app="); pw.print(app);
- pw.print(" pid="); pw.print(pid); pw.print(" uid="); pw.println(uid);
+ pw.print(prefix); pw.print("app="); pw.print(app.toShortString());
+ pw.print(" pid="); pw.print(pid); pw.print(" uid="); pw.print(uid);
+ pw.print(" user="); pw.println(userId);
if (curBroadcast != null || linkedToDeath) {
pw.print(prefix); pw.print("curBroadcast="); pw.print(curBroadcast);
pw.print(" linkedToDeath="); pw.println(linkedToDeath);
@@ -103,6 +106,8 @@
sb.append((app != null ? app.processName : "(unknown name)"));
sb.append('/');
sb.append(uid);
+ sb.append("/u");
+ sb.append(userId);
sb.append((receiver.asBinder() instanceof Binder) ? " local:" : " remote:");
sb.append(Integer.toHexString(System.identityHashCode(receiver.asBinder())));
sb.append('}');
diff --git a/services/java/com/android/server/display/DisplayDevice.java b/services/java/com/android/server/display/DisplayDevice.java
index 69d8385..bdc87f9 100644
--- a/services/java/com/android/server/display/DisplayDevice.java
+++ b/services/java/com/android/server/display/DisplayDevice.java
@@ -38,8 +38,8 @@
// the display manager service. The display device shouldn't really be looking at these.
private int mCurrentLayerStack = -1;
private int mCurrentOrientation = -1;
- private Rect mCurrentViewport;
- private Rect mCurrentFrame;
+ private Rect mCurrentLayerStackRect;
+ private Rect mCurrentDisplayRect;
// The display device does own its surface texture, but it should only set it
// within a transaction from performTraversalInTransactionLocked.
@@ -117,44 +117,26 @@
}
/**
- * Sets the display orientation while in a transaction.
+ * Sets the display projection while in a transaction.
+ *
+ * @param orientation defines the display's orientation
+ * @param layerStackRect defines which area of the window manager coordinate
+ * space will be used
+ * @param displayRect defines where on the display will layerStackRect be
+ * mapped to. displayRect is specified post-orientation, that is
+ * it uses the orientation seen by the end-user
*/
- public final void setOrientationInTransactionLocked(int orientation) {
- if (mCurrentOrientation == orientation) {
- return;
- }
+ public final void setProjectionInTransactionLocked(int orientation, Rect layerStackRect, Rect displayRect) {
mCurrentOrientation = orientation;
- Surface.setDisplayOrientation(mDisplayToken, orientation);
- }
-
- /**
- * Sets the display viewport while in a transaction.
- */
- public final void setViewportInTransactionLocked(Rect viewport) {
- if (mCurrentViewport != null) {
- if (mCurrentViewport.equals(viewport)) {
- return;
- }
- } else {
- mCurrentViewport = new Rect();
+ if (mCurrentLayerStackRect == null) {
+ mCurrentLayerStackRect = new Rect();
}
- mCurrentViewport.set(viewport);
- Surface.setDisplayViewport(mDisplayToken, viewport);
- }
-
- /**
- * Sets the display frame while in a transaction.
- */
- public final void setFrameInTransactionLocked(Rect frame) {
- if (mCurrentFrame != null) {
- if (mCurrentFrame.equals(frame)) {
- return;
- }
- } else {
- mCurrentFrame = new Rect();
+ mCurrentLayerStackRect.set(layerStackRect);
+ if (mCurrentDisplayRect == null) {
+ mCurrentDisplayRect = new Rect();
}
- mCurrentFrame.set(frame);
- Surface.setDisplayFrame(mDisplayToken, frame);
+ mCurrentDisplayRect.set(displayRect);
+ Surface.setDisplayProjection(mDisplayToken, orientation, layerStackRect, displayRect);
}
/**
@@ -176,8 +158,8 @@
pw.println("mAdapter=" + mDisplayAdapter.getName());
pw.println("mCurrentLayerStack=" + mCurrentLayerStack);
pw.println("mCurrentOrientation=" + mCurrentOrientation);
- pw.println("mCurrentViewport=" + mCurrentViewport);
- pw.println("mCurrentFrame=" + mCurrentFrame);
+ pw.println("mCurrentViewport=" + mCurrentLayerStackRect);
+ pw.println("mCurrentFrame=" + mCurrentDisplayRect);
pw.println("mCurrentSurfaceTexture=" + mCurrentSurfaceTexture);
}
}
diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java
index 32ca1c5..c864189 100644
--- a/services/java/com/android/server/display/LogicalDisplay.java
+++ b/services/java/com/android/server/display/LogicalDisplay.java
@@ -64,7 +64,8 @@
private DisplayDeviceInfo mPrimaryDisplayDeviceInfo;
// Temporary rectangle used when needed.
- private final Rect mTempRect = new Rect();
+ private final Rect mTempLayerStackRect = new Rect();
+ private final Rect mTempDisplayRect = new Rect();
public LogicalDisplay(int layerStack, DisplayDevice primaryDisplayDevice) {
mLayerStack = layerStack;
@@ -208,8 +209,7 @@
// Set the viewport.
// This is the area of the logical display that we intend to show on the
// display device. For now, it is always the full size of the logical display.
- mTempRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
- device.setViewportInTransactionLocked(mTempRect);
+ mTempLayerStackRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
// Set the orientation.
// The orientation specifies how the physical coordinate system of the display
@@ -219,7 +219,6 @@
&& (displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_SUPPORTS_ROTATION) != 0) {
orientation = displayInfo.rotation;
}
- device.setOrientationInTransactionLocked(orientation);
// Set the frame.
// The frame specifies the rotated physical coordinates into which the viewport
@@ -238,21 +237,23 @@
// We avoid a division (and possible floating point imprecision) here by
// multiplying the fractions by the product of their denominators before
// comparing them.
- int frameWidth, frameHeight;
+ int displayRectWidth, displayRectHeight;
if (physWidth * displayInfo.logicalHeight
< physHeight * displayInfo.logicalWidth) {
// Letter box.
- frameWidth = physWidth;
- frameHeight = displayInfo.logicalHeight * physWidth / displayInfo.logicalWidth;
+ displayRectWidth = physWidth;
+ displayRectHeight = displayInfo.logicalHeight * physWidth / displayInfo.logicalWidth;
} else {
// Pillar box.
- frameWidth = displayInfo.logicalWidth * physHeight / displayInfo.logicalHeight;
- frameHeight = physHeight;
+ displayRectWidth = displayInfo.logicalWidth * physHeight / displayInfo.logicalHeight;
+ displayRectHeight = physHeight;
}
- int frameTop = (physHeight - frameHeight) / 2;
- int frameLeft = (physWidth - frameWidth) / 2;
- mTempRect.set(frameLeft, frameTop, frameLeft + frameWidth, frameTop + frameHeight);
- device.setFrameInTransactionLocked(mTempRect);
+ int displayRectTop = (physHeight - displayRectHeight) / 2;
+ int displayRectLeft = (physWidth - displayRectWidth) / 2;
+ mTempDisplayRect.set(displayRectLeft, displayRectTop,
+ displayRectLeft + displayRectWidth, displayRectTop + displayRectHeight);
+
+ device.setProjectionInTransactionLocked(orientation, mTempLayerStackRect, mTempDisplayRect);
}
public void dumpLocked(PrintWriter pw) {
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 183f8ec..55da11f 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -208,8 +208,7 @@
/**
* Whether verification is enabled by default.
*/
- // STOPSHIP: change this to true
- private static final boolean DEFAULT_VERIFY_ENABLE = false;
+ private static final boolean DEFAULT_VERIFY_ENABLE = true;
/**
* The default maximum time to wait for the verification agent to return in
@@ -9635,7 +9634,8 @@
if (pkgList.size() > 0) {
sendResourcesChangedBroadcast(false, pkgList, uidArr, new IIntentReceiver.Stub() {
public void performReceive(Intent intent, int resultCode, String data,
- Bundle extras, boolean ordered, boolean sticky) throws RemoteException {
+ Bundle extras, boolean ordered, boolean sticky,
+ int sendingUser) throws RemoteException {
Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
reportStatus ? 1 : 0, 1, keys);
mHandler.sendMessage(msg);
diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java
index c25f010..c6b7e0d 100644
--- a/services/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/java/com/android/server/wm/AppWindowAnimator.java
@@ -234,10 +234,8 @@
return false;
}
- mAnimator.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("AppWindowToken", mAnimator.mPendingLayoutChanges);
- }
+ mAnimator.setAppLayoutChanges(this, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
+ "AppWindowToken");
clearAnimation();
animating = false;
diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java
index afcf339c..87a2880 100644
--- a/services/java/com/android/server/wm/DimAnimator.java
+++ b/services/java/com/android/server/wm/DimAnimator.java
@@ -212,5 +212,12 @@
mDimHeight = dimHeight;
mDimTarget = dimTarget;
}
+
+ Parameters(Parameters o) {
+ mDimWinAnimator = o.mDimWinAnimator;
+ mDimWidth = o.mDimWidth;
+ mDimHeight = o.mDimHeight;
+ mDimTarget = o.mDimTarget;
+ }
}
}
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index 1e1b097..f4964cf 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -64,6 +64,7 @@
// Accessed directly by all users.
boolean layoutNeeded;
+ int pendingLayoutChanges;
DisplayContent(Display display) {
mDisplay = display;
@@ -84,6 +85,7 @@
}
DisplayInfo getDisplayInfo() {
+ mDisplay.getDisplayInfo(mDisplayInfo);
return mDisplayInfo;
}
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 232f2ed..f0b514c 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -16,12 +16,12 @@
import android.os.SystemClock;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseIntArray;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManagerPolicy;
import android.view.animation.Animation;
-import com.android.internal.policy.impl.PhoneWindowManager;
import com.android.server.wm.WindowManagerService.AppWindowAnimParams;
import com.android.server.wm.WindowManagerService.LayoutToAnimatorParams;
@@ -47,7 +47,8 @@
int mAdjResult;
- int mPendingLayoutChanges;
+ // Layout changes for individual Displays. Indexed by displayId.
+ SparseIntArray mPendingLayoutChanges = new SparseIntArray();
/** Overall window dimensions */
int mDw, mDh;
@@ -97,7 +98,7 @@
static class AnimatorToLayoutParams {
boolean mUpdateQueued;
int mBulkUpdateParams;
- int mPendingLayoutChanges;
+ SparseIntArray mPendingLayoutChanges;
WindowState mWindowDetachedWallpaper;
}
/** Do not modify unless holding mService.mWindowMap or this and mAnimToLayout in that order */
@@ -137,7 +138,7 @@
final AnimatorToLayoutParams animToLayout = mAnimToLayout;
synchronized (animToLayout) {
animToLayout.mBulkUpdateParams = mBulkUpdateParams;
- animToLayout.mPendingLayoutChanges = mPendingLayoutChanges;
+ animToLayout.mPendingLayoutChanges = mPendingLayoutChanges.clone();
animToLayout.mWindowDetachedWallpaper = mWindowDetachedWallpaper;
if (!animToLayout.mUpdateQueued) {
@@ -175,7 +176,7 @@
// Set the new DimAnimator params.
DimAnimator.Parameters dimParams = layoutToAnim.mDimParams;
if (dimParams == null) {
- mDimParams = dimParams;
+ mDimParams = null;
} else {
final WindowStateAnimator newWinAnimator = dimParams.mDimWinAnimator;
@@ -186,7 +187,7 @@
if (newWinAnimator.mSurfaceShown &&
(existingDimWinAnimator == null || !existingDimWinAnimator.mSurfaceShown
|| existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
- mDimParams = dimParams;
+ mDimParams = new DimAnimator.Parameters(dimParams);
}
}
@@ -214,7 +215,8 @@
if (!winAnimator.mLastHidden) {
winAnimator.hide();
mService.dispatchWallpaperVisibility(wallpaper, false);
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
}
}
token.hidden = true;
@@ -233,11 +235,8 @@
mAnimating = true;
} else if (wasAnimating) {
// stopped animating, do one more pass through the layout
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("appToken " + appAnimator.mAppToken + " done",
- mPendingLayoutChanges);
- }
+ setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+ "appToken " + appAnimator.mAppToken + " done");
if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
"updateWindowsApps...: done animating " + appAnimator.mAppToken);
}
@@ -252,11 +251,8 @@
mAnimating = true;
} else if (wasAnimating) {
// stopped animating, do one more pass through the layout
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("exiting appToken " + appAnimator.mAppToken
- + " done", mPendingLayoutChanges);
- }
+ setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+ "exiting appToken " + appAnimator.mAppToken + " done");
if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
"updateWindowsApps...: done animating exiting " + appAnimator.mAppToken);
}
@@ -302,10 +298,11 @@
if (wasAnimating && !winAnimator.mAnimating && mWallpaperTarget == win) {
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 2",
- mPendingLayoutChanges);
+ mPendingLayoutChanges.get(Display.DEFAULT_DISPLAY));
}
}
@@ -315,10 +312,12 @@
WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
"Animation started that could impact force hide: " + win);
mBulkUpdateParams |= SET_FORCE_HIDING_CHANGED;
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ final int displayId = win.mDisplayContent.getDisplayId();
+ setPendingLayoutChanges(displayId,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 3",
- mPendingLayoutChanges);
+ mPendingLayoutChanges.get(displayId));
}
mService.mFocusMayChange = true;
}
@@ -377,10 +376,11 @@
}
if (changed && (flags & FLAG_SHOW_WALLPAPER) != 0) {
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 4",
- mPendingLayoutChanges);
+ mPendingLayoutChanges.get(Display.DEFAULT_DISPLAY));
}
}
}
@@ -390,10 +390,12 @@
if (winAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW) {
if (atoken == null || atoken.allDrawn) {
if (winAnimator.performShowLocked()) {
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+ final int displayId = win.mDisplayContent.getDisplayId();
+ mPendingLayoutChanges.put(displayId,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 5",
- mPendingLayoutChanges);
+ mPendingLayoutChanges.get(displayId));
}
}
}
@@ -536,14 +538,14 @@
+ wtoken + " numInteresting=" + wtoken.numInterestingWindows
+ " numDrawn=" + wtoken.numDrawnWindows);
// This will set mOrientationChangeComplete and cause a pass through layout.
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ setAppLayoutChanges(appAnimator,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+ "testTokenMayBeDrawnLocked: freezingScreen");
} else {
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("testTokenMayBeDrawnLocked",
- mPendingLayoutChanges);
- }
-
+ setAppLayoutChanges(appAnimator,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
+ "testTokenMayBeDrawnLocked");
+
// We can now show all of the drawn windows!
if (!mService.mOpeningApps.contains(wtoken)) {
mAnimating |= appAnimator.showAllWindowsLocked();
@@ -557,12 +559,6 @@
private void performAnimationsLocked(final WinAnimatorList winAnimatorList) {
updateWindowsLocked(winAnimatorList);
updateWallpaperLocked(winAnimatorList);
-
- if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
- mPendingActions |= WALLPAPER_ACTION_PENDING;
- }
-
- testTokenMayBeDrawnLocked();
}
// TODO(cmautner): Change the following comment when no longer locked on mWindowMap */
@@ -571,13 +567,8 @@
if (!mInitialized) {
return;
}
- for (int i = mWinAnimatorLists.size() - 1; i >= 0; i--) {
- animateLocked(mWinAnimatorLists.get(i));
- }
- }
- private void animateLocked(final WinAnimatorList winAnimatorList) {
- mPendingLayoutChanges = 0;
+ mPendingLayoutChanges.clear();
mCurrentTime = SystemClock.uptimeMillis();
mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
boolean wasAnimating = mAnimating;
@@ -586,25 +577,31 @@
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
- // Update animations of all applications, including those
- // associated with exiting/removed apps
+ if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
+ TAG, ">>> OPEN TRANSACTION animateLocked");
Surface.openTransaction();
-
try {
updateWindowsAppsAndRotationAnimationsLocked();
- performAnimationsLocked(winAnimatorList);
- // THIRD LOOP: Update the surfaces of all windows.
+ for (int i = mWinAnimatorLists.size() - 1; i >= 0; i--) {
+ final WinAnimatorList winAnimatorList = mWinAnimatorLists.get(i);
+
+ // Update animations of all applications, including those
+ // associated with exiting/removed apps
+ performAnimationsLocked(winAnimatorList);
+
+ final int N = winAnimatorList.size();
+ for (int j = 0; j < N; j++) {
+ winAnimatorList.get(j).prepareSurfaceLocked(true);
+ }
+ }
+
+ testTokenMayBeDrawnLocked();
if (mScreenRotationAnimation != null) {
mScreenRotationAnimation.updateSurfacesInTransaction();
}
- final int N = winAnimatorList.size();
- for (int i = 0; i < N; i++) {
- winAnimatorList.get(i).prepareSurfaceLocked(true);
- }
-
if (mDimParams != null) {
mDimAnimator.updateParameters(mContext.getResources(), mDimParams, mCurrentTime);
}
@@ -629,9 +626,18 @@
Log.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
Surface.closeTransaction();
+ if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
+ TAG, "<<< CLOSE TRANSACTION animateLocked");
}
- if (mBulkUpdateParams != 0 || mPendingLayoutChanges != 0) {
+ for (int i = mPendingLayoutChanges.size() - 1; i >= 0; i--) {
+ if ((mPendingLayoutChanges.valueAt(i)
+ & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+ mPendingActions |= WALLPAPER_ACTION_PENDING;
+ }
+ }
+
+ if (mBulkUpdateParams != 0 || mPendingLayoutChanges.size() > 0) {
updateAnimToLayoutLocked();
}
@@ -645,7 +651,8 @@
if (WindowManagerService.DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
- + " mPendingLayoutChanges=" + Integer.toHexString(mPendingLayoutChanges));
+ + " mPendingLayoutChanges(DEFAULT_DISPLAY)="
+ + Integer.toHexString(mPendingLayoutChanges.get(Display.DEFAULT_DISPLAY)));
}
}
@@ -705,7 +712,30 @@
}
}
- synchronized void clearPendingActions() {
- mPendingActions = 0;
+ void clearPendingActions() {
+ synchronized (this) {
+ mPendingActions = 0;
+ }
+ }
+
+ void setPendingLayoutChanges(final int displayId, final int changes) {
+ mPendingLayoutChanges.put(displayId, mPendingLayoutChanges.get(displayId) | changes);
+ }
+
+ void setAppLayoutChanges(final AppWindowAnimator appAnimator, final int changes, String s) {
+ // Used to track which displays layout changes have been done.
+ SparseIntArray displays = new SparseIntArray();
+ for (int i = appAnimator.mAllAppWinAnimators.size() - 1; i >= 0; i--) {
+ WindowStateAnimator winAnimator = appAnimator.mAllAppWinAnimators.get(i);
+ final int displayId = winAnimator.mWin.mDisplayContent.getDisplayId();
+ if (displays.indexOfKey(displayId) < 0) {
+ setPendingLayoutChanges(displayId, changes);
+ if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ mService.debugLayoutRepeats(s, mPendingLayoutChanges.get(displayId));
+ }
+ // Keep from processing this display again.
+ displays.put(displayId, changes);
+ }
+ }
}
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index b7eeb69..c82fa55 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -99,7 +99,6 @@
import android.util.FloatMath;
import android.util.Log;
import android.util.SparseArray;
-//import android.util.LogPrinter;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -454,8 +453,6 @@
int mSystemDecorLayer = 0;
final Rect mScreenRect = new Rect();
- int mPendingLayoutChanges = 0;
- boolean mLayoutNeeded = true;
boolean mTraversalScheduled = false;
boolean mDisplayFrozen = false;
boolean mWaitingForConfig = false;
@@ -1769,7 +1766,7 @@
token.hidden = !visible;
// Need to do a layout to ensure the wallpaper now has the
// correct size.
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
}
int curWallpaperIndex = token.windows.size();
@@ -2012,7 +2009,7 @@
token.hidden = !visible;
// Need to do a layout to ensure the wallpaper now has the
// correct size.
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
}
int curWallpaperIndex = token.windows.size();
@@ -2311,7 +2308,7 @@
//Slog.i(TAG, "*** Running exit animation...");
win.mExiting = true;
win.mRemoveOnExit = true;
- mLayoutNeeded = true;
+ win.mDisplayContent.layoutNeeded = true;
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/);
performLayoutAndPlaceSurfacesLocked();
@@ -2428,7 +2425,7 @@
if (!mInLayout) {
assignLayersLocked(windows);
- mLayoutNeeded = true;
+ win.mDisplayContent.layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
@@ -2494,7 +2491,7 @@
w.mGivenVisibleInsets.scale(w.mGlobalScale);
w.mGivenTouchableRegion.scale(w.mGlobalScale);
}
- mLayoutNeeded = true;
+ w.mDisplayContent.layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
}
@@ -2589,7 +2586,7 @@
window.mGivenTouchableRegion.op((int)dispRect.left, (int)dispRect.top,
(int)dispRect.right, (int)dispRect.bottom, Region.Op.DIFFERENCE);
window.mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
- mLayoutNeeded = true;
+ window.mDisplayContent.layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
@@ -2857,7 +2854,7 @@
}
}
- mLayoutNeeded = true;
+ win.mDisplayContent.layoutNeeded = true;
win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
if (assignLayers) {
assignLayersLocked(win.getWindowList());
@@ -2947,7 +2944,7 @@
if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
adjustWallpaperWindowsLocked();
}
- mLayoutNeeded = true;
+ win.mDisplayContent.layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
}
@@ -3461,11 +3458,11 @@
win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT,
false);
changed = true;
+ win.mDisplayContent.layoutNeeded = true;
}
}
if (changed) {
- mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
false /*updateInputWindows*/);
@@ -3721,7 +3718,7 @@
if (computeScreenConfigurationLocked(mTempConfiguration)) {
if (currentConfig.diff(mTempConfiguration) != 0) {
mWaitingForConfig = true;
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
startFreezingDisplayLocked(false);
config = new Configuration(mTempConfiguration);
}
@@ -3807,6 +3804,7 @@
}
}
+ @Override
public int getAppOrientation(IApplicationToken token) {
synchronized(mWindowMap) {
AppWindowToken wtoken = findAppWindowToken(token.asBinder());
@@ -3818,6 +3816,7 @@
}
}
+ @Override
public void setFocusedApp(IBinder token, boolean moveFocusNow) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setFocusedApp()")) {
@@ -3855,6 +3854,7 @@
}
}
+ @Override
public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"prepareAppTransition()")) {
@@ -4084,7 +4084,7 @@
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
true /*updateInputWindows*/);
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
Binder.restoreCallingIdentity(origId);
return;
@@ -4249,6 +4249,7 @@
WindowManagerPolicy.TRANSIT_ENTER, true);
}
changed = true;
+ win.mDisplayContent.layoutNeeded = true;
}
} else if (win.isVisibleNow()) {
if (!runningAppAnimation) {
@@ -4256,6 +4257,7 @@
WindowManagerPolicy.TRANSIT_EXIT, false);
}
changed = true;
+ win.mDisplayContent.layoutNeeded = true;
}
}
@@ -4277,7 +4279,6 @@
+ wtoken.hiddenRequested);
if (changed) {
- mLayoutNeeded = true;
mInputMonitor.setUpdateInputWindowsNeededLw();
if (performLayout) {
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
@@ -4405,6 +4406,7 @@
mInnerFields.mOrientationChangeComplete = false;
}
unfrozeWindows = true;
+ w.mDisplayContent.layoutNeeded = true;
}
}
if (force || unfrozeWindows) {
@@ -4414,7 +4416,6 @@
}
if (unfreezeSurfaceNow) {
if (unfrozeWindows) {
- mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
stopFreezingDisplayLocked();
@@ -4759,13 +4760,15 @@
final DisplayContent displayContent = iterator.next();
final WindowList windows = displayContent.getWindowList();
final int pos = findWindowOffsetLocked(windows, index);
- reAddAppWindowsLocked(displayContent, pos, wtoken);
+ final int newPos = reAddAppWindowsLocked(displayContent, pos, wtoken);
+ if (pos != newPos) {
+ displayContent.layoutNeeded = true;
+ }
}
if (DEBUG_REORDER) Slog.v(TAG, "Final window list:");
if (DEBUG_REORDER) dumpWindowsLocked();
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/);
- mLayoutNeeded = true;
mInputMonitor.setUpdateInputWindowsNeededLw();
performLayoutAndPlaceSurfacesLocked();
mInputMonitor.updateInputWindowsLw(false /*force*/);
@@ -4805,7 +4808,10 @@
final DisplayContent displayContent = iterator.next();
final WindowList windows = displayContent.getWindowList();
final int pos = findWindowOffsetLocked(windows, tokenPos);
- reAddAppWindowsLocked(displayContent, pos, wtoken);
+ final int newPos = reAddAppWindowsLocked(displayContent, pos, wtoken);
+ if (pos != newPos) {
+ displayContent.layoutNeeded = true;
+ }
if (updateFocusAndLayout && !updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/)) {
@@ -4818,7 +4824,6 @@
// Note that the above updateFocusedWindowLocked conditional used to sit here.
- mLayoutNeeded = true;
if (!mInLayout) {
performLayoutAndPlaceSurfacesLocked();
}
@@ -4847,7 +4852,11 @@
for (i=0; i<N; i++) {
WindowToken token = mTokenMap.get(tokens.get(i));
if (token != null) {
- pos = reAddAppWindowsLocked(displayContent, pos, token);
+ final int newPos = reAddAppWindowsLocked(displayContent, pos, token);
+ if (newPos != pos) {
+ displayContent.layoutNeeded = true;
+ }
+ pos = newPos;
}
}
if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
@@ -4860,7 +4869,6 @@
// Note that the above updateFocusedWindowLocked used to sit here.
- mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
mInputMonitor.updateInputWindowsLw(false /*force*/);
@@ -5654,7 +5662,7 @@
synchronized(mWindowMap) {
changed = updateRotationUncheckedLocked(false);
if (!changed || forceRelayout) {
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
}
@@ -5732,7 +5740,7 @@
mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT), 2000);
mWaitingForConfig = true;
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
startFreezingDisplayLocked(inTransaction);
mInputManager.setDisplayOrientation(0, rotation);
@@ -6390,7 +6398,8 @@
sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh);
sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw);
outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);
- outConfig.screenLayout = sl;
+ outConfig.screenLayout =
+ sl|(outConfig.screenLayout&Configuration.SCREENLAYOUT_LAYOUTDIR_MASK);
}
private int reduceCompatConfigWidthSize(int curSize, int rotation, DisplayMetrics dm,
@@ -7576,7 +7585,7 @@
mPolicy.setInitialDisplaySize(mDefaultDisplay, displayContent.mBaseDisplayWidth,
displayContent.mBaseDisplayHeight, displayContent.mBaseDisplayDensity);
- mLayoutNeeded = true;
+ displayContent.layoutNeeded = true;
boolean configChanged = updateOrientationFromAppTokensLocked(false);
mTempConfiguration.setToDefaults();
@@ -7831,31 +7840,11 @@
}
try {
- DisplayContentsIterator iterator = new DisplayContentsIterator();
- while (iterator.hasNext()) {
- final DisplayContent displayContent = iterator.next();
- performLayoutAndPlaceSurfacesLockedInner(displayContent, recoveringMemory);
-
- final int N = mPendingRemove.size();
- if (N > 0) {
- if (mPendingRemoveTmp.length < N) {
- mPendingRemoveTmp = new WindowState[N+10];
- }
- mPendingRemove.toArray(mPendingRemoveTmp);
- mPendingRemove.clear();
- for (int i=0; i<N; i++) {
- WindowState w = mPendingRemoveTmp[i];
- removeWindowInnerLocked(w.mSession, w);
- }
-
- assignLayersLocked(displayContent.getWindowList());
- mLayoutNeeded = true;
- }
- }
+ performLayoutAndPlaceSurfacesLockedInner(recoveringMemory);
mInLayout = false;
- if (mLayoutNeeded) {
+ if (needsLayout()) {
if (++mLayoutRepeatCount < 6) {
requestTraversalLocked();
} else {
@@ -7880,11 +7869,11 @@
private final void performLayoutLockedInner(final DisplayContent displayContent,
boolean initial, boolean updateInputWindows) {
- if (!mLayoutNeeded) {
+ if (!displayContent.layoutNeeded) {
return;
}
+ displayContent.layoutNeeded = false;
WindowList windows = displayContent.getWindowList();
- mLayoutNeeded = false;
DisplayInfo displayInfo = displayContent.getDisplayInfo();
final int dw = displayInfo.logicalWidth;
@@ -7901,7 +7890,7 @@
if (DEBUG_LAYOUT) {
Slog.v(TAG, "-------------------------------------");
Slog.v(TAG, "performLayout: needed="
- + mLayoutNeeded + " dw=" + dw + " dh=" + dh);
+ + displayContent.layoutNeeded + " dw=" + dw + " dh=" + dh);
}
WindowStateAnimator universeBackground = null;
@@ -8046,8 +8035,7 @@
/**
* Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
- * @param windows TODO(cmautner):
- *
+ * @param windows List of windows on default display.
* @return bitmap indicating if another pass through layout must be made.
*/
public int handleAppTransitionReadyLocked(WindowList windows) {
@@ -8303,7 +8291,7 @@
// a new layout to get them all up-to-date.
changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT
| WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
// TODO(multidisplay): IMEs are only supported on the default display.
if (windows == getDefaultWindowList() && !moveInputMethodWindowsIfNeededLocked(true)) {
@@ -8525,8 +8513,7 @@
}
// "Something has changed! Let's make it correct now."
- private final void performLayoutAndPlaceSurfacesLockedInner(
- final DisplayContent displayContent, boolean recoveringMemory) {
+ private final void performLayoutAndPlaceSurfacesLockedInner(boolean recoveringMemory) {
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "performLayoutAndPlaceSurfacesLockedInner: entry. Called by "
+ Debug.getCallers(3));
@@ -8536,13 +8523,7 @@
return;
}
- final WindowList windows = displayContent.getWindowList();
final long currentTime = SystemClock.uptimeMillis();
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- final int dw = displayInfo.logicalWidth;
- final int dh = displayInfo.logicalHeight;
- final int innerDw = displayInfo.appWidth;
- final int innerDh = displayInfo.appHeight;
int i;
@@ -8567,201 +8548,233 @@
mInnerFields.mButtonBrightness = -1;
mTransactionSequence++;
+ final DisplayContent defaultDisplay = getDefaultDisplayContent();
+ final DisplayInfo defaultInfo = defaultDisplay.getDisplayInfo();
+ final int defaultDw = defaultInfo.logicalWidth;
+ final int defaultDh = defaultInfo.logicalHeight;
+ final int defaultInnerDw = defaultInfo.appWidth;
+ final int defaultInnerDh = defaultInfo.appHeight;
+
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
-
Surface.openTransaction();
try {
+
if (mWatermark != null) {
- mWatermark.positionSurface(dw, dh);
+ mWatermark.positionSurface(defaultDw, defaultDh);
}
if (mStrictModeFlash != null) {
- mStrictModeFlash.positionSurface(dw, dh);
+ mStrictModeFlash.positionSurface(defaultDw, defaultDh);
}
// Give the display manager a chance to adjust properties
// like display rotation if it needs to.
mDisplayManagerService.performTraversalInTransactionFromWindowManager();
- int repeats = 0;
-
- do {
- repeats++;
- if (repeats > 6) {
- Slog.w(TAG, "Animation repeat aborted after too many iterations");
- mLayoutNeeded = false;
- break;
- }
-
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("On entry to LockedInner",
- mPendingLayoutChanges);
-
- if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
- if ((adjustWallpaperWindowsLocked() & ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
- assignLayersLocked(windows);
- mLayoutNeeded = true;
- }
- }
-
- if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
- if (updateOrientationFromAppTokensLocked(true)) {
- mLayoutNeeded = true;
- mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
- }
- }
-
- if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
- mLayoutNeeded = true;
- }
-
- // FIRST LOOP: Perform a layout, if needed.
- if (repeats < 4) {
- performLayoutLockedInner(displayContent, repeats == 1, false /*updateInputWindows*/);
- } else {
- Slog.w(TAG, "Layout repeat skipped after too many iterations");
- }
-
- // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
- // it is animating.
- mPendingLayoutChanges = 0;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("loop number " + mLayoutRepeatCount,
- mPendingLayoutChanges);
- mPolicy.beginPostLayoutPolicyLw(dw, dh);
- for (i = windows.size() - 1; i >= 0; i--) {
- WindowState w = windows.get(i);
- if (w.mHasSurface) {
- mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs);
- }
- }
- mPendingLayoutChanges |= mPolicy.finishPostLayoutPolicyLw();
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after finishPostLayoutPolicyLw",
- mPendingLayoutChanges);
- } while (mPendingLayoutChanges != 0);
-
- final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
-
- mInnerFields.mObscured = false;
- mInnerFields.mDimming = false;
- mInnerFields.mSyswin = false;
-
boolean focusDisplayed = false;
boolean updateAllDrawn = false;
- final int N = windows.size();
- for (i=N-1; i>=0; i--) {
- WindowState w = windows.get(i);
- final boolean obscuredChanged = w.mObscured != mInnerFields.mObscured;
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ final DisplayContent displayContent = iterator.next();
+ WindowList windows = displayContent.getWindowList();
+ DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final int displayId = displayContent.getDisplayId();
+ final int dw = displayInfo.logicalWidth;
+ final int dh = displayInfo.logicalHeight;
+ final int innerDw = displayInfo.appWidth;
+ final int innerDh = displayInfo.appHeight;
+ final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
- // Update effect.
- w.mObscured = mInnerFields.mObscured;
- if (!mInnerFields.mObscured) {
- handleNotObscuredLocked(w, currentTime, innerDw, innerDh);
- }
-
- if (obscuredChanged && (mWallpaperTarget == w) && w.isVisibleLw()) {
- // This is the wallpaper target and its obscured state
- // changed... make sure the current wallaper's visibility
- // has been updated accordingly.
- updateWallpaperVisibilityLocked();
- }
-
- final WindowStateAnimator winAnimator = w.mWinAnimator;
-
- // If the window has moved due to its containing
- // content frame changing, then we'd like to animate
- // it.
- if (w.mHasSurface && w.shouldAnimateMove()) {
- // Frame has moved, containing content frame
- // has also moved, and we're not currently animating...
- // let's do something.
- Animation a = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.window_move_from_decor);
- winAnimator.setAnimation(a);
- winAnimator.mAnimDw = w.mLastFrame.left - w.mFrame.left;
- winAnimator.mAnimDh = w.mLastFrame.top - w.mFrame.top;
- try {
- w.mClient.moved(w.mFrame.left, w.mFrame.top);
- } catch (RemoteException e) {
+ int repeats = 0;
+ do {
+ repeats++;
+ if (repeats > 6) {
+ Slog.w(TAG, "Animation repeat aborted after too many iterations");
+ displayContent.layoutNeeded = false;
+ break;
}
- }
- //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
- w.mContentChanged = false;
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("On entry to LockedInner",
+ displayContent.pendingLayoutChanges);
- // Moved from updateWindowsAndWallpaperLocked().
- if (w.mHasSurface) {
- // Take care of the window being ready to display.
- if (winAnimator.commitFinishDrawingLocked(currentTime)) {
- if ((w.mAttrs.flags
- & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
- if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
- "First draw done in potential wallpaper target " + w);
- mInnerFields.mWallpaperMayChange = true;
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- debugLayoutRepeats("wallpaper and commitFinishDrawingLocked true",
- mPendingLayoutChanges);
- }
+ if (isDefaultDisplay && ((displayContent.pendingLayoutChanges
+ & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0)
+ && ((adjustWallpaperWindowsLocked()
+ & ADJUST_WALLPAPER_LAYERS_CHANGED) != 0)) {
+ assignLayersLocked(windows);
+ displayContent.layoutNeeded = true;
+ }
+
+ if (isDefaultDisplay && (displayContent.pendingLayoutChanges
+ & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
+ if (updateOrientationFromAppTokensLocked(true)) {
+ displayContent.layoutNeeded = true;
+ mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
}
}
- winAnimator.setSurfaceBoundaries(recoveringMemory);
-
- final AppWindowToken atoken = w.mAppToken;
- if (DEBUG_STARTING_WINDOW && atoken != null && w == atoken.startingWindow) {
- Slog.d(TAG, "updateWindows: starting " + w + " isOnScreen="
- + w.isOnScreen() + " allDrawn=" + atoken.allDrawn
- + " freezingScreen=" + atoken.mAppAnimator.freezingScreen);
+ if ((displayContent.pendingLayoutChanges
+ & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+ displayContent.layoutNeeded = true;
}
- if (atoken != null
- && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) {
- if (atoken.lastTransactionSequence != mTransactionSequence) {
- atoken.lastTransactionSequence = mTransactionSequence;
- atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
- atoken.startingDisplayed = false;
+
+ // FIRST LOOP: Perform a layout, if needed.
+ if (repeats < 4) {
+ performLayoutLockedInner(displayContent, repeats == 1,
+ false /*updateInputWindows*/);
+ } else {
+ Slog.w(TAG, "Layout repeat skipped after too many iterations");
+ }
+
+ // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
+ // it is animating.
+ displayContent.pendingLayoutChanges = 0;
+
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("loop number "
+ + mLayoutRepeatCount, displayContent.pendingLayoutChanges);
+
+ mPolicy.beginPostLayoutPolicyLw(dw, dh);
+ for (i = windows.size() - 1; i >= 0; i--) {
+ WindowState w = windows.get(i);
+ if (w.mHasSurface) {
+ mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs);
}
- if ((w.isOnScreen() || winAnimator.mAttrType
- == WindowManager.LayoutParams.TYPE_BASE_APPLICATION)
- && !w.mExiting && !w.mDestroying) {
- if (WindowManagerService.DEBUG_VISIBILITY ||
- WindowManagerService.DEBUG_ORIENTATION) {
- Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
- + ", isAnimating=" + winAnimator.isAnimating());
- if (!w.isDrawnLw()) {
- Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurface
- + " pv=" + w.mPolicyVisibility
- + " mDrawState=" + winAnimator.mDrawState
- + " ah=" + w.mAttachedHidden
- + " th=" + atoken.hiddenRequested
- + " a=" + winAnimator.mAnimating);
+ }
+ displayContent.pendingLayoutChanges |= mPolicy.finishPostLayoutPolicyLw();
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after finishPostLayoutPolicyLw",
+ displayContent.pendingLayoutChanges);
+ } while (displayContent.pendingLayoutChanges != 0);
+
+ mInnerFields.mObscured = false;
+ mInnerFields.mDimming = false;
+ mInnerFields.mSyswin = false;
+
+ // Only used if default window
+ final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
+
+ final int N = windows.size();
+ for (i=N-1; i>=0; i--) {
+ WindowState w = windows.get(i);
+
+ final boolean obscuredChanged = w.mObscured != mInnerFields.mObscured;
+
+ // Update effect.
+ w.mObscured = mInnerFields.mObscured;
+ if (!mInnerFields.mObscured) {
+ handleNotObscuredLocked(w, currentTime, innerDw, innerDh);
+ }
+
+ if (isDefaultDisplay && obscuredChanged && (mWallpaperTarget == w)
+ && w.isVisibleLw()) {
+ // This is the wallpaper target and its obscured state
+ // changed... make sure the current wallaper's visibility
+ // has been updated accordingly.
+ updateWallpaperVisibilityLocked();
+ }
+
+ final WindowStateAnimator winAnimator = w.mWinAnimator;
+
+ // If the window has moved due to its containing
+ // content frame changing, then we'd like to animate
+ // it.
+ if (w.mHasSurface && w.shouldAnimateMove()) {
+ // Frame has moved, containing content frame
+ // has also moved, and we're not currently animating...
+ // let's do something.
+ Animation a = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.window_move_from_decor);
+ winAnimator.setAnimation(a);
+ winAnimator.mAnimDw = w.mLastFrame.left - w.mFrame.left;
+ winAnimator.mAnimDh = w.mLastFrame.top - w.mFrame.top;
+ try {
+ w.mClient.moved(w.mFrame.left, w.mFrame.top);
+ } catch (RemoteException e) {
+ }
+ }
+
+ //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
+ w.mContentChanged = false;
+
+ // Moved from updateWindowsAndWallpaperLocked().
+ if (w.mHasSurface) {
+ // Take care of the window being ready to display.
+ if (isDefaultDisplay
+ && winAnimator.commitFinishDrawingLocked(currentTime)) {
+ if ((w.mAttrs.flags
+ & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
+ if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
+ "First draw done in potential wallpaper target " + w);
+ mInnerFields.mWallpaperMayChange = true;
+ displayContent.pendingLayoutChanges |=
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ debugLayoutRepeats(
+ "wallpaper and commitFinishDrawingLocked true",
+ displayContent.pendingLayoutChanges);
}
}
- if (w != atoken.startingWindow) {
- if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) {
- atoken.numInterestingWindows++;
- if (w.isDrawnLw()) {
- atoken.numDrawnWindows++;
- if (WindowManagerService.DEBUG_VISIBILITY ||
- WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
- "tokenMayBeDrawn: " + atoken
- + " freezingScreen=" + atoken.mAppAnimator.freezingScreen
- + " mAppFreezing=" + w.mAppFreezing);
- updateAllDrawn = true;
+ }
+
+ winAnimator.setSurfaceBoundaries(recoveringMemory);
+
+ final AppWindowToken atoken = w.mAppToken;
+ if (DEBUG_STARTING_WINDOW && atoken != null && w == atoken.startingWindow) {
+ Slog.d(TAG, "updateWindows: starting " + w + " isOnScreen="
+ + w.isOnScreen() + " allDrawn=" + atoken.allDrawn
+ + " freezingScreen=" + atoken.mAppAnimator.freezingScreen);
+ }
+ if (atoken != null
+ && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) {
+ if (atoken.lastTransactionSequence != mTransactionSequence) {
+ atoken.lastTransactionSequence = mTransactionSequence;
+ atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
+ atoken.startingDisplayed = false;
+ }
+ if ((w.isOnScreen() || winAnimator.mAttrType
+ == WindowManager.LayoutParams.TYPE_BASE_APPLICATION)
+ && !w.mExiting && !w.mDestroying) {
+ if (WindowManagerService.DEBUG_VISIBILITY ||
+ WindowManagerService.DEBUG_ORIENTATION) {
+ Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
+ + ", isAnimating=" + winAnimator.isAnimating());
+ if (!w.isDrawnLw()) {
+ Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurface
+ + " pv=" + w.mPolicyVisibility
+ + " mDrawState=" + winAnimator.mDrawState
+ + " ah=" + w.mAttachedHidden
+ + " th=" + atoken.hiddenRequested
+ + " a=" + winAnimator.mAnimating);
}
}
- } else if (w.isDrawnLw()) {
- atoken.startingDisplayed = true;
+ if (w != atoken.startingWindow) {
+ if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) {
+ atoken.numInterestingWindows++;
+ if (w.isDrawnLw()) {
+ atoken.numDrawnWindows++;
+ if (WindowManagerService.DEBUG_VISIBILITY ||
+ WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
+ "tokenMayBeDrawn: " + atoken
+ + " freezingScreen=" + atoken.mAppAnimator.freezingScreen
+ + " mAppFreezing=" + w.mAppFreezing);
+ updateAllDrawn = true;
+ }
+ }
+ } else if (w.isDrawnLw()) {
+ atoken.startingDisplayed = true;
+ }
}
}
}
- }
- if (someoneLosingFocus && w == mCurrentFocus && w.isDisplayedLw()) {
- focusDisplayed = true;
- }
+ if (isDefaultDisplay && someoneLosingFocus && (w == mCurrentFocus)
+ && w.isDisplayedLw()) {
+ focusDisplayed = true;
+ }
- updateResizingWindows(w);
+ updateResizingWindows(w);
+ }
}
if (updateAllDrawn) {
@@ -8781,13 +8794,15 @@
Surface.closeTransaction();
}
+ final WindowList defaultWindows = defaultDisplay.getWindowList();
+
// If we are ready to perform an app transition, check through
// all of the app tokens to be shown and see if they are ready
// to go.
if (mAppTransitionReady) {
- mPendingLayoutChanges |= handleAppTransitionReadyLocked(windows);
+ defaultDisplay.pendingLayoutChanges |= handleAppTransitionReadyLocked(defaultWindows);
if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAppTransitionReadyLocked",
- mPendingLayoutChanges);
+ defaultDisplay.pendingLayoutChanges);
}
mInnerFields.mAdjResult = 0;
@@ -8799,22 +8814,22 @@
// reflects the correct Z-order, but the window list may now
// be out of sync with it. So here we will just rebuild the
// entire app window list. Fun!
- mPendingLayoutChanges |= handleAnimatingStoppedAndTransitionLocked();
+ defaultDisplay.pendingLayoutChanges |= handleAnimatingStoppedAndTransitionLocked();
if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAnimStopAndXitionLock",
- mPendingLayoutChanges);
+ defaultDisplay.pendingLayoutChanges);
}
- if (mInnerFields.mWallpaperForceHidingChanged && mPendingLayoutChanges == 0 &&
- !mAppTransitionReady) {
+ if (mInnerFields.mWallpaperForceHidingChanged && defaultDisplay.pendingLayoutChanges == 0
+ && !mAppTransitionReady) {
// At this point, there was a window with a wallpaper that
// was force hiding other windows behind it, but now it
// is going away. This may be simple -- just animate
// away the wallpaper and its window -- or it may be
// hard -- the wallpaper now needs to be shown behind
// something that was hidden.
- mPendingLayoutChanges |= animateAwayWallpaperLocked();
+ defaultDisplay.pendingLayoutChanges |= animateAwayWallpaperLocked();
if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animateAwayWallpaperLocked",
- mPendingLayoutChanges);
+ defaultDisplay.pendingLayoutChanges);
}
mInnerFields.mWallpaperForceHidingChanged = false;
@@ -8827,26 +8842,27 @@
if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
if (DEBUG_WALLPAPER) Slog.v(TAG,
"Wallpaper layer changed: assigning layers + relayout");
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
- assignLayersLocked(windows);
+ defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+ assignLayersLocked(defaultWindows);
} else if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_VISIBILITY_CHANGED) != 0) {
if (DEBUG_WALLPAPER) Slog.v(TAG,
"Wallpaper visibility changed: relayout");
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
+ defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
}
if (mFocusMayChange) {
mFocusMayChange = false;
if (updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
false /*updateInputWindows*/)) {
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
+ defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
mInnerFields.mAdjResult = 0;
}
}
- if (mLayoutNeeded) {
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded", mPendingLayoutChanges);
+ if (needsLayout()) {
+ defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded",
+ defaultDisplay.pendingLayoutChanges);
}
if (!mResizingWindows.isEmpty()) {
@@ -8954,11 +8970,16 @@
mRelayoutWhileAnimating.clear();
}
- if (wallpaperDestroyed) {
- mLayoutNeeded |= adjustWallpaperWindowsLocked() != 0;
+ if (wallpaperDestroyed && (adjustWallpaperWindowsLocked() != 0)) {
+ getDefaultDisplayContent().layoutNeeded = true;
}
- if (mPendingLayoutChanges != 0) {
- mLayoutNeeded = true;
+
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ DisplayContent displayContent = iterator.next();
+ if (displayContent.pendingLayoutChanges != 0) {
+ displayContent.layoutNeeded = true;
+ }
}
// Finally update all input windows now that the window changes have stabilized.
@@ -8995,11 +9016,33 @@
}
}
- if (mInnerFields.mOrientationChangeComplete && !mLayoutNeeded &&
- !mInnerFields.mUpdateRotation) {
+ if (mInnerFields.mOrientationChangeComplete && !defaultDisplay.layoutNeeded
+ && !mInnerFields.mUpdateRotation) {
checkDrawnWindowsLocked();
}
+ final int N = mPendingRemove.size();
+ if (N > 0) {
+ if (mPendingRemoveTmp.length < N) {
+ mPendingRemoveTmp = new WindowState[N+10];
+ }
+ mPendingRemove.toArray(mPendingRemoveTmp);
+ mPendingRemove.clear();
+ DisplayContentList displayList = new DisplayContentList();
+ for (i = 0; i < N; i++) {
+ WindowState w = mPendingRemoveTmp[i];
+ removeWindowInnerLocked(w.mSession, w);
+ if (!displayList.contains(w.mDisplayContent)) {
+ displayList.add(w.mDisplayContent);
+ }
+ }
+
+ for (DisplayContent displayContent : displayList) {
+ assignLayersLocked(displayContent.getWindowList());
+ displayContent.layoutNeeded = true;
+ }
+ }
+
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
enableScreenIfNeededLocked();
@@ -9007,9 +9050,8 @@
updateLayoutToAnimationLocked();
if (DEBUG_WINDOW_TRACE) {
- Slog.e(TAG, "performLayoutAndPlaceSurfacesLockedInner exit: mPendingLayoutChanges="
- + Integer.toHexString(mPendingLayoutChanges) + " mLayoutNeeded=" + mLayoutNeeded
- + " animating=" + mAnimator.mAnimating);
+ Slog.e(TAG, "performLayoutAndPlaceSurfacesLockedInner exit: animating="
+ + mAnimator.mAnimating);
}
}
@@ -9165,6 +9207,16 @@
setAnimDimParams(null);
}
+ private boolean needsLayout() {
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ if (iterator.next().layoutNeeded) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private boolean copyAnimToLayoutParamsLocked() {
boolean doRequest = false;
final WindowAnimator.AnimatorToLayoutParams animToLayout = mAnimator.mAnimToLayout;
@@ -9197,10 +9249,15 @@
mTurnOnScreen = true;
}
- mPendingLayoutChanges |= animToLayout.mPendingLayoutChanges;
- if (mPendingLayoutChanges != 0) {
+ SparseIntArray pendingLayouts = animToLayout.mPendingLayoutChanges;
+ final int count = pendingLayouts.size();
+ if (count > 0) {
doRequest = true;
}
+ for (int i = 0; i < count; ++i) {
+ final DisplayContent displayContent = getDisplayContent(pendingLayouts.keyAt(i));
+ displayContent.pendingLayoutChanges = pendingLayouts.valueAt(i);
+ }
mWindowDetachedWallpaper = animToLayout.mWindowDetachedWallpaper;
}
@@ -9336,7 +9393,7 @@
if (moveInputMethodWindowsIfNeededLocked(
mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS &&
mode != UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
}
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows);
@@ -9350,7 +9407,7 @@
if ((focusChanged & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
// The change in focus caused us to need to do a layout. Okay.
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows);
}
@@ -10012,8 +10069,18 @@
}
pw.print(" mSystemBooted="); pw.print(mSystemBooted);
pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
- pw.print(" mLayoutNeeded="); pw.print(mLayoutNeeded);
- pw.print("mTransactionSequence="); pw.println(mTransactionSequence);
+ if (needsLayout()) {
+ pw.print(" layoutNeeded on displays=");
+ DisplayContentsIterator dcIterator = new DisplayContentsIterator();
+ while (dcIterator.hasNext()) {
+ final DisplayContent displayContent = dcIterator.next();
+ if (displayContent.layoutNeeded) {
+ pw.print(displayContent.getDisplayId());
+ }
+ }
+ pw.println();
+ }
+ pw.print("mTransactionSequence="); pw.println(mTransactionSequence);
pw.print(" mDisplayFrozen="); pw.print(mDisplayFrozen);
pw.print(" mWindowsFreezingScreen="); pw.print(mWindowsFreezingScreen);
pw.print(" mAppsFreezingScreen="); pw.print(mAppsFreezingScreen);
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index a52e1d7..6711445 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -587,10 +587,12 @@
}
}
+ @Override
public int getSystemUiVisibility() {
return mSystemUiVisibility;
}
+ @Override
public int getSurfaceLayer() {
return mLayer;
}
@@ -598,7 +600,11 @@
public IApplicationToken getAppToken() {
return mAppToken != null ? mAppToken.appToken : null;
}
-
+
+ public int getDisplayId() {
+ return mDisplayContent.getDisplayId();
+ }
+
public long getInputDispatchingTimeoutNanos() {
return mAppToken != null
? mAppToken.inputDispatchingTimeoutNanos
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index 1bda22a..9bb7299 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -335,7 +335,7 @@
+ mWin.mPolicyVisibilityAfterAnim);
}
mWin.mPolicyVisibility = mWin.mPolicyVisibilityAfterAnim;
- mService.mLayoutNeeded = true;
+ mWin.mDisplayContent.layoutNeeded = true;
if (!mWin.mPolicyVisibility) {
if (mService.mCurrentFocus == mWin) {
mService.mFocusMayChange = true;
@@ -359,9 +359,10 @@
}
finishExit();
- mAnimator.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+ final int displayId = mWin.mDisplayContent.getDisplayId();
+ mAnimator.setPendingLayoutChanges(displayId, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) mService.debugLayoutRepeats(
- "WindowStateAnimator", mAnimator.mPendingLayoutChanges);
+ "WindowStateAnimator", mAnimator.mPendingLayoutChanges.get(displayId));
if (mWin.mAppToken != null) {
mWin.mAppToken.updateReportedVisibilityLocked();
@@ -1106,8 +1107,9 @@
"SIZE " + width + "x" + height, null);
mSurfaceResized = true;
mSurface.setSize(width, height);
- mAnimator.mPendingLayoutChanges |=
- WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ final int displayId = w.mDisplayContent.getDisplayId();
+ mAnimator.setPendingLayoutChanges(displayId,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if ((w.mAttrs.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) {
final DisplayInfo displayInfo = mWin.mDisplayContent.getDisplayInfo();
mService.startDimming(this, w.mExiting ? 0 : w.mAttrs.dimAmount,
@@ -1360,7 +1362,7 @@
// do a layout. If called from within the transaction
// loop, this will cause it to restart with a new
// layout.
- mService.mLayoutNeeded = true;
+ c.mDisplayContent.layoutNeeded = true;
}
}
}
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 9004e10..5c9282e 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -370,6 +370,13 @@
throw new UnsupportedOperationException();
}
+ /** @hide */
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
throw new UnsupportedOperationException();
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index c783ad6..aebc594 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -33,6 +33,15 @@
<meta-data android:name="android.graphics.renderThread" android:value="true" />
<activity
+ android:name="PathOffsetActivity"
+ android:label="_PathOffset">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name="TextPathActivity"
android:label="_TextPath">
<intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PathOffsetActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PathOffsetActivity.java
new file mode 100644
index 0000000..fa73de1
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PathOffsetActivity.java
@@ -0,0 +1,67 @@
+/*
+ * 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 com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class PathOffsetActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final PathsView view = new PathsView(this);
+ setContentView(view);
+ }
+
+ public class PathsView extends View {
+ private Path mPath;
+ private Paint mPaint;
+
+ public PathsView(Context context) {
+ super(context);
+
+ mPaint = new Paint();
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeWidth(3);
+
+ mPath = new Path();
+ mPath.lineTo(100, 100);
+ mPath.lineTo(200, 300);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ mPath.offset(1, 1);
+ mPaint.setColor(Color.RED);
+ canvas.drawPath(mPath, mPaint);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ invalidate();
+ return super.onTouchEvent(event);
+ }
+ }
+}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 07626a3..327ff06 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -168,10 +168,10 @@
mTest = new Vignette(true, true);
break;
case 15:
- mTest = new GroupTest(true);
+ mTest = new GroupTest(false);
break;
case 16:
- mTest = new GroupTest(false);
+ mTest = new GroupTest(true);
break;
case 17:
mTest = new Intrinsics(0);
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 46b8a27..438a670 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -183,6 +183,13 @@
return 0;
}
+ // layout direction
+ if (getLayoutDirectionName(part.string(), &config)) {
+ *axis = AXIS_LAYOUTDIR;
+ *value = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
+ return 0;
+ }
+
// smallest screen dp width
if (getSmallestScreenWidthDpName(part.string(), &config)) {
*axis = AXIS_SMALLESTSCREENWIDTHDP;
@@ -309,6 +316,8 @@
case AXIS_LANGUAGE:
return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16)
| (((uint32_t)config.language[1]) << 8) | (config.language[0]);
+ case AXIS_LAYOUTDIR:
+ return config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
case AXIS_SCREENLAYOUTSIZE:
return config.screenLayout&ResTable_config::MASK_SCREENSIZE;
case AXIS_ORIENTATION:
@@ -364,7 +373,7 @@
Vector<String8> parts;
String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
- String8 touch, key, keysHidden, nav, navHidden, size, vers;
+ String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
const char *p = dir;
@@ -452,6 +461,18 @@
//printf("not region: %s\n", part.string());
}
+ 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;
@@ -674,6 +695,7 @@
this->navHidden = navHidden;
this->navigation = nav;
this->screenSize = size;
+ this->layoutDirection = layoutDir;
this->version = vers;
// what is this anyway?
@@ -691,6 +713,8 @@
s += ",";
s += this->locale;
s += ",";
+ s += layoutDirection;
+ s += ",";
s += smallestScreenWidthDp;
s += ",";
s += screenWidthDp;
@@ -747,6 +771,12 @@
}
s += locale;
}
+ if (this->layoutDirection != "") {
+ if (s.length() > 0) {
+ s += "-";
+ }
+ s += layoutDirection;
+ }
if (this->smallestScreenWidthDp != "") {
if (s.length() > 0) {
s += "-";
@@ -958,6 +988,28 @@
return false;
}
+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, "ltr") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+ | ResTable_config::LAYOUTDIR_LTR;
+ return true;
+ } else if (strcmp(name, "rtl") == 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)
{
@@ -1415,6 +1467,7 @@
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);
@@ -1447,6 +1500,7 @@
getMccName(mcc.string(), ¶ms);
getMncName(mnc.string(), ¶ms);
getLocaleName(locale.string(), ¶ms);
+ getLayoutDirectionName(layoutDirection.string(), ¶ms);
getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), ¶ms);
getScreenWidthDpName(screenWidthDp.string(), ¶ms);
getScreenHeightDpName(screenHeightDp.string(), ¶ms);
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index d5f296c..5cfa913 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -51,6 +51,7 @@
AXIS_SMALLESTSCREENWIDTHDP,
AXIS_SCREENWIDTHDP,
AXIS_SCREENHEIGHTDP,
+ AXIS_LAYOUTDIR,
AXIS_VERSION,
AXIS_START = AXIS_MCC,
@@ -95,6 +96,7 @@
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);
int compare(const AaptGroupEntry& o) const;
@@ -133,6 +135,7 @@
String8 navHidden;
String8 navigation;
String8 screenSize;
+ String8 layoutDirection;
String8 version;
mutable bool mParamsChanged;
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index d98fe65..3d7b088 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2811,7 +2811,7 @@
NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
"orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
- "sw%ddp w%ddp h%ddp\n",
+ "sw%ddp w%ddp h%ddp dir:%d\n",
ti+1,
config.mcc, config.mnc,
config.language[0] ? config.language[0] : '-',
@@ -2829,7 +2829,8 @@
config.screenHeight,
config.smallestScreenWidthDp,
config.screenWidthDp,
- config.screenHeightDp));
+ config.screenHeightDp,
+ config.layoutDirection));
if (filterable && !filter.match(config)) {
continue;
@@ -2853,7 +2854,7 @@
tHeader->config = config;
NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
"orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
- "sw%ddp w%ddp h%ddp\n",
+ "sw%ddp w%ddp h%ddp dir:%d\n",
ti+1,
tHeader->config.mcc, tHeader->config.mnc,
tHeader->config.language[0] ? tHeader->config.language[0] : '-',
@@ -2871,7 +2872,8 @@
tHeader->config.screenHeight,
tHeader->config.smallestScreenWidthDp,
tHeader->config.screenWidthDp,
- tHeader->config.screenHeightDp));
+ tHeader->config.screenHeightDp,
+ tHeader->config.layoutDirection));
tHeader->config.swapHtoD();
// Build the entries inside of this type.
@@ -3489,7 +3491,7 @@
if (config != NULL) {
NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
"orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
- "sw%ddp w%ddp h%ddp\n",
+ "sw%ddp w%ddp h%ddp dir:%d\n",
sourcePos.file.string(), sourcePos.line,
config->mcc, config->mnc,
config->language[0] ? config->language[0] : '-',
@@ -3506,7 +3508,8 @@
config->screenHeight,
config->smallestScreenWidthDp,
config->screenWidthDp,
- config->screenHeightDp));
+ config->screenHeightDp,
+ config->layoutDirection));
} else {
NOISY(printf("New entry at %s:%d: NULL config\n",
sourcePos.file.string(), sourcePos.line));
diff --git a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
index bd332a6..26cb97b 100644
--- a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
@@ -31,6 +31,7 @@
*/
public class SystemClock_Delegate {
private static long sBootTime = System.currentTimeMillis();
+ private static long sBootTimeNano = System.nanoTime();
@LayoutlibDelegate
/*package*/ static boolean setCurrentTimeMillis(long millis) {
@@ -60,6 +61,16 @@
}
/**
+ * Returns nanoseconds since boot, including time spent in sleep.
+ *
+ * @return elapsed nanoseconds since boot.
+ */
+ @LayoutlibDelegate
+ /*package*/ static long elapsedRealtimeNano() {
+ return System.nanoTime() - sBootTimeNano;
+ }
+
+ /**
* Returns milliseconds running in the current thread.
*
* @return elapsed milliseconds in the thread
diff --git a/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
index 1df78c2..8b4c60b 100644
--- a/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
@@ -91,4 +91,42 @@
/*package*/ static float sqrt(float value) {
return (float)Math.sqrt(value);
}
+
+ /**
+ * Returns the closest float approximation of the raising "e" to the power
+ * of the argument.
+ *
+ * @param value to compute the exponential of
+ * @return the exponential of value
+ */
+ @LayoutlibDelegate
+ /*package*/ static float exp(float value) {
+ return (float)Math.exp(value);
+ }
+
+ /**
+ * Returns the closest float approximation of the result of raising {@code
+ * x} to the power of {@code y}.
+ *
+ * @param x the base of the operation.
+ * @param y the exponent of the operation.
+ * @return {@code x} to the power of {@code y}.
+ */
+ @LayoutlibDelegate
+ /*package*/ static float pow(float x, float y) {
+ return (float)Math.pow(x, y);
+ }
+
+ /**
+ * Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +} <i>
+ * {@code y}</i><sup>{@code 2}</sup>{@code )}.
+ *
+ * @param x a float number
+ * @param y a float number
+ * @return the hypotenuse
+ */
+ @LayoutlibDelegate
+ /*package*/ static float hypot(float x, float y) {
+ return (float)Math.sqrt(x*x + y*y);
+ }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index e629b75..428c4c2 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1164,6 +1164,13 @@
}
@Override
+ public Intent registerReceiverAsUser(BroadcastReceiver arg0, UserHandle arg0p5,
+ IntentFilter arg1, String arg2, Handler arg3) {
+ // pass
+ return null;
+ }
+
+ @Override
public void removeStickyBroadcast(Intent arg0) {
// pass