diff options
53 files changed, 1898 insertions, 1139 deletions
diff --git a/api/current.txt b/api/current.txt index fbda3c6f44d4..f1777dbab51a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4179,6 +4179,7 @@ package android.app.admin { 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 +4215,7 @@ package android.app.admin { 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 +4239,7 @@ package android.app.admin { 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 +4263,8 @@ package android.app.admin { 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 +6255,7 @@ package android.content.pm { 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 +6858,12 @@ package android.content.res { 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 +6892,11 @@ package android.content.res { 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 +6907,7 @@ package android.content.res { 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 diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c index ab64747ab801..9e83a670b44d 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 @@ int dexopt(const char *apk_path, uid_t uid, int is_public) 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 @@ int dexopt(const char *apk_path, uid_t uid, int is_public) 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 d559639b160e..652543fdd957 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 @@ fail: 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 @@ int main(const int argc, const char *argv[]) { 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/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java index 1c37414032a2..c8062caf888e 100644 --- a/core/java/android/app/admin/DeviceAdminInfo.java +++ b/core/java/android/app/admin/DeviceAdminInfo.java @@ -50,23 +50,23 @@ import java.util.HashMap; */ 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 @@ public final class DeviceAdminInfo implements Parcelable { * 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 @@ public final class DeviceAdminInfo implements Parcelable { * 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 @@ public final class DeviceAdminInfo implements Parcelable { * 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 final class DeviceAdminInfo implements Parcelable { */ 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 @@ public final class DeviceAdminInfo implements Parcelable { 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 @@ public final class DeviceAdminInfo implements Parcelable { 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 @@ public final class DeviceAdminInfo implements Parcelable { 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 @@ public final class DeviceAdminInfo implements Parcelable { 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 @@ public final class DeviceAdminInfo implements Parcelable { 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 @@ public final class DeviceAdminInfo implements Parcelable { 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 @@ public final class DeviceAdminInfo implements Parcelable { 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 @@ public final class DeviceAdminInfo implements Parcelable { } 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 final class DeviceAdminInfo implements Parcelable { 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 final class DeviceAdminInfo implements Parcelable { 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 final class DeviceAdminInfo implements Parcelable { 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 @@ public final class DeviceAdminInfo implements Parcelable { } 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 @@ public final class DeviceAdminInfo implements Parcelable { /** * 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 0b583960e6c3..4c55bb3be949 100755 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1155,6 +1155,16 @@ public class DevicePolicyManager { = "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 @@ public class DevicePolicyManager { } /** + * 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 9419a62b7de8..0b7ec1229e18 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -48,7 +48,7 @@ interface IDevicePolicyManager { 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 @@ interface IDevicePolicyManager { 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 @@ interface IDevicePolicyManager { 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 @@ interface IDevicePolicyManager { 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/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 303572911f82..0b320f0653cc 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -372,6 +372,12 @@ public class ActivityInfo extends ComponentInfo 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 @@ public class ActivityInfo extends ComponentInfo 0x0200, // SCREEN SIZE 0x2000, // SMALLEST SCREEN SIZE 0x0100, // DENSITY + 0x4000, // LAYOUT DIRECTION }; /** @hide * Convert Java change bits to native. @@ -434,8 +441,9 @@ public class ActivityInfo extends ComponentInfo * {@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 52b6498f80e5..0b7784222a12 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -125,7 +125,25 @@ public final class Configuration implements Parcelable, Comparable<Configuration * <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 @@ public final class Configuration implements Parcelable, Comparable<Configuration * 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 final class Configuration implements Parcelable, Comparable<Configuration 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 @@ public final class Configuration implements Parcelable, Comparable<Configuration 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 @@ public final class Configuration implements Parcelable, Comparable<Configuration } 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 @@ public final class Configuration implements Parcelable, Comparable<Configuration 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 @@ public final class Configuration implements Parcelable, Comparable<Configuration 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 @@ public final class Configuration implements Parcelable, Comparable<Configuration 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 @@ public final class Configuration implements Parcelable, Comparable<Configuration if (delta.compatSmallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { compatSmallestScreenWidthDp = delta.compatSmallestScreenWidthDp; } - if (delta.seq != 0) { seq = delta.seq; } @@ -807,6 +835,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration * 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 @@ public final class Configuration implements Parcelable, Comparable<Configuration 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 @@ public final class Configuration implements Parcelable, Comparable<Configuration && 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 @@ public final class Configuration implements Parcelable, Comparable<Configuration && densityDpi != delta.densityDpi) { changed |= ActivityInfo.CONFIG_DENSITY; } - + return changed; } @@ -963,7 +996,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration dest.writeInt(compatScreenWidthDp); dest.writeInt(compatScreenHeightDp); dest.writeInt(compatSmallestScreenWidthDp); - dest.writeInt(layoutDirection); dest.writeInt(seq); } @@ -992,7 +1024,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration compatScreenWidthDp = source.readInt(); compatScreenHeightDp = source.readInt(); compatSmallestScreenWidthDp = source.readInt(); - layoutDirection = source.readInt(); seq = source.readInt(); } @@ -1100,4 +1131,50 @@ public final class Configuration implements Parcelable, Comparable<Configuration 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 7559f1e70630..42a6bdceaee1 100755 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1444,12 +1444,14 @@ public class Resources { } 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 dc3608894115..bcb8800621e1 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -86,8 +86,8 @@ public class ScaleGestureDetector { * 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 @@ public class ScaleGestureDetector { private float mCurrSpan; private float mPrevSpan; + private float mInitialSpan; private float mCurrSpanX; private float mCurrSpanY; private float mPrevSpanX; @@ -135,6 +136,7 @@ public class ScaleGestureDetector { private long mCurrTime; private long mPrevTime; private boolean mInProgress; + private int mSpanSlop; /** * Consistency verifier for debugging purposes. @@ -146,6 +148,7 @@ public class ScaleGestureDetector { public ScaleGestureDetector(Context context, OnScaleGestureListener listener) { mContext = context; mListener = listener; + mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2; } /** @@ -176,6 +179,7 @@ public class ScaleGestureDetector { if (mInProgress) { mListener.onScaleEnd(this); mInProgress = false; + mInitialSpan = 0; } if (streamComplete) { @@ -221,18 +225,24 @@ public class ScaleGestureDetector { // 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 @@ public class ScaleGestureDetector { mCurrSpanX = spanX; mCurrSpanY = spanY; mCurrSpan = span; - mFocusX = focusX; - mFocusY = focusY; boolean updatePrev = true; if (mInProgress) { diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java index 591b87faffcb..93340366f570 100644 --- a/core/java/android/webkit/WebViewClassic.java +++ b/core/java/android/webkit/WebViewClassic.java @@ -1346,20 +1346,40 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc 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 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } } - 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 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc // 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 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc * 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 88301191c654..80a67826e1d6 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -204,7 +204,7 @@ class ZoomManager { */ 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 @@ class ZoomManager { return isZoomAnimating(); } - public ScaleGestureDetector getMultiTouchGestureDetector() { + public ScaleGestureDetector getScaleGestureDetector() { return mScaleDetector; } diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index e8bf9d958f69..485bd37a266e 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -379,7 +379,7 @@ public class Toast { // 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 7d1231e533bd..f173327fe69f 100644 --- a/core/java/com/android/internal/app/LocalePicker.java +++ b/core/java/com/android/internal/app/LocalePicker.java @@ -243,10 +243,9 @@ public class LocalePicker extends ListFragment { 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/res/res/layout-sw600dp/keyguard_screen_status_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml index df999f044999..c6ddd1bd2f2b 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 565785b263bf..765dc95116b4 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 f9e3ef8f4fb5..a03310152119 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 f8d05b767b41..37e6779fce15 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/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 1c3318df28b2..3a69937d504a 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/public.xml b/core/res/res/values/public.xml index 8e0eb155056f..c993c0b35666 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -710,6 +710,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 +720,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 +1295,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" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 381055fb4bc0..d2951bf6cd60 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/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h index 23bca3e2ad9f..48f5bf3ec162 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -957,6 +957,13 @@ struct ResTable_config 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 @@ struct ResTable_config 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 @@ struct ResTable_config * 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 8cce191e3e8b..069dfa3a34e7 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -1470,6 +1470,9 @@ int ResTable_config::compareLogical(const ResTable_config& o) const { 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 @@ bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const { } } + 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 @@ bool ResTable_config::isBetterThan(const ResTable_config& o, } } + 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 @@ bool ResTable_config::match(const ResTable_config& settings) const { } } 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 @@ String8 ResTable_config::toString() const { 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 c3a07a1a111f..c0f79dfb2098 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -6,6 +6,8 @@ include $(CLEAR_VARS) 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 95ccdfce36f1..86667eeea7ec 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 @@ FontRenderer::FontRenderer() { mMaxNumberOfQuads = 1024; mCurrentQuadIndex = 0; - mTextMeshPtr = NULL; + mTextMesh = NULL; mCurrentCacheTexture = NULL; mLastCacheTexture = NULL; @@ -636,20 +63,25 @@ FontRenderer::FontRenderer() { 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 @@ FontRenderer::~FontRenderer() { Caches::getInstance().unbindIndicesBuffer(); glDeleteBuffers(1, &mIndexBufferID); - delete[] mTextMeshPtr; + delete[] mTextMesh; } Vector<Font*> fontsToDereference = mActiveFonts; @@ -695,68 +127,34 @@ void FontRenderer::flushAllAndInvalidate() { 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 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp 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 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp 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* FontRenderer::createCacheTexture(int width, int height, bool alloc CacheTexture* cacheTexture = new CacheTexture(width, height); if (allocate) { - allocateTextureMemory(cacheTexture); + Caches::getInstance().activeTexture(0); + cacheTexture->allocateTexture(); } return cacheTexture; @@ -905,7 +305,7 @@ void FontRenderer::initVertexArrayBuffers() { 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 @@ void FontRenderer::checkTextureUpdate() { // 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 @@ void FontRenderer::checkTextureUpdate() { 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 @@ void FontRenderer::issueDrawCommand() { 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 @@ void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 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 @@ bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char 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 @@ void FontRenderer::verticalBlur(float* weights, int32_t radius, 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 @@ void FontRenderer::verticalBlur(float* weights, int32_t radius, } } *output = (uint8_t) blurredPixel; - output ++; + output++; } } } diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index 241b73eb7a3a..405db09c1776 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 @@ public: 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 @@ protected: 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 @@ protected: 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 @@ protected: 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 2f43be8964ae..7abcc6321721 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -1933,7 +1933,7 @@ status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) { // 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 @@ status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) { 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 000000000000..793282272866 --- /dev/null +++ b/libs/hwui/font/CacheTexture.cpp @@ -0,0 +1,193 @@ +/* + * 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 > 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 000000000000..daaafff36b2f --- /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 000000000000..6680a00bc3f9 --- /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 000000000000..34e1a685dff8 --- /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 000000000000..7cab31e8a2ab --- /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 000000000000..12247baa876a --- /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 7eb51ddacc37..74cf80e1f4f2 100644 --- a/native/android/configuration.cpp +++ b/native/android/configuration.cpp @@ -123,6 +123,11 @@ int32_t AConfiguration_getSmallestScreenWidthDp(AConfiguration* config) { 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 @@ void AConfiguration_setSmallestScreenWidthDp(AConfiguration* config, int32_t val 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/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 3e96f9bc75f0..e761847b788c 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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { 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 2551c04e271f..e0ba211c2e25 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 @@ public class KeyguardHostView extends KeyguardViewBase { protected void onAttachedToWindow() { super.onAttachedToWindow(); mAppWidgetHost.startListening(); - populateWidgets(); + maybePopulateWidgets(); } @Override @@ -581,7 +581,12 @@ public class KeyguardHostView extends KeyguardViewBase { 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 693856110c25..b2ce73e64206 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 @@ public class KeyguardPasswordView extends LinearLayout 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 @@ public class KeyguardPasswordView extends LinearLayout 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 294ea5ca8fe5..4861b781070e 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 @@ public class KeyguardSimPinView extends LinearLayout } }); } - - 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 06ed88a7794b..20fad0b9997c 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 @@ class KeyguardStatusViewManager { 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 409f87bf5f63..b6ffde0b8e69 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 @@ class KeyguardStatusViewManager implements OnClickListener { 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/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index 77b062c2d389..61517b11fac8 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -177,6 +177,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + /** + * 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/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 5993f327cd4a..37dae35e08c7 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.ApplicationInfo; 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 @@ import java.util.Set; * 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 @@ public class LocationManagerService extends ILocationManager.Stub implements Obs 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 @@ public class LocationManagerService extends ILocationManager.Stub implements Obs if (mGeocodeProvider == null) { Slog.e(TAG, "no geocoder provider found"); } - - updateProvidersLocked(); } /** @@ -544,14 +549,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Obs } } - /** 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/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index db2ec5b6d4e1..e670da083466 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.os.UserManager; 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; @@ -1530,7 +1531,8 @@ public final class ActivityManagerService extends ActivityManagerNative ConfigurationInfo.GL_ES_VERSION_UNDEFINED); mConfiguration.setToDefaults(); - mConfiguration.locale = Locale.getDefault(); + mConfiguration.setLocale(Locale.getDefault()); + mConfigurationSeq = mConfiguration.seq = 1; mProcessStats.init(); diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 24be75fad076..55da11f43406 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -208,8 +208,7 @@ public class PackageManagerService extends IPackageManager.Stub { /** * 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 diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 0d6de38078cc..c82fa55bc3f6 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.EventLog; 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,7 +453,6 @@ public class WindowManagerService extends IWindowManager.Stub int mSystemDecorLayer = 0; final Rect mScreenRect = new Rect(); - boolean mLayoutNeeded = true; boolean mTraversalScheduled = false; boolean mDisplayFrozen = false; boolean mWaitingForConfig = false; @@ -1768,7 +1766,7 @@ public class WindowManagerService extends IWindowManager.Stub 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(); @@ -2011,7 +2009,7 @@ public class WindowManagerService extends IWindowManager.Stub 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(); @@ -2310,7 +2308,7 @@ public class WindowManagerService extends IWindowManager.Stub //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(); @@ -2427,7 +2425,7 @@ public class WindowManagerService extends IWindowManager.Stub if (!mInLayout) { assignLayersLocked(windows); - mLayoutNeeded = true; + win.mDisplayContent.layoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); if (win.mAppToken != null) { win.mAppToken.updateReportedVisibilityLocked(); @@ -2493,7 +2491,7 @@ public class WindowManagerService extends IWindowManager.Stub w.mGivenVisibleInsets.scale(w.mGlobalScale); w.mGivenTouchableRegion.scale(w.mGlobalScale); } - mLayoutNeeded = true; + w.mDisplayContent.layoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); } } @@ -2588,7 +2586,7 @@ public class WindowManagerService extends IWindowManager.Stub 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(); } @@ -2856,7 +2854,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - mLayoutNeeded = true; + win.mDisplayContent.layoutNeeded = true; win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0; if (assignLayers) { assignLayersLocked(win.getWindowList()); @@ -2946,7 +2944,7 @@ public class WindowManagerService extends IWindowManager.Stub if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) { adjustWallpaperWindowsLocked(); } - mLayoutNeeded = true; + win.mDisplayContent.layoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); } } @@ -3460,11 +3458,11 @@ public class WindowManagerService extends IWindowManager.Stub win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT, false); changed = true; + win.mDisplayContent.layoutNeeded = true; } } if (changed) { - mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /*updateInputWindows*/); @@ -3720,7 +3718,7 @@ public class WindowManagerService extends IWindowManager.Stub if (computeScreenConfigurationLocked(mTempConfiguration)) { if (currentConfig.diff(mTempConfiguration) != 0) { mWaitingForConfig = true; - mLayoutNeeded = true; + getDefaultDisplayContent().layoutNeeded = true; startFreezingDisplayLocked(false); config = new Configuration(mTempConfiguration); } @@ -4086,7 +4084,7 @@ public class WindowManagerService extends IWindowManager.Stub updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/); - mLayoutNeeded = true; + getDefaultDisplayContent().layoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); Binder.restoreCallingIdentity(origId); return; @@ -4251,6 +4249,7 @@ public class WindowManagerService extends IWindowManager.Stub WindowManagerPolicy.TRANSIT_ENTER, true); } changed = true; + win.mDisplayContent.layoutNeeded = true; } } else if (win.isVisibleNow()) { if (!runningAppAnimation) { @@ -4258,6 +4257,7 @@ public class WindowManagerService extends IWindowManager.Stub WindowManagerPolicy.TRANSIT_EXIT, false); } changed = true; + win.mDisplayContent.layoutNeeded = true; } } @@ -4279,7 +4279,6 @@ public class WindowManagerService extends IWindowManager.Stub + wtoken.hiddenRequested); if (changed) { - mLayoutNeeded = true; mInputMonitor.setUpdateInputWindowsNeededLw(); if (performLayout) { updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, @@ -4407,6 +4406,7 @@ public class WindowManagerService extends IWindowManager.Stub mInnerFields.mOrientationChangeComplete = false; } unfrozeWindows = true; + w.mDisplayContent.layoutNeeded = true; } } if (force || unfrozeWindows) { @@ -4416,7 +4416,6 @@ public class WindowManagerService extends IWindowManager.Stub } if (unfreezeSurfaceNow) { if (unfrozeWindows) { - mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); } stopFreezingDisplayLocked(); @@ -4761,13 +4760,15 @@ public class WindowManagerService extends IWindowManager.Stub 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*/); @@ -4807,7 +4808,10 @@ public class WindowManagerService extends IWindowManager.Stub 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*/)) { @@ -4820,7 +4824,6 @@ public class WindowManagerService extends IWindowManager.Stub // Note that the above updateFocusedWindowLocked conditional used to sit here. - mLayoutNeeded = true; if (!mInLayout) { performLayoutAndPlaceSurfacesLocked(); } @@ -4849,7 +4852,11 @@ public class WindowManagerService extends IWindowManager.Stub 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, @@ -4862,7 +4869,6 @@ public class WindowManagerService extends IWindowManager.Stub // Note that the above updateFocusedWindowLocked used to sit here. - mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); mInputMonitor.updateInputWindowsLw(false /*force*/); @@ -5656,7 +5662,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { changed = updateRotationUncheckedLocked(false); if (!changed || forceRelayout) { - mLayoutNeeded = true; + getDefaultDisplayContent().layoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); } } @@ -5734,7 +5740,7 @@ public class WindowManagerService extends IWindowManager.Stub 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); @@ -6392,7 +6398,8 @@ public class WindowManagerService extends IWindowManager.Stub 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, @@ -7578,7 +7585,7 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.setInitialDisplaySize(mDefaultDisplay, displayContent.mBaseDisplayWidth, displayContent.mBaseDisplayHeight, displayContent.mBaseDisplayDensity); - mLayoutNeeded = true; + displayContent.layoutNeeded = true; boolean configChanged = updateOrientationFromAppTokensLocked(false); mTempConfiguration.setToDefaults(); @@ -7837,7 +7844,7 @@ public class WindowManagerService extends IWindowManager.Stub mInLayout = false; - if (mLayoutNeeded) { + if (needsLayout()) { if (++mLayoutRepeatCount < 6) { requestTraversalLocked(); } else { @@ -7862,11 +7869,11 @@ public class WindowManagerService extends IWindowManager.Stub 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; @@ -7883,7 +7890,7 @@ public class WindowManagerService extends IWindowManager.Stub 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; @@ -8028,8 +8035,7 @@ public class WindowManagerService extends IWindowManager.Stub /** * 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) { @@ -8285,7 +8291,7 @@ public class WindowManagerService extends IWindowManager.Stub // 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)) { @@ -8573,19 +8579,18 @@ public class WindowManagerService extends IWindowManager.Stub 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 = - displayContent.getDisplayId() == Display.DEFAULT_DISPLAY; + final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); int repeats = 0; do { repeats++; if (repeats > 6) { Slog.w(TAG, "Animation repeat aborted after too many iterations"); - mLayoutNeeded = false; displayContent.layoutNeeded = false; break; } @@ -8598,7 +8603,6 @@ public class WindowManagerService extends IWindowManager.Stub && ((adjustWallpaperWindowsLocked() & ADJUST_WALLPAPER_LAYERS_CHANGED) != 0)) { assignLayersLocked(windows); - mLayoutNeeded = true; displayContent.layoutNeeded = true; } @@ -8606,7 +8610,6 @@ public class WindowManagerService extends IWindowManager.Stub & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) { if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout"); if (updateOrientationFromAppTokensLocked(true)) { - mLayoutNeeded = true; displayContent.layoutNeeded = true; mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); } @@ -8614,7 +8617,6 @@ public class WindowManagerService extends IWindowManager.Stub if ((displayContent.pendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) { - mLayoutNeeded = true; displayContent.layoutNeeded = true; } @@ -8857,7 +8859,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - if (mLayoutNeeded) { + if (needsLayout()) { defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded", defaultDisplay.pendingLayoutChanges); @@ -8968,15 +8970,15 @@ public class WindowManagerService extends IWindowManager.Stub mRelayoutWhileAnimating.clear(); } - if (wallpaperDestroyed) { - mLayoutNeeded |= adjustWallpaperWindowsLocked() != 0; + if (wallpaperDestroyed && (adjustWallpaperWindowsLocked() != 0)) { + getDefaultDisplayContent().layoutNeeded = true; } DisplayContentsIterator iterator = new DisplayContentsIterator(); while (iterator.hasNext()) { DisplayContent displayContent = iterator.next(); if (displayContent.pendingLayoutChanges != 0) { - mLayoutNeeded = true; + displayContent.layoutNeeded = true; } } @@ -9014,8 +9016,8 @@ public class WindowManagerService extends IWindowManager.Stub } } - if (mInnerFields.mOrientationChangeComplete && !mLayoutNeeded && - !mInnerFields.mUpdateRotation) { + if (mInnerFields.mOrientationChangeComplete && !defaultDisplay.layoutNeeded + && !mInnerFields.mUpdateRotation) { checkDrawnWindowsLocked(); } @@ -9037,8 +9039,8 @@ public class WindowManagerService extends IWindowManager.Stub for (DisplayContent displayContent : displayList) { assignLayersLocked(displayContent.getWindowList()); + displayContent.layoutNeeded = true; } - mLayoutNeeded = true; } // Check to see if we are now in a state where the screen should @@ -9048,8 +9050,8 @@ public class WindowManagerService extends IWindowManager.Stub updateLayoutToAnimationLocked(); if (DEBUG_WINDOW_TRACE) { - Slog.e(TAG, "performLayoutAndPlaceSurfacesLockedInner exit: mLayoutNeeded=" - + mLayoutNeeded + " animating=" + mAnimator.mAnimating); + Slog.e(TAG, "performLayoutAndPlaceSurfacesLockedInner exit: animating=" + + mAnimator.mAnimating); } } @@ -9205,6 +9207,16 @@ public class WindowManagerService extends IWindowManager.Stub 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; @@ -9381,7 +9393,7 @@ public class WindowManagerService extends IWindowManager.Stub 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); @@ -9395,7 +9407,7 @@ public class WindowManagerService extends IWindowManager.Stub 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); } @@ -10057,8 +10069,18 @@ public class WindowManagerService extends IWindowManager.Stub } 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 a52e1d7bc72e..6711445428b2 100644 --- a/services/java/com/android/server/wm/WindowState.java +++ b/services/java/com/android/server/wm/WindowState.java @@ -587,10 +587,12 @@ final class WindowState implements WindowManagerPolicy.WindowState { } } + @Override public int getSystemUiVisibility() { return mSystemUiVisibility; } + @Override public int getSurfaceLayer() { return mLayer; } @@ -598,7 +600,11 @@ final class WindowState implements WindowManagerPolicy.WindowState { 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 a7b116af3497..9bb72995bf6e 100644 --- a/services/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/java/com/android/server/wm/WindowStateAnimator.java @@ -335,7 +335,7 @@ class WindowStateAnimator { + mWin.mPolicyVisibilityAfterAnim); } mWin.mPolicyVisibility = mWin.mPolicyVisibilityAfterAnim; - mService.mLayoutNeeded = true; + mWin.mDisplayContent.layoutNeeded = true; if (!mWin.mPolicyVisibility) { if (mService.mCurrentFocus == mWin) { mService.mFocusMayChange = true; @@ -1362,7 +1362,7 @@ class WindowStateAnimator { // 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/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java index 07626a350a79..327ff06c46db 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 @@ public class ImageProcessingActivity extends Activity 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 46b8a274b1af..438a670452e4 100644 --- a/tools/aapt/AaptAssets.cpp +++ b/tools/aapt/AaptAssets.cpp @@ -183,6 +183,13 @@ AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value) 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 @@ AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis) 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 @@ AaptGroupEntry::initFromDirName(const char* dir, String8* resType) 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 @@ AaptGroupEntry::initFromDirName(const char* dir, String8* resType) //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 @@ success: this->navHidden = navHidden; this->navigation = nav; this->screenSize = size; + this->layoutDirection = layoutDir; this->version = vers; // what is this anyway? @@ -691,6 +713,8 @@ AaptGroupEntry::toString() const s += ","; s += this->locale; s += ","; + s += layoutDirection; + s += ","; s += smallestScreenWidthDp; s += ","; s += screenWidthDp; @@ -747,6 +771,12 @@ AaptGroupEntry::toDirName(const String8& resType) const } s += locale; } + if (this->layoutDirection != "") { + if (s.length() > 0) { + s += "-"; + } + s += layoutDirection; + } if (this->smallestScreenWidthDp != "") { if (s.length() > 0) { s += "-"; @@ -958,6 +988,28 @@ bool AaptGroupEntry::getLocaleName(const char* fileName, 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 AaptGroupEntry::compare(const AaptGroupEntry& o) const int v = mcc.compare(o.mcc); if (v == 0) v = mnc.compare(o.mnc); if (v == 0) v = locale.compare(o.locale); + if (v == 0) v = layoutDirection.compare(o.layoutDirection); if (v == 0) v = vendor.compare(o.vendor); if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp); if (v == 0) v = screenWidthDp.compare(o.screenWidthDp); @@ -1447,6 +1500,7 @@ const ResTable_config& AaptGroupEntry::toParams() const 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 d5f296cb5238..5cfa91350063 100644 --- a/tools/aapt/AaptAssets.h +++ b/tools/aapt/AaptAssets.h @@ -51,6 +51,7 @@ enum { AXIS_SMALLESTSCREENWIDTHDP, AXIS_SCREENWIDTHDP, AXIS_SCREENHEIGHTDP, + AXIS_LAYOUTDIR, AXIS_VERSION, AXIS_START = AXIS_MCC, @@ -95,6 +96,7 @@ public: 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 @@ private: 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 d98fe65b7c95..3d7b0889f0e2 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -2811,7 +2811,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) 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 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) config.screenHeight, config.smallestScreenWidthDp, config.screenWidthDp, - config.screenHeightDp)); + config.screenHeightDp, + config.layoutDirection)); if (filterable && !filter.match(config)) { continue; @@ -2853,7 +2854,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) 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 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) 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 @@ sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry, 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 @@ sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry, 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 bd332a68f8fb..26cb97b63c52 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 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; */ 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 @@ public class SystemClock_Delegate { } /** + * 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 1df78c2efaac..8b4c60b5fead 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 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /*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); + } } |