summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/java/android/app/ActivityManager.java41
-rw-r--r--core/java/android/app/ActivityManagerNative.java9
-rw-r--r--core/java/android/app/AppOpsManager.java22
-rw-r--r--core/java/android/app/IActivityManager.java3
-rw-r--r--core/java/android/app/VoiceInteractor.java30
-rw-r--r--core/java/android/content/res/Resources.java96
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java4
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java20
-rw-r--r--core/java/android/service/voice/VoiceInteractionSession.java10
-rw-r--r--core/java/android/text/Hyphenator.java33
-rw-r--r--core/java/android/widget/RadialTimePickerView.java187
-rw-r--r--core/java/com/android/internal/app/DumpHeapActivity.java37
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java6
-rw-r--r--core/jni/android/graphics/Paint.cpp3
14 files changed, 333 insertions, 168 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d143f8b..8f125d7 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2682,6 +2682,47 @@ public class ActivityManager {
}
/**
+ * Request that the system start watching for the calling process to exceed a pss
+ * size as given here. Once called, the system will look for any occassions where it
+ * sees the associated process with a larger pss size and, when this happens, automatically
+ * pull a heap dump from it and allow the user to share the data. Note that this request
+ * continues running even if the process is killed and restarted. To remove the watch,
+ * use {@link #clearWatchHeapLimit()}.
+ *
+ * <p>This API only work if running on a debuggable (userdebug or eng) build.</p>
+ *
+ * <p>Callers can optionally implement {@link #ACTION_REPORT_HEAP_LIMIT} to directly
+ * handle heap limit reports themselves.</p>
+ *
+ * @param pssSize The size in bytes to set the limit at.
+ */
+ public void setWatchHeapLimit(long pssSize) {
+ try {
+ ActivityManagerNative.getDefault().setDumpHeapDebugLimit(null, 0, pssSize,
+ mContext.getPackageName());
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Action an app can implement to handle reports from {@link #setWatchHeapLimit(long)}.
+ * If your package has an activity handling this action, it will be launched with the
+ * heap data provided to it the same way as {@link Intent#ACTION_SEND}. Note that to
+ * match the activty must support this action and a MIME type of "*&#47;*".
+ */
+ public static final String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
+
+ /**
+ * Clear a heap watch limit previously set by {@link #setWatchHeapLimit(long)}.
+ */
+ public void clearWatchHeapLimit() {
+ try {
+ ActivityManagerNative.getDefault().setDumpHeapDebugLimit(null, 0, 0, null);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* @hide
*/
public void startLockTaskMode(int taskId) {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 1484af8..be7287f 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2427,8 +2427,10 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
case SET_DUMP_HEAP_DEBUG_LIMIT_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String procName = data.readString();
+ int uid = data.readInt();
long maxMemSize = data.readLong();
- setDumpHeapDebugLimit(procName, maxMemSize);
+ String reportPackage = data.readString();
+ setDumpHeapDebugLimit(procName, uid, maxMemSize, reportPackage);
reply.writeNoException();
return true;
}
@@ -5644,12 +5646,15 @@ class ActivityManagerProxy implements IActivityManager
}
@Override
- public void setDumpHeapDebugLimit(String processName, long maxMemSize) throws RemoteException {
+ public void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize,
+ String reportPackage) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeString(processName);
+ data.writeInt(uid);
data.writeLong(maxMemSize);
+ data.writeString(reportPackage);
mRemote.transact(SET_DUMP_HEAP_DEBUG_LIMIT_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 4bd2332..381c20c 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -208,8 +208,12 @@ public class AppOpsManager {
public static final int OP_ACTIVATE_VPN = 47;
/** @hide Access the WallpaperManagerAPI to write wallpapers. */
public static final int OP_WRITE_WALLPAPER = 48;
+ /** @hide Received the assist structure from an app. */
+ public static final int OP_ASSIST_STRUCTURE = 49;
+ /** @hide Received a screenshot from assist. */
+ public static final int OP_ASSIST_SCREENSHOT = 50;
/** @hide */
- public static final int _NUM_OP = 49;
+ public static final int _NUM_OP = 51;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION =
@@ -288,6 +292,8 @@ public class AppOpsManager {
OP_PROJECT_MEDIA,
OP_ACTIVATE_VPN,
OP_WRITE_WALLPAPER,
+ OP_ASSIST_STRUCTURE,
+ OP_ASSIST_SCREENSHOT,
};
/**
@@ -344,6 +350,8 @@ public class AppOpsManager {
null,
OPSTR_ACTIVATE_VPN,
null,
+ null,
+ null,
};
/**
@@ -400,6 +408,8 @@ public class AppOpsManager {
"PROJECT_MEDIA",
"ACTIVATE_VPN",
"WRITE_WALLPAPER",
+ "ASSIST_STRUCTURE",
+ "ASSIST_SCREENSHOT"
};
/**
@@ -456,6 +466,8 @@ public class AppOpsManager {
null, // no permission for projecting media
null, // no permission for activating vpn
null, // no permission for supporting wallpaper
+ null, // no permission for receiving assist structure
+ null, // no permission for receiving assist screenshot
};
/**
@@ -513,6 +525,8 @@ public class AppOpsManager {
null, //PROJECT_MEDIA
UserManager.DISALLOW_CONFIG_VPN, // ACTIVATE_VPN
UserManager.DISALLOW_WALLPAPER, // WRITE_WALLPAPER
+ null, // ASSIST_STRUCTURE
+ null, // ASSIST_SCREENSHOT
};
/**
@@ -569,6 +583,8 @@ public class AppOpsManager {
false, //PROJECT_MEDIA
false, //ACTIVATE_VPN
false, //WALLPAPER
+ false, //ASSIST_STRUCTURE
+ false, //ASSIST_SCREENSHOT
};
/**
@@ -624,6 +640,8 @@ public class AppOpsManager {
AppOpsManager.MODE_IGNORED, // OP_PROJECT_MEDIA
AppOpsManager.MODE_IGNORED, // OP_ACTIVATE_VPN
AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_ALLOWED,
};
/**
@@ -683,6 +701,8 @@ public class AppOpsManager {
false,
false,
false,
+ false,
+ false,
};
private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index d794aa3..e20b0da 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -482,7 +482,8 @@ public interface IActivityManager extends IInterface {
public void systemBackupRestored() throws RemoteException;
public void notifyCleartextNetwork(int uid, byte[] firstPacket) throws RemoteException;
- public void setDumpHeapDebugLimit(String processName, long maxMemSize) throws RemoteException;
+ public void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize,
+ String reportPackage) throws RemoteException;
public void dumpHeapFinished(String path) throws RemoteException;
public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake)
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index da7bb05..7acf5f0 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -103,9 +103,9 @@ public class VoiceInteractor {
request = pullRequest((IVoiceInteractorRequest)args.arg1, true);
if (DEBUG) Log.d(TAG, "onCompleteVoice: req="
+ ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request
- + " result=" + args.arg1);
+ + " result=" + args.arg2);
if (request != null) {
- ((CompleteVoiceRequest)request).onCompleteResult((Bundle) args.arg1);
+ ((CompleteVoiceRequest)request).onCompleteResult((Bundle) args.arg2);
request.clear();
}
break;
@@ -297,6 +297,7 @@ public class VoiceInteractor {
*/
public static final class Option implements Parcelable {
final CharSequence mLabel;
+ final int mIndex;
ArrayList<CharSequence> mSynonyms;
Bundle mExtras;
@@ -308,6 +309,21 @@ public class VoiceInteractor {
*/
public Option(CharSequence label) {
mLabel = label;
+ mIndex = -1;
+ }
+
+ /**
+ * Creates an option that a user can select with their voice by matching the label
+ * or one of several synonyms.
+ * @param label The label that will both be matched against what the user speaks
+ * and displayed visually.
+ * @param index The location of this option within the overall set of options.
+ * Can be used to help identify which the option when it is returned from the
+ * voice interactor.
+ */
+ public Option(CharSequence label, int index) {
+ mLabel = label;
+ mIndex = index;
}
/**
@@ -328,6 +344,14 @@ public class VoiceInteractor {
return mLabel;
}
+ /**
+ * Return the index that was supplied in the constructor.
+ * If the option was constructed without an index, -1 is returned.
+ */
+ public int getIndex() {
+ return mIndex;
+ }
+
public int countSynonyms() {
return mSynonyms != null ? mSynonyms.size() : 0;
}
@@ -356,6 +380,7 @@ public class VoiceInteractor {
Option(Parcel in) {
mLabel = in.readCharSequence();
+ mIndex = in.readInt();
mSynonyms = in.readCharSequenceList();
mExtras = in.readBundle();
}
@@ -368,6 +393,7 @@ public class VoiceInteractor {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeCharSequence(mLabel);
+ dest.writeInt(mIndex);
dest.writeCharSequenceList(mSynonyms);
dest.writeBundle(mExtras);
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 299eb7e..334d180 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -117,6 +117,9 @@ public class Resources {
private static final LongSparseArray<android.content.res.ConstantState<ColorStateList>>
sPreloadedColorStateLists = new LongSparseArray<>();
+ private static final String CACHE_NOT_THEMED = "";
+ private static final String CACHE_NULL_THEME = "null_theme";
+
// Pool of TypedArrays targeted to this Resources object.
final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5);
@@ -2441,8 +2444,8 @@ public class Resources {
}
}
- // Next, check preloaded drawables. These are unthemed but may have
- // themeable attributes.
+ // Next, check preloaded drawables. These may contain unresolved theme
+ // attributes.
final ConstantState cs;
if (isColorDrawable) {
cs = sPreloadedColorDrawables.get(key);
@@ -2450,42 +2453,49 @@ public class Resources {
cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
}
- final Drawable dr;
+ Drawable dr;
if (cs != null) {
- final Drawable clonedDr = cs.newDrawable(this);
- if (theme != null) {
- dr = clonedDr.mutate();
- dr.applyTheme(theme);
- dr.clearMutated();
- } else {
- dr = clonedDr;
- }
+ dr = cs.newDrawable(this);
} else if (isColorDrawable) {
dr = new ColorDrawable(value.data);
} else {
- dr = loadDrawableForCookie(value, id, theme);
+ dr = loadDrawableForCookie(value, id, null);
+ }
+
+ // Determine if the drawable has unresolved theme attributes. If it
+ // does, we'll need to apply a theme and store it in a theme-specific
+ // cache.
+ final String cacheKey;
+ if (!dr.canApplyTheme()) {
+ cacheKey = CACHE_NOT_THEMED;
+ } else if (theme == null) {
+ cacheKey = CACHE_NULL_THEME;
+ } else {
+ cacheKey = theme.getKey();
+ dr = dr.mutate();
+ dr.applyTheme(theme);
+ dr.clearMutated();
}
// If we were able to obtain a drawable, store it in the appropriate
- // cache (either preload or themed).
+ // cache: preload, not themed, null theme, or theme-specific.
if (dr != null) {
dr.setChangingConfigurations(value.changingConfigurations);
- cacheDrawable(value, theme, isColorDrawable, caches, key, dr);
+ cacheDrawable(value, isColorDrawable, caches, cacheKey, key, dr);
}
return dr;
}
- private void cacheDrawable(TypedValue value, Theme theme, boolean isColorDrawable,
+ private void cacheDrawable(TypedValue value, boolean isColorDrawable,
ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
- long key, Drawable dr) {
+ String cacheKey, long key, Drawable dr) {
final ConstantState cs = dr.getConstantState();
if (cs == null) {
return;
}
if (mPreloading) {
- // Preloaded drawables never have a theme, but may be themeable.
final int changingConfigs = cs.getChangingConfigurations();
if (isColorDrawable) {
if (verifyPreloadConfig(changingConfigs, 0, value.resourceId, "drawable")) {
@@ -2507,14 +2517,13 @@ public class Resources {
}
} else {
synchronized (mAccessLock) {
- final String themeKey = theme == null ? "" : theme.mKey;
- LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey);
+ LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(cacheKey);
if (themedCache == null) {
// Clean out the caches before we add more. This shouldn't
// happen very often.
pruneCaches(caches);
themedCache = new LongSparseArray<>(1);
- caches.put(themeKey, themedCache);
+ caches.put(cacheKey, themedCache);
}
themedCache.put(key, new WeakReference<>(cs));
}
@@ -2612,46 +2621,47 @@ public class Resources {
ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
long key, Theme theme) {
synchronized (mAccessLock) {
- final String themeKey = theme != null ? theme.mKey : "";
- final LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey);
- if (themedCache != null) {
- final Drawable themedDrawable = getCachedDrawableLocked(themedCache, key);
- if (themedDrawable != null) {
- return themedDrawable;
- }
+ // First search theme-agnostic cache.
+ final Drawable unthemedDrawable = getCachedDrawableLocked(
+ caches, key, CACHE_NOT_THEMED);
+ if (unthemedDrawable != null) {
+ return unthemedDrawable;
}
- // No cached drawable, we'll need to create a new one.
- return null;
+ // Next search theme-specific cache.
+ final String themeKey = theme != null ? theme.getKey() : CACHE_NULL_THEME;
+ return getCachedDrawableLocked(caches, key, themeKey);
}
}
+ private Drawable getCachedDrawableLocked(
+ ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
+ long key, String themeKey) {
+ final LongSparseArray<WeakReference<ConstantState>> cache = caches.get(themeKey);
+ if (cache != null) {
+ final ConstantState entry = getConstantStateLocked(cache, key);
+ if (entry != null) {
+ return entry.newDrawable(this);
+ }
+ }
+ return null;
+ }
+
private ConstantState getConstantStateLocked(
LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) {
final WeakReference<ConstantState> wr = drawableCache.get(key);
- if (wr != null) { // we have the key
+ if (wr != null) {
final ConstantState entry = wr.get();
if (entry != null) {
- //Log.i(TAG, "Returning cached drawable @ #" +
- // Integer.toHexString(((Integer)key).intValue())
- // + " in " + this + ": " + entry);
return entry;
- } else { // our entry has been purged
+ } else {
+ // Our entry has been purged.
drawableCache.delete(key);
}
}
return null;
}
- private Drawable getCachedDrawableLocked(
- LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) {
- final ConstantState entry = getConstantStateLocked(drawableCache, key);
- if (entry != null) {
- return entry.newDrawable(this);
- }
- return null;
- }
-
@Nullable
ColorStateList loadColorStateList(TypedValue value, int id, Theme theme)
throws NotFoundException {
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 1503bf5..f28c96e 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2354,8 +2354,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>Camera devices that support the MANUAL_POST_PROCESSING capability will always contain
* at least one of below mode combinations:</p>
* <ul>
- * <li>CONTRAST_CURVE and FAST</li>
- * <li>GAMMA_VALUE, PRESET_CURVE, and FAST</li>
+ * <li>CONTRAST_CURVE, FAST and HIGH_QUALITY</li>
+ * <li>GAMMA_VALUE, PRESET_CURVE, FAST and HIGH_QUALITY</li>
* </ul>
* <p>This includes all FULL level devices.</p>
* <p><b>Range of valid values:</b><br>
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 7f901c8..85c574a 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -852,8 +852,8 @@ public abstract class CameraMetadata<TKey> {
/**
* <p>Color correction processing operates at improved
- * quality but reduced capture rate (relative to sensor raw
- * output).</p>
+ * quality but the capture rate might be reduced (relative to sensor
+ * raw output rate)</p>
* <p>Advanced white balance adjustments above and beyond
* the specified white balance pipeline may be applied.</p>
* <p>If AWB is enabled with <code>{@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} != OFF</code>, then
@@ -883,8 +883,8 @@ public abstract class CameraMetadata<TKey> {
public static final int COLOR_CORRECTION_ABERRATION_MODE_FAST = 1;
/**
- * <p>Aberration correction operates at improved quality but reduced
- * capture rate (relative to sensor raw output).</p>
+ * <p>Aberration correction operates at improved quality but the capture rate might be
+ * reduced (relative to sensor raw output rate)</p>
* @see CaptureRequest#COLOR_CORRECTION_ABERRATION_MODE
*/
public static final int COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY = 2;
@@ -1797,7 +1797,7 @@ public abstract class CameraMetadata<TKey> {
public static final int EDGE_MODE_FAST = 1;
/**
- * <p>Apply high-quality edge enhancement, at a cost of reducing output frame rate.</p>
+ * <p>Apply high-quality edge enhancement, at a cost of possibly reduced output frame rate.</p>
* @see CaptureRequest#EDGE_MODE
*/
public static final int EDGE_MODE_HIGH_QUALITY = 2;
@@ -1852,7 +1852,7 @@ public abstract class CameraMetadata<TKey> {
/**
* <p>High-quality hot pixel correction is applied, at a cost
- * of reducing frame rate relative to sensor raw output.</p>
+ * of possibly reduced frame rate relative to sensor raw output.</p>
* <p>The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p>
*
* @see CaptureResult#STATISTICS_HOT_PIXEL_MAP
@@ -1894,8 +1894,8 @@ public abstract class CameraMetadata<TKey> {
public static final int NOISE_REDUCTION_MODE_FAST = 1;
/**
- * <p>High-quality noise reduction is applied, at the cost of reducing frame rate
- * relative to sensor output.</p>
+ * <p>High-quality noise reduction is applied, at the cost of possibly reduced frame
+ * rate relative to sensor output.</p>
* @see CaptureRequest#NOISE_REDUCTION_MODE
*/
public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2;
@@ -2032,7 +2032,7 @@ public abstract class CameraMetadata<TKey> {
/**
* <p>Apply high-quality lens shading correction, at the
- * cost of reduced frame rate.</p>
+ * cost of possibly reduced frame rate.</p>
* @see CaptureRequest#SHADING_MODE
*/
public static final int SHADING_MODE_HIGH_QUALITY = 2;
@@ -2105,7 +2105,7 @@ public abstract class CameraMetadata<TKey> {
/**
* <p>High-quality gamma mapping and color enhancement will be applied, at
- * the cost of reduced frame rate compared to raw sensor output.</p>
+ * the cost of possibly reduced frame rate compared to raw sensor output.</p>
* @see CaptureRequest#TONEMAP_MODE
*/
public static final int TONEMAP_MODE_HIGH_QUALITY = 2;
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 4c31f80..20d7079 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -520,6 +520,10 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
mCallbacks, true);
}
+ public Context getContext() {
+ return mContext;
+ }
+
Request newRequest(IVoiceInteractorCallback callback) {
synchronized (this) {
Request req = new Request(callback, this);
@@ -832,6 +836,12 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
return false;
}
+ /**
+ * Called when the user presses the back button while focus is in the session UI. Note
+ * that this will only happen if the session UI has requested input focus in its window;
+ * otherwise, the back key will go to whatever window has focus and do whatever behavior
+ * it normally has there.
+ */
public void onBackPressed() {
hide();
}
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index f4dff9b..a99bdf5 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -30,7 +30,7 @@ import java.util.Locale;
*
* @hide
*/
-/* package */ class Hyphenator {
+public class Hyphenator {
// This class has deliberately simple lifetime management (no finalizer) because in
// the common case a process will use a very small number of locales.
@@ -45,20 +45,14 @@ import java.util.Locale;
}
public static long get(Locale locale) {
- synchronized (sMap) {
- Hyphenator result = sMap.get(locale);
- if (result == null) {
- result = loadHyphenator(locale);
- sMap.put(locale, result);
- }
- return result == null ? 0 : result.mNativePtr;
- }
+ Hyphenator result = sMap.get(locale);
+ return result == null ? 0 : result.mNativePtr;
}
private static Hyphenator loadHyphenator(Locale locale) {
// TODO: find pattern dictionary (from system location) that best matches locale
if (Locale.US.equals(locale)) {
- File f = new File("/data/local/tmp/hyph-en-us.pat.txt");
+ File f = new File(getSystemHyphenatorLocation(), "hyph-en-us.pat.txt");
try {
RandomAccessFile rf = new RandomAccessFile(f, "r");
byte[] buf = new byte[(int)rf.length()];
@@ -68,9 +62,26 @@ import java.util.Locale;
long nativePtr = StaticLayout.nLoadHyphenator(patternData);
return new Hyphenator(nativePtr);
} catch (IOException e) {
- Log.e(TAG, "error loading hyphenation " + f);
+ Log.e(TAG, "error loading hyphenation " + f, e);
}
}
return null;
}
+
+ private static File getSystemHyphenatorLocation() {
+ // TODO: move to a sensible location under system
+ return new File("/system/usr/hyphen-data");
+ }
+
+ /**
+ * Load hyphenation patterns at initialization time. We want to have patterns
+ * for all locales loaded and ready to use so we don't have to do any file IO
+ * on the UI thread when drawing text in different locales.
+ *
+ * @hide
+ */
+ public static void init() {
+ Locale l = Locale.US;
+ sMap.put(l, loadHyphenator(l));
+ }
}
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 143dea4..52e1728 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -79,8 +79,10 @@ public class RadialTimePickerView extends View {
// Transparent alpha level
private static final int ALPHA_TRANSPARENT = 0;
- private static final int DEGREES_FOR_ONE_HOUR = 30;
- private static final int DEGREES_FOR_ONE_MINUTE = 6;
+ private static final int HOURS_IN_DAY = 24;
+ private static final int MINUTES_IN_HOUR = 60;
+ private static final int DEGREES_FOR_ONE_HOUR = 360 / HOURS_IN_DAY;
+ private static final int DEGREES_FOR_ONE_MINUTE = 360 / MINUTES_IN_HOUR;
private static final int[] HOURS_NUMBERS = {12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
private static final int[] HOURS_NUMBERS_24 = {0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23};
@@ -140,8 +142,7 @@ public class RadialTimePickerView extends View {
private final float[] mInnerTextX = new float[12];
private final float[] mInnerTextY = new float[12];
- private final int[] mLineLength = new int[3];
- private final int[] mSelectionDegrees = new int[3];
+ private final int[] mSelectionDegrees = new int[2];
private final ArrayList<Animator> mHoursToMinutesAnims = new ArrayList<>();
private final ArrayList<Animator> mMinuteToHoursAnims = new ArrayList<>();
@@ -168,13 +169,13 @@ public class RadialTimePickerView extends View {
private int mYCenter;
private int mCircleRadius;
- private int mMinHypotenuseForInnerNumber;
- private int mMaxHypotenuseForOuterNumber;
- private int mHalfwayHypotenusePoint;
+ private int mMinDistForInnerNumber;
+ private int mMaxDistForOuterNumber;
+ private int mHalfwayDist;
private String[] mOuterTextHours;
private String[] mInnerTextHours;
- private String[] mOuterTextMinutes;
+ private String[] mMinutesText;
private AnimatorSet mTransition;
private int mAmOrPm;
@@ -462,11 +463,10 @@ public class RadialTimePickerView extends View {
private void setCurrentHourInternal(int hour, boolean callback, boolean autoAdvance) {
final int degrees = (hour % 12) * DEGREES_FOR_ONE_HOUR;
mSelectionDegrees[HOURS] = degrees;
- mSelectionDegrees[HOURS_INNER] = degrees;
// 0 is 12 AM (midnight) and 12 is 12 PM (noon).
final int amOrPm = (hour == 0 || (hour % 24) < 12) ? AM : PM;
- final boolean isOnInnerCircle = mIs24HourMode && hour >= 1 && hour <= 12;
+ final boolean isOnInnerCircle = getInnerCircleForHour(hour);
if (mAmOrPm != amOrPm || mIsOnInnerCircle != isOnInnerCircle) {
mAmOrPm = amOrPm;
mIsOnInnerCircle = isOnInnerCircle;
@@ -488,8 +488,7 @@ public class RadialTimePickerView extends View {
* @return the current hour between 0 and 23 (inclusive)
*/
public int getCurrentHour() {
- return getHourForDegrees(
- mSelectionDegrees[mIsOnInnerCircle ? HOURS_INNER : HOURS], mIsOnInnerCircle);
+ return getHourForDegrees(mSelectionDegrees[HOURS], mIsOnInnerCircle);
}
private int getHourForDegrees(int degrees, boolean innerCircle) {
@@ -497,11 +496,11 @@ public class RadialTimePickerView extends View {
if (mIs24HourMode) {
// Convert the 12-hour value into 24-hour time based on where the
// selector is positioned.
- if (innerCircle && hour == 0) {
- // Inner circle is 1 through 12.
+ if (!innerCircle && hour == 0) {
+ // Outer circle is 1 through 12.
hour = 12;
- } else if (!innerCircle && hour != 0) {
- // Outer circle is 13 through 23 and 0.
+ } else if (innerCircle && hour != 0) {
+ // Inner circle is 13 through 23 and 0.
hour += 12;
}
} else if (mAmOrPm == PM) {
@@ -510,6 +509,9 @@ public class RadialTimePickerView extends View {
return hour;
}
+ /**
+ * @param hour the hour in 24-hour time or 12-hour time
+ */
private int getDegreesForHour(int hour) {
// Convert to be 0-11.
if (mIs24HourMode) {
@@ -522,12 +524,19 @@ public class RadialTimePickerView extends View {
return hour * DEGREES_FOR_ONE_HOUR;
}
+ /**
+ * @param hour the hour in 24-hour time or 12-hour time
+ */
+ private boolean getInnerCircleForHour(int hour) {
+ return mIs24HourMode && (hour == 0 || hour > 12);
+ }
+
public void setCurrentMinute(int minute) {
setCurrentMinuteInternal(minute, true);
}
private void setCurrentMinuteInternal(int minute, boolean callback) {
- mSelectionDegrees[MINUTES] = (minute % 60) * DEGREES_FOR_ONE_MINUTE;
+ mSelectionDegrees[MINUTES] = (minute % MINUTES_IN_HOUR) * DEGREES_FOR_ONE_MINUTE;
invalidate();
@@ -572,6 +581,7 @@ public class RadialTimePickerView extends View {
initData();
invalidate();
+ mTouchHelper.invalidateRoot();
}
public void showMinutes(boolean animate) {
@@ -587,6 +597,7 @@ public class RadialTimePickerView extends View {
initData();
invalidate();
+ mTouchHelper.invalidateRoot();
}
private void initHoursAndMinutesText() {
@@ -608,7 +619,7 @@ public class RadialTimePickerView extends View {
mInnerTextHours = mHours12Texts;
}
- mOuterTextMinutes = mMinutesTexts;
+ mMinutesText = mMinutesTexts;
final int hoursAlpha = mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT;
mAlpha[HOURS].setValue(hoursAlpha);
@@ -627,9 +638,9 @@ public class RadialTimePickerView extends View {
mYCenter = getHeight() / 2;
mCircleRadius = Math.min(mXCenter, mYCenter);
- mMinHypotenuseForInnerNumber = mCircleRadius - mTextInset[HOURS_INNER] - mSelectorRadius;
- mMaxHypotenuseForOuterNumber = mCircleRadius - mTextInset[HOURS] + mSelectorRadius;
- mHalfwayHypotenusePoint = mCircleRadius - (mTextInset[HOURS] + mTextInset[HOURS_INNER]) / 2;
+ mMinDistForInnerNumber = mCircleRadius - mTextInset[HOURS_INNER] - mSelectorRadius;
+ mMaxDistForOuterNumber = mCircleRadius - mTextInset[HOURS] + mSelectorRadius;
+ mHalfwayDist = mCircleRadius - (mTextInset[HOURS] + mTextInset[HOURS_INNER]) / 2;
calculatePositionsHours();
calculatePositionsMinutes();
@@ -674,6 +685,7 @@ public class RadialTimePickerView extends View {
private void drawMinutes(Canvas canvas, float alphaMod) {
final int minutesAlpha = (int) (mAlpha[MINUTES].getValue() * alphaMod + 0.5f);
if (minutesAlpha > 0) {
+ // Draw the minute selector under the elements.
drawSelector(canvas, MINUTES, mSelectorPath, alphaMod);
// Exclude the selector region, then draw minutes with no
@@ -681,7 +693,7 @@ public class RadialTimePickerView extends View {
canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipPath(mSelectorPath, Region.Op.DIFFERENCE);
drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES],
- mOuterTextMinutes, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES],
+ mMinutesText, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES],
minutesAlpha, false, 0, false);
canvas.restore();
@@ -690,7 +702,7 @@ public class RadialTimePickerView extends View {
canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipPath(mSelectorPath, Region.Op.INTERSECT);
drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES],
- mOuterTextMinutes, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES],
+ mMinutesText, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES],
minutesAlpha, true, mSelectionDegrees[MINUTES], true);
canvas.restore();
}
@@ -718,7 +730,7 @@ public class RadialTimePickerView extends View {
// Calculate the current radius at which to place the selection circle.
final int selRadius = mSelectorRadius;
final int selLength = mCircleRadius - mTextInset[index];
- final double selAngleRad = Math.toRadians(mSelectionDegrees[index]);
+ final double selAngleRad = Math.toRadians(mSelectionDegrees[index % 2]);
final float selCenterX = mXCenter + selLength * (float) Math.sin(selAngleRad);
final float selCenterY = mYCenter - selLength * (float) Math.cos(selAngleRad);
@@ -734,10 +746,10 @@ public class RadialTimePickerView extends View {
}
// Draw the dot if we're between two items.
- final boolean shouldDrawDot = mSelectionDegrees[index] % 30 != 0;
+ final boolean shouldDrawDot = mSelectionDegrees[index % 2] % 30 != 0;
if (shouldDrawDot) {
final Paint dotPaint = mPaintSelector[index % 2][SELECTOR_DOT];
- dotPaint.setColor(color);
+ dotPaint.setColor(mSelectorDotColor);
canvas.drawCircle(selCenterX, selCenterY, mSelectorDotRadius, dotPaint);
}
@@ -898,56 +910,43 @@ public class RadialTimePickerView extends View {
}
private int getDegreesFromXY(float x, float y, boolean constrainOutside) {
- final double hypotenuse = Math.sqrt(
- (y - mYCenter) * (y - mYCenter) + (x - mXCenter) * (x - mXCenter));
+ // Ensure the point is inside the touchable area.
+ final int innerBound;
+ final int outerBound;
+ if (mIs24HourMode && mShowHours) {
+ innerBound = mMinDistForInnerNumber;
+ outerBound = mMaxDistForOuterNumber;
+ } else {
+ final int index = mShowHours ? HOURS : MINUTES;
+ final int center = mCircleRadius - mTextInset[index];
+ innerBound = center - mSelectorRadius;
+ outerBound = center + mSelectorRadius;
+ }
- // Basic check if we're outside the range of the disk
- if (constrainOutside && hypotenuse > mCircleRadius) {
+ final double dX = x - mXCenter;
+ final double dY = y - mYCenter;
+ final double distFromCenter = Math.sqrt(dX * dX + dY * dY);
+ if (distFromCenter < innerBound || constrainOutside && distFromCenter > outerBound) {
return -1;
}
- // Check
- if (mIs24HourMode && mShowHours) {
- if (hypotenuse >= mMinHypotenuseForInnerNumber
- && hypotenuse <= mHalfwayHypotenusePoint) {
- mIsOnInnerCircle = true;
- } else if ((hypotenuse <= mMaxHypotenuseForOuterNumber || !constrainOutside)
- && hypotenuse >= mHalfwayHypotenusePoint) {
- mIsOnInnerCircle = false;
- } else {
- return -1;
- }
+ // Convert to degrees.
+ final int degrees = (int) (Math.toDegrees(Math.atan2(dY, dX) + Math.PI / 2) + 0.5);
+ if (degrees < 0) {
+ return degrees + 360;
} else {
- final int index = (mShowHours) ? HOURS : MINUTES;
- final float length = (mCircleRadius - mTextInset[index]);
- final int distanceToNumber = (int) (hypotenuse - length);
- final int maxAllowedDistance = mTextInset[index];
- if (distanceToNumber < -maxAllowedDistance
- || (constrainOutside && distanceToNumber > maxAllowedDistance)) {
- return -1;
- }
+ return degrees;
}
+ }
- final float opposite = Math.abs(y - mYCenter);
- int degrees = (int) (Math.toDegrees(Math.asin(opposite / hypotenuse)) + 0.5);
-
- // Now we have to translate to the correct quadrant.
- final boolean rightSide = (x > mXCenter);
- final boolean topSide = (y < mYCenter);
- if (rightSide) {
- if (topSide) {
- degrees = 90 - degrees;
- } else {
- degrees = 90 + degrees;
- }
- } else {
- if (topSide) {
- degrees = 270 + degrees;
- } else {
- degrees = 270 - degrees;
- }
+ private boolean getInnerCircleFromXY(float x, float y) {
+ if (mIs24HourMode && mShowHours) {
+ final double dX = x - mXCenter;
+ final double dY = y - mYCenter;
+ final double distFromCenter = Math.sqrt(dX * dX + dY * dY);
+ return distFromCenter <= mHalfwayDist;
}
- return degrees;
+ return false;
}
boolean mChangedDuringTouch = false;
@@ -987,34 +986,28 @@ public class RadialTimePickerView extends View {
private boolean handleTouchInput(
float x, float y, boolean forceSelection, boolean autoAdvance) {
- // Calling getDegreesFromXY has side effects, so cache
- // whether we used to be on the inner circle.
- final boolean wasOnInnerCircle = mIsOnInnerCircle;
+ final boolean isOnInnerCircle = getInnerCircleFromXY(x, y);
final int degrees = getDegreesFromXY(x, y, false);
if (degrees == -1) {
return false;
}
- final int[] selectionDegrees = mSelectionDegrees;
final int type;
final int newValue;
final boolean valueChanged;
if (mShowHours) {
final int snapDegrees = snapOnly30s(degrees, 0) % 360;
- valueChanged = selectionDegrees[HOURS] != snapDegrees
- || selectionDegrees[HOURS_INNER] != snapDegrees
- || wasOnInnerCircle != mIsOnInnerCircle;
-
- selectionDegrees[HOURS] = snapDegrees;
- selectionDegrees[HOURS_INNER] = snapDegrees;
+ valueChanged = mIsOnInnerCircle != isOnInnerCircle
+ || mSelectionDegrees[HOURS] != snapDegrees;
+ mIsOnInnerCircle = isOnInnerCircle;
+ mSelectionDegrees[HOURS] = snapDegrees;
type = HOURS;
newValue = getCurrentHour();
} else {
final int snapDegrees = snapPrefer30s(degrees) % 360;
- valueChanged = selectionDegrees[MINUTES] != snapDegrees;
-
- selectionDegrees[MINUTES] = snapDegrees;
+ valueChanged = mSelectionDegrees[MINUTES] != snapDegrees;
+ mSelectionDegrees[MINUTES] = snapDegrees;
type = MINUTES;
newValue = getCurrentMinute();
}
@@ -1132,17 +1125,11 @@ public class RadialTimePickerView extends View {
@Override
protected int getVirtualViewAt(float x, float y) {
final int id;
-
- // Calling getDegreesXY() has side-effects, so we need to cache the
- // current inner circle value and restore after the call.
- final boolean wasOnInnerCircle = mIsOnInnerCircle;
final int degrees = getDegreesFromXY(x, y, true);
- final boolean isOnInnerCircle = mIsOnInnerCircle;
- mIsOnInnerCircle = wasOnInnerCircle;
-
if (degrees != -1) {
final int snapDegrees = snapOnly30s(degrees, 0) % 360;
if (mShowHours) {
+ final boolean isOnInnerCircle = getInnerCircleFromXY(x, y);
final int hour24 = getHourForDegrees(snapDegrees, isOnInnerCircle);
final int hour = mIs24HourMode ? hour24 : hour24To12(hour24);
id = makeId(TYPE_HOUR, hour);
@@ -1153,8 +1140,10 @@ public class RadialTimePickerView extends View {
// If the touched minute is closer to the current minute
// than it is to the snapped minute, return current.
+ final int currentOffset = getCircularDiff(current, touched, MINUTES_IN_HOUR);
+ final int snappedOffset = getCircularDiff(snapped, touched, MINUTES_IN_HOUR);
final int minute;
- if (Math.abs(current - touched) < Math.abs(snapped - touched)) {
+ if (currentOffset < snappedOffset) {
minute = current;
} else {
minute = snapped;
@@ -1168,6 +1157,20 @@ public class RadialTimePickerView extends View {
return id;
}
+ /**
+ * Returns the difference in degrees between two values along a circle.
+ *
+ * @param first value in the range [0,max]
+ * @param second value in the range [0,max]
+ * @param max the maximum value along the circle
+ * @return the difference in between the two values
+ */
+ private int getCircularDiff(int first, int second, int max) {
+ final int diff = Math.abs(first - second);
+ final int midpoint = max / 2;
+ return (diff > midpoint) ? (max - diff) : diff;
+ }
+
@Override
protected void getVisibleVirtualViews(IntArray virtualViewIds) {
if (mShowHours) {
@@ -1178,7 +1181,7 @@ public class RadialTimePickerView extends View {
}
} else {
final int current = getCurrentMinute();
- for (int i = 0; i < 60; i += MINUTE_INCREMENT) {
+ for (int i = 0; i < MINUTES_IN_HOUR; i += MINUTE_INCREMENT) {
virtualViewIds.add(makeId(TYPE_MINUTE, i));
// If the current minute falls between two increments,
@@ -1236,7 +1239,7 @@ public class RadialTimePickerView extends View {
if (value < current && nextValue > current) {
// The current value is between two snap values.
return makeId(type, current);
- } else if (nextValue < 60) {
+ } else if (nextValue < MINUTES_IN_HOUR) {
return makeId(type, nextValue);
}
}
@@ -1290,7 +1293,7 @@ public class RadialTimePickerView extends View {
final float centerRadius;
final float degrees;
if (type == TYPE_HOUR) {
- final boolean innerCircle = mIs24HourMode && value > 0 && value <= 12;
+ final boolean innerCircle = getInnerCircleForHour(value);
if (innerCircle) {
centerRadius = mCircleRadius - mTextInset[HOURS_INNER];
radius = mSelectorRadius;
diff --git a/core/java/com/android/internal/app/DumpHeapActivity.java b/core/java/com/android/internal/app/DumpHeapActivity.java
index 7e70b0c..0ce501e 100644
--- a/core/java/com/android/internal/app/DumpHeapActivity.java
+++ b/core/java/com/android/internal/app/DumpHeapActivity.java
@@ -17,13 +17,16 @@
package com.android.internal.app;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.DebugUtils;
+import android.util.Slog;
/**
* This activity is displayed when the system has collected a heap dump from
@@ -34,6 +37,8 @@ public class DumpHeapActivity extends Activity {
public static final String KEY_PROCESS = "process";
/** The size limit the process reached */
public static final String KEY_SIZE = "size";
+ /** Optional name of package to directly launch */
+ public static final String KEY_DIRECT_LAUNCH = "direct_launch";
// Broadcast action to determine when to delete the current dump heap data.
public static final String ACTION_DELETE_DUMPHEAP = "com.android.server.am.DELETE_DUMPHEAP";
@@ -54,6 +59,28 @@ public class DumpHeapActivity extends Activity {
mProcess = getIntent().getStringExtra(KEY_PROCESS);
mSize = getIntent().getLongExtra(KEY_SIZE, 0);
+
+ String directLaunch = getIntent().getStringExtra(KEY_DIRECT_LAUNCH);
+ if (directLaunch != null) {
+ Intent intent = new Intent(ActivityManager.ACTION_REPORT_HEAP_LIMIT);
+ intent.setPackage(directLaunch);
+ ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", JAVA_URI);
+ intent.setClipData(clip);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.setType(clip.getDescription().getMimeType(0));
+ intent.putExtra(Intent.EXTRA_STREAM, JAVA_URI);
+ try {
+ startActivity(intent);
+ scheduleDelete();
+ mHandled = true;
+ finish();
+ return;
+ } catch (ActivityNotFoundException e) {
+ Slog.i("DumpHeapActivity", "Unable to direct launch to " + directLaunch
+ + ": " + e.getMessage());
+ }
+ }
+
AlertDialog.Builder b = new AlertDialog.Builder(this,
android.R.style.Theme_Material_Light_Dialog_Alert);
b.setTitle(com.android.internal.R.string.dump_heap_title);
@@ -71,9 +98,7 @@ public class DumpHeapActivity extends Activity {
@Override
public void onClick(DialogInterface dialog, int which) {
mHandled = true;
- Intent broadcast = new Intent(ACTION_DELETE_DUMPHEAP);
- broadcast.putExtra(EXTRA_DELAY_DELETE, true);
- sendBroadcast(broadcast);
+ scheduleDelete();
Intent intent = new Intent(Intent.ACTION_SEND);
ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", JAVA_URI);
intent.setClipData(clip);
@@ -88,6 +113,12 @@ public class DumpHeapActivity extends Activity {
mDialog = b.show();
}
+ void scheduleDelete() {
+ Intent broadcast = new Intent(ACTION_DELETE_DUMPHEAP);
+ broadcast.putExtra(EXTRA_DELAY_DELETE, true);
+ sendBroadcast(broadcast);
+ }
+
@Override
protected void onStop() {
super.onStop();
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 49d565d..70f7b72 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -32,6 +32,7 @@ import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructPollfd;
+import android.text.Hyphenator;
import android.util.EventLog;
import android.util.Log;
import android.webkit.WebViewFactory;
@@ -182,6 +183,7 @@ public class ZygoteInit {
preloadResources();
preloadOpenGL();
preloadSharedLibraries();
+ preloadTextResources();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
WebViewFactory.prepareWebViewInZygote();
@@ -201,6 +203,10 @@ public class ZygoteInit {
}
}
+ private static void preloadTextResources() {
+ Hyphenator.init();
+ }
+
/**
* Performs Zygote process initialization. Loads and initializes
* commonly used classes.
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 873b516..4906f59 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -577,7 +577,8 @@ public:
Layout layout;
TypefaceImpl* typeface = getNativeTypeface(env, jpaint);
- MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray, index, count, textLength);
+ MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray + index, 0, count,
+ count);
result = layout.getAdvance();
env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
return result;