diff options
Diffstat (limited to 'core/java')
18 files changed, 426 insertions, 193 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 5d1d461..1230c81 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3986,31 +3986,39 @@ public final class ActivityThread { dalvik.system.VMRuntime.getRuntime().clearGrowthLimit(); } - // If the app is being launched for full backup or restore, bring it up in - // a restricted environment with the base application class. - Application app = data.info.makeApplication(data.restrictedBackupMode, null); - mInitialApplication = app; - - // don't bring up providers in restricted mode; they may depend on the - // app's custom Application class - if (!data.restrictedBackupMode){ - List<ProviderInfo> providers = data.providers; - if (providers != null) { - installContentProviders(app, providers); - // For process that contains content providers, we want to - // ensure that the JIT is enabled "at some point". - mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000); + // Allow disk access during application and provider setup. This could + // block processing ordered broadcasts, but later processing would + // probably end up doing the same disk access. + final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites(); + try { + // If the app is being launched for full backup or restore, bring it up in + // a restricted environment with the base application class. + Application app = data.info.makeApplication(data.restrictedBackupMode, null); + mInitialApplication = app; + + // don't bring up providers in restricted mode; they may depend on the + // app's custom Application class + if (!data.restrictedBackupMode) { + List<ProviderInfo> providers = data.providers; + if (providers != null) { + installContentProviders(app, providers); + // For process that contains content providers, we want to + // ensure that the JIT is enabled "at some point". + mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000); + } } - } - try { - mInstrumentation.callApplicationOnCreate(app); - } catch (Exception e) { - if (!mInstrumentation.onException(app, e)) { - throw new RuntimeException( - "Unable to create application " + app.getClass().getName() - + ": " + e.toString(), e); + try { + mInstrumentation.callApplicationOnCreate(app); + } catch (Exception e) { + if (!mInstrumentation.onException(app, e)) { + throw new RuntimeException( + "Unable to create application " + app.getClass().getName() + + ": " + e.toString(), e); + } } + } finally { + StrictMode.setThreadPolicy(savedPolicy); } } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index a1198de..111f45e 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1646,6 +1646,17 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a {@link + * android.os.IUpdateLock} for managing runtime sequences that + * must not be interrupted by headless OTA application or similar. + * + * @hide + * @see #getSystemService + * @see android.os.UpdateLock + */ + public static final String UPDATE_LOCK_SERVICE = "updatelock"; + + /** + * Use with {@link #getSystemService} to retrieve a {@link * android.net.NetworkManagementService} for handling management of * system network services * diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index e04b2f7..079f739 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -20,6 +20,7 @@ import android.content.pm.ActivityInfo; import android.os.Parcel; import android.os.Parcelable; import android.util.LocaleUtil; +import android.view.View; import java.util.Locale; @@ -280,9 +281,9 @@ public final class Configuration implements Parcelable, Comparable<Configuration public int compatSmallestScreenWidthDp; /** - * @hide The text layout direction associated to the current Locale + * @hide The layout direction associated to the current Locale */ - public int textLayoutDirection; + public int layoutDirection; /** * @hide Internal book-keeping. @@ -310,7 +311,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration mnc = o.mnc; if (o.locale != null) { locale = (Locale) o.locale.clone(); - textLayoutDirection = o.textLayoutDirection; + layoutDirection = o.layoutDirection; } userSetLocale = o.userSetLocale; touchscreen = o.touchscreen; @@ -346,10 +347,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration } else { sb.append(" (no locale)"); } - switch (textLayoutDirection) { - case LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE: /* ltr not interesting */ break; - case LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE: sb.append(" rtl"); break; - default: sb.append(" layoutdir="); sb.append(textLayoutDirection); break; + 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; } if (smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { sb.append(" sw"); sb.append(smallestScreenWidthDp); sb.append("dp"); @@ -472,7 +473,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration screenWidthDp = compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED; screenHeightDp = compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED; smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; - textLayoutDirection = LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE; + layoutDirection = View.LAYOUT_DIRECTION_LTR; seq = 0; } @@ -508,7 +509,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration changed |= ActivityInfo.CONFIG_LOCALE; locale = delta.locale != null ? (Locale) delta.locale.clone() : null; - textLayoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale); + layoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale); } if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0))) { @@ -776,7 +777,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration dest.writeInt(compatScreenWidthDp); dest.writeInt(compatScreenHeightDp); dest.writeInt(compatSmallestScreenWidthDp); - dest.writeInt(textLayoutDirection); + dest.writeInt(layoutDirection); dest.writeInt(seq); } @@ -804,7 +805,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration compatScreenWidthDp = source.readInt(); compatScreenHeightDp = source.readInt(); compatSmallestScreenWidthDp = source.readInt(); - textLayoutDirection = source.readInt(); + layoutDirection = source.readInt(); seq = source.readInt(); } diff --git a/core/java/android/os/IUpdateLock.aidl b/core/java/android/os/IUpdateLock.aidl new file mode 100644 index 0000000..4492fb8 --- /dev/null +++ b/core/java/android/os/IUpdateLock.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** + * Direct interface to the UpdateLockService's functionality + * + * {@hide} + */ +interface IUpdateLock { + void acquireUpdateLock(IBinder token, String tag); + void releaseUpdateLock(IBinder token); +} diff --git a/core/java/android/os/TokenWatcher.java b/core/java/android/os/TokenWatcher.java index ac3cc92..9b3a2d6 100755 --- a/core/java/android/os/TokenWatcher.java +++ b/core/java/android/os/TokenWatcher.java @@ -16,6 +16,8 @@ package android.os; +import java.io.PrintWriter; +import java.util.ArrayList; import java.util.WeakHashMap; import java.util.Set; import android.util.Log; @@ -115,15 +117,31 @@ public abstract class TokenWatcher public void dump() { + ArrayList<String> a = dumpInternal(); + for (String s : a) { + Log.i(mTag, s); + } + } + + public void dump(PrintWriter pw) { + ArrayList<String> a = dumpInternal(); + for (String s : a) { + pw.println(s); + } + } + + private ArrayList<String> dumpInternal() { + ArrayList<String> a = new ArrayList<String>(); synchronized (mTokens) { Set<IBinder> keys = mTokens.keySet(); - Log.i(mTag, "Token count: " + mTokens.size()); + a.add("Token count: " + mTokens.size()); int i = 0; for (IBinder b: keys) { - Log.i(mTag, "[" + i + "] " + mTokens.get(b).tag + " - " + b); + a.add("[" + i + "] " + mTokens.get(b).tag + " - " + b); i++; } } + return a; } private Runnable mNotificationTask = new Runnable() { diff --git a/core/java/android/os/UpdateLock.java b/core/java/android/os/UpdateLock.java new file mode 100644 index 0000000..4060326 --- /dev/null +++ b/core/java/android/os/UpdateLock.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.content.Context; +import android.util.Log; + +/** + * Advisory wakelock-like mechanism by which processes that should not be interrupted for + * OTA/update purposes can so advise the OS. This is particularly relevant for headless + * or kiosk-like operation. + * + * @hide + */ +public class UpdateLock { + private static final boolean DEBUG = false; + private static final String TAG = "UpdateLock"; + + private static IUpdateLock sService; + private static void checkService() { + if (sService == null) { + sService = IUpdateLock.Stub.asInterface( + ServiceManager.getService(Context.UPDATE_LOCK_SERVICE)); + } + } + + IBinder mToken; + int mCount = 0; + boolean mRefCounted = true; + boolean mHeld = false; + final String mTag; + + /** + * Broadcast Intent action sent when the global update lock state changes, + * i.e. when the first locker acquires an update lock, or when the last + * locker releases theirs. The broadcast is sticky but is sent only to + * registered receivers. + */ + public static final String UPDATE_LOCK_CHANGED = "android.os.UpdateLock.UPDATE_LOCK_CHANGED"; + + /** + * Boolean Intent extra on the UPDATE_LOCK_CHANGED sticky broadcast, indicating + * whether now is an appropriate time to interrupt device activity with an + * update operation. True means that updates are okay right now; false indicates + * that perhaps later would be a better time. + */ + public static final String NOW_IS_CONVENIENT = "nowisconvenient"; + + /** + * Long Intent extra on the UPDATE_LOCK_CHANGED sticky broadcast, marking the + * wall-clock time [in UTC] at which the broadcast was sent. Note that this is + * in the System.currentTimeMillis() time base, which may be non-monotonic especially + * around reboots. + */ + public static final String TIMESTAMP = "timestamp"; + + /** + * Construct an UpdateLock instance. + * @param tag An arbitrary string used to identify this lock instance in dump output. + */ + public UpdateLock(String tag) { + mTag = tag; + mToken = new Binder(); + } + + /** + * Change the refcount behavior of this update lock. + */ + public void setReferenceCounted(boolean isRefCounted) { + if (DEBUG) { + Log.v(TAG, "setting refcounted=" + isRefCounted + " : " + this); + } + mRefCounted = isRefCounted; + } + + /** + * Is this lock currently held? + */ + public boolean isHeld() { + synchronized (mToken) { + return mHeld; + } + } + + /** + * Acquire an update lock. + */ + public void acquire() { + if (DEBUG) { + Log.v(TAG, "acquire() : " + this, new RuntimeException("here")); + } + checkService(); + synchronized (mToken) { + acquireLocked(); + } + } + + private void acquireLocked() { + if (!mRefCounted || mCount++ == 0) { + if (sService != null) { + try { + sService.acquireUpdateLock(mToken, mTag); + } catch (RemoteException e) { + Log.e(TAG, "Unable to contact service to acquire"); + } + } + mHeld = true; + } + } + + /** + * Release this update lock. + */ + public void release() { + if (DEBUG) Log.v(TAG, "release() : " + this, new RuntimeException("here")); + checkService(); + synchronized (mToken) { + releaseLocked(); + } + } + + private void releaseLocked() { + if (!mRefCounted || --mCount == 0) { + if (sService != null) { + try { + sService.releaseUpdateLock(mToken); + } catch (RemoteException e) { + Log.e(TAG, "Unable to contact service to release"); + } + } + mHeld = false; + } + if (mCount < 0) { + throw new RuntimeException("UpdateLock under-locked"); + } + } + + @Override + protected void finalize() throws Throwable { + synchronized (mToken) { + // if mHeld is true, sService must be non-null + if (mHeld) { + Log.wtf(TAG, "UpdateLock finalized while still held"); + try { + sService.releaseUpdateLock(mToken); + } catch (RemoteException e) { + Log.e(TAG, "Unable to contact service to release"); + } + } + } + } +} diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java index ca7263c..8c97293 100644 --- a/core/java/android/text/Html.java +++ b/core/java/android/text/Html.java @@ -16,6 +16,7 @@ package android.text; +import com.android.internal.util.ArrayUtils; import org.ccil.cowan.tagsoup.HTMLSchema; import org.ccil.cowan.tagsoup.Parser; import org.xml.sax.Attributes; @@ -45,13 +46,11 @@ import android.text.style.TextAppearanceSpan; import android.text.style.TypefaceSpan; import android.text.style.URLSpan; import android.text.style.UnderlineSpan; -import android.util.Log; import com.android.internal.util.XmlUtils; import java.io.IOException; import java.io.StringReader; -import java.nio.CharBuffer; import java.util.HashMap; /** @@ -203,9 +202,26 @@ public class Html { } } + private static String getOpenParaTagWithDirection(Spanned text, int start, int end) { + final int len = end - start; + final byte[] levels = new byte[ArrayUtils.idealByteArraySize(len)]; + final char[] buffer = TextUtils.obtain(len); + TextUtils.getChars(text, start, end, buffer, 0); + + int paraDir = AndroidBidi.bidi(Layout.DIR_REQUEST_DEFAULT_LTR, buffer, levels, len, + false /* no info */); + switch(paraDir) { + case Layout.DIR_RIGHT_TO_LEFT: + return "<p dir=rtl>"; + case Layout.DIR_LEFT_TO_RIGHT: + default: + return "<p dir=ltr>"; + } + } + private static void withinBlockquote(StringBuilder out, Spanned text, int start, int end) { - out.append("<p>"); + out.append(getOpenParaTagWithDirection(text, start, end)); int next; for (int i = start; i < end; i = next) { @@ -340,7 +356,7 @@ public class Html { } } - String p = last ? "" : "</p>\n<p>"; + String p = last ? "" : "</p>\n" + getOpenParaTagWithDirection(text, start, end); if (nl == 1) { out.append("<br>\n"); @@ -350,7 +366,6 @@ public class Html { for (int i = 2; i < nl; i++) { out.append("<br>"); } - out.append(p); } } diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java index ae41eab..6ca6161 100644 --- a/core/java/android/text/TextDirectionHeuristics.java +++ b/core/java/android/text/TextDirectionHeuristics.java @@ -18,6 +18,7 @@ package android.text; import android.util.LocaleUtil; +import android.view.View; /** * Some objects that implement TextDirectionHeuristic. @@ -240,7 +241,7 @@ public class TextDirectionHeuristics { @Override protected boolean defaultIsRtl() { final int dir = LocaleUtil.getLayoutDirectionFromLocale(java.util.Locale.getDefault()); - return (dir == LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE); + return (dir == View.LAYOUT_DIRECTION_RTL); } public static final TextDirectionHeuristicLocale INSTANCE = diff --git a/core/java/android/util/LocaleUtil.java b/core/java/android/util/LocaleUtil.java index 4d773f6..9953252 100644 --- a/core/java/android/util/LocaleUtil.java +++ b/core/java/android/util/LocaleUtil.java @@ -18,6 +18,7 @@ package android.util; import java.util.Locale; +import android.view.View; import libcore.icu.ICU; /** @@ -29,16 +30,6 @@ public class LocaleUtil { private LocaleUtil() { /* cannot be instantiated */ } - /** - * @hide Do not use. Implementation not finished. - */ - public static final int TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE = 0; - - /** - * @hide Do not use. Implementation not finished. - */ - public static final int TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE = 1; - private static String ARAB_SCRIPT_SUBTAG = "Arab"; private static String HEBR_SCRIPT_SUBTAG = "Hebr"; @@ -47,8 +38,8 @@ public class LocaleUtil { * * @param locale the Locale for which we want the layout direction. Can be null. * @return the layout direction. This may be one of: - * {@link #TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE} or - * {@link #TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE}. + * {@link View#LAYOUT_DIRECTION_LTR} or + * {@link View#LAYOUT_DIRECTION_RTL}. * * Be careful: this code will need to be changed when vertical scripts will be supported * @@ -61,11 +52,11 @@ public class LocaleUtil { if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) || scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) { - return TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE; + return View.LAYOUT_DIRECTION_RTL; } } - return TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE; + return View.LAYOUT_DIRECTION_LTR; } /** @@ -75,8 +66,8 @@ public class LocaleUtil { * * @param locale * @return the layout direction. This may be one of: - * {@link #TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE} or - * {@link #TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE}. + * {@link View#LAYOUT_DIRECTION_LTR} or + * {@link View#LAYOUT_DIRECTION_RTL}. * * Be careful: this code will need to be changed when vertical scripts will be supported * @@ -86,11 +77,11 @@ public class LocaleUtil { switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) { case Character.DIRECTIONALITY_RIGHT_TO_LEFT: case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: - return TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE; + return View.LAYOUT_DIRECTION_RTL; case Character.DIRECTIONALITY_LEFT_TO_RIGHT: default: - return TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE; + return View.LAYOUT_DIRECTION_LTR; } } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index e6b41da..5cf0459 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -40,7 +40,6 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.IBinder; -import android.os.Message; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; @@ -9711,8 +9710,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * @hide */ protected static boolean isLayoutDirectionRtl(Locale locale) { - return (LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE == - LocaleUtil.getLayoutDirectionFromLocale(locale)); + return (LAYOUT_DIRECTION_RTL == LocaleUtil.getLayoutDirectionFromLocale(locale)); } /** diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index e8c0239..dc8c71b 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -913,7 +913,7 @@ public abstract class Animation implements Cloneable { * * @param interpolatedTime The value of the normalized time (0.0 to 1.0) * after it has been run through the interpolation function. - * @param t The Transofrmation object to fill in with the current + * @param t The Transformation object to fill in with the current * transforms. */ protected void applyTransformation(float interpolatedTime, Transformation t) { diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 89aba3c..7ec5398 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -152,6 +152,15 @@ public class BaseInputConnection implements InputConnection { } /** + * Called when this InputConnection is no longer used by the InputMethodManager. + * + * @hide + */ + protected void reportFinish() { + // Intentionaly empty + } + + /** * Default implementation uses * {@link MetaKeyKeyListener#clearMetaKeyState(long, int) * MetaKeyKeyListener.clearMetaKeyState(long, int)} to clear the state. diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index b6b27c1..3b6ebbe 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -708,6 +708,10 @@ public final class InputMethodManager { public void reportFinishInputConnection(InputConnection ic) { if (mServedInputConnection != ic) { ic.finishComposingText(); + // To avoid modifying the public InputConnection interface + if (ic instanceof BaseInputConnection) { + ((BaseInputConnection) ic).reportFinish(); + } } } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 424dd6d..af3bb4d 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -378,6 +378,7 @@ public class WebView extends AbsoluteLayout private int mInputType; private int mImeOptions; private String mHint; + private int mMaxLength; public WebViewInputConnection() { super(WebView.this, true); @@ -412,13 +413,9 @@ public class WebView extends AbsoluteLayout Editable editable = getEditable(); int selectionStart = Selection.getSelectionStart(editable); int selectionEnd = Selection.getSelectionEnd(editable); + text = limitReplaceTextByMaxLength(text, editable.length()); editable.replace(0, editable.length(), text); - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - // Since the text has changed, do not allow the IME to replace the - // existing text as though it were a completion. - imm.restartInput(WebView.this); - } + restartInput(); // Keep the previous selection. selectionStart = Math.min(selectionStart, editable.length()); selectionEnd = Math.min(selectionEnd, editable.length()); @@ -429,14 +426,10 @@ public class WebView extends AbsoluteLayout Editable editable = getEditable(); int selectionStart = Selection.getSelectionStart(editable); int selectionEnd = Selection.getSelectionEnd(editable); + text = limitReplaceTextByMaxLength(text, selectionEnd - selectionStart); setNewText(selectionStart, selectionEnd, text); editable.replace(selectionStart, selectionEnd, text); - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - // Since the text has changed, do not allow the IME to replace the - // existing text as though it were a completion. - imm.restartInput(WebView.this); - } + restartInput(); // Move caret to the end of the new text int newCaret = selectionStart + text.length(); setSelection(newCaret, newCaret); @@ -456,8 +449,19 @@ public class WebView extends AbsoluteLayout end = start; start = temp; } - setNewText(start, end, text); - return super.setComposingText(text, newCursorPosition); + CharSequence limitedText = limitReplaceTextByMaxLength(text, end - start); + setNewText(start, end, limitedText); + if (limitedText != text) { + newCursorPosition -= text.length() - limitedText.length(); + } + super.setComposingText(limitedText, newCursorPosition); + if (limitedText != text) { + restartInput(); + int lastCaret = start + limitedText.length(); + finishComposingText(); + setSelection(lastCaret, lastCaret); + } + return true; } @Override @@ -573,6 +577,7 @@ public class WebView extends AbsoluteLayout mHint = initData.mLabel; mInputType = inputType; mImeOptions = imeOptions; + mMaxLength = initData.mMaxLength; } public void setupEditorInfo(EditorInfo outAttrs) { @@ -660,6 +665,29 @@ public class WebView extends AbsoluteLayout KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD)); } + + private CharSequence limitReplaceTextByMaxLength(CharSequence text, + int numReplaced) { + if (mMaxLength > 0) { + Editable editable = getEditable(); + int maxReplace = mMaxLength - editable.length() + numReplaced; + if (maxReplace < text.length()) { + maxReplace = Math.max(maxReplace, 0); + // New length is greater than the maximum. trim it down. + text = text.subSequence(0, maxReplace); + } + } + return text; + } + + private void restartInput() { + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + // Since the text has changed, do not allow the IME to replace the + // existing text as though it were a completion. + imm.restartInput(WebView.this); + } + } } private class PastePopupWindow extends PopupWindow implements OnClickListener { diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index e7da1a8..93fd92b 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -923,13 +923,14 @@ public final class WebViewCore { static class TextFieldInitData { public TextFieldInitData(int fieldPointer, String text, int type, boolean isSpellCheckEnabled, - boolean isTextFieldNext, String label) { + boolean isTextFieldNext, String label, int maxLength) { mFieldPointer = fieldPointer; mText = text; mType = type; mIsSpellCheckEnabled = isSpellCheckEnabled; mIsTextFieldNext = isTextFieldNext; mLabel = label; + mMaxLength = maxLength; } int mFieldPointer; String mText; @@ -937,6 +938,7 @@ public final class WebViewCore { boolean mIsSpellCheckEnabled; boolean mIsTextFieldNext; String mLabel; + int mMaxLength; } // mAction of TouchEventData can be MotionEvent.getAction() which uses the @@ -2826,12 +2828,13 @@ public final class WebViewCore { // called by JNI private void initEditField(int pointer, String text, int inputType, boolean isSpellCheckEnabled, boolean nextFieldIsText, - String label, int start, int end, int selectionPtr) { + String label, int start, int end, int selectionPtr, int maxLength) { if (mWebView == null) { return; } TextFieldInitData initData = new TextFieldInitData(pointer, - text, inputType, isSpellCheckEnabled, nextFieldIsText, label); + text, inputType, isSpellCheckEnabled, nextFieldIsText, label, + maxLength); Message.obtain(mWebView.mPrivateHandler, WebView.INIT_EDIT_FIELD, initData).sendToTarget(); Message.obtain(mWebView.mPrivateHandler, diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index f384dc1..a162eaa 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -243,10 +243,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private static final int SIGNED = 2; private static final int DECIMAL = 4; - private static enum TEXT_ALIGN { - INHERIT, GRAVITY, TEXT_START, TEXT_END, CENTER, VIEW_START, VIEW_END; - } - /** * Draw marquee text with fading edges as usual */ @@ -329,9 +325,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // The alignment to pass to Layout, or null if not resolved. private Layout.Alignment mLayoutAlignment; - // The default value for mTextAlign. - private TEXT_ALIGN mTextAlign = TEXT_ALIGN.INHERIT; - private boolean mResolvedDrawables; /** @@ -5667,69 +5660,28 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener physicalWidth, false); } - @Override - protected void resetResolvedLayoutDirection() { - super.resetResolvedLayoutDirection(); - - if (mLayoutAlignment != null && - (mTextAlign == TEXT_ALIGN.VIEW_START || - mTextAlign == TEXT_ALIGN.VIEW_END)) { - mLayoutAlignment = null; - } - } - private Layout.Alignment getLayoutAlignment() { if (mLayoutAlignment == null) { - Layout.Alignment alignment; - TEXT_ALIGN textAlign = mTextAlign; - switch (textAlign) { - case INHERIT: - // fall through to gravity temporarily - // intention is to inherit value through view hierarchy. - case GRAVITY: - switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) { - case Gravity.START: - alignment = Layout.Alignment.ALIGN_NORMAL; - break; - case Gravity.END: - alignment = Layout.Alignment.ALIGN_OPPOSITE; - break; - case Gravity.LEFT: - alignment = Layout.Alignment.ALIGN_LEFT; - break; - case Gravity.RIGHT: - alignment = Layout.Alignment.ALIGN_RIGHT; - break; - case Gravity.CENTER_HORIZONTAL: - alignment = Layout.Alignment.ALIGN_CENTER; - break; - default: - alignment = Layout.Alignment.ALIGN_NORMAL; - break; - } - break; - case TEXT_START: - alignment = Layout.Alignment.ALIGN_NORMAL; + switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) { + case Gravity.START: + mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL; break; - case TEXT_END: - alignment = Layout.Alignment.ALIGN_OPPOSITE; + case Gravity.END: + mLayoutAlignment = Layout.Alignment.ALIGN_OPPOSITE; break; - case CENTER: - alignment = Layout.Alignment.ALIGN_CENTER; + case Gravity.LEFT: + mLayoutAlignment = Layout.Alignment.ALIGN_LEFT; break; - case VIEW_START: - alignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ? - Layout.Alignment.ALIGN_RIGHT : Layout.Alignment.ALIGN_LEFT; + case Gravity.RIGHT: + mLayoutAlignment = Layout.Alignment.ALIGN_RIGHT; break; - case VIEW_END: - alignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ? - Layout.Alignment.ALIGN_LEFT : Layout.Alignment.ALIGN_RIGHT; + case Gravity.CENTER_HORIZONTAL: + mLayoutAlignment = Layout.Alignment.ALIGN_CENTER; break; default: - alignment = Layout.Alignment.ALIGN_NORMAL; + mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL; break; } - mLayoutAlignment = alignment; } return mLayoutAlignment; } @@ -6044,7 +5996,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (des < 0) { des = (int) FloatMath.ceil(Layout.getDesiredWidth(mTransformed, mTextPaint)); } - width = des; } else { width = boring.width; @@ -6065,7 +6016,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (hintDes < 0) { - hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mHintBoring); + hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir, mHintBoring); if (hintBoring != null) { mHintBoring = hintBoring; } @@ -6073,10 +6024,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (hintBoring == null || hintBoring == UNKNOWN_BORING) { if (hintDes < 0) { - hintDes = (int) FloatMath.ceil( - Layout.getDesiredWidth(mHint, mTextPaint)); + hintDes = (int) FloatMath.ceil(Layout.getDesiredWidth(mHint, mTextPaint)); } - hintWidth = hintDes; } else { hintWidth = hintBoring.width; @@ -6340,20 +6289,25 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (changed && mEditor != null) getEditor().mTextDisplayListIsValid = false; } + private boolean isShowingHint() { + return TextUtils.isEmpty(mText) && !TextUtils.isEmpty(mHint); + } + /** * Returns true if anything changed. */ private boolean bringTextIntoView() { + Layout layout = isShowingHint() ? mHintLayout : mLayout; int line = 0; if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) { - line = mLayout.getLineCount() - 1; + line = layout.getLineCount() - 1; } - Layout.Alignment a = mLayout.getParagraphAlignment(line); - int dir = mLayout.getParagraphDirection(line); + Layout.Alignment a = layout.getParagraphAlignment(line); + int dir = layout.getParagraphDirection(line); int hspace = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(); int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom(); - int ht = mLayout.getHeight(); + int ht = layout.getHeight(); int scrollx, scrolly; @@ -6372,8 +6326,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * keep leading edge in view. */ - int left = (int) FloatMath.floor(mLayout.getLineLeft(line)); - int right = (int) FloatMath.ceil(mLayout.getLineRight(line)); + int left = (int) FloatMath.floor(layout.getLineLeft(line)); + int right = (int) FloatMath.ceil(layout.getLineRight(line)); if (right - left < hspace) { scrollx = (right + left) / 2 - hspace / 2; @@ -6385,10 +6339,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } } else if (a == Layout.Alignment.ALIGN_RIGHT) { - int right = (int) FloatMath.ceil(mLayout.getLineRight(line)); + int right = (int) FloatMath.ceil(layout.getLineRight(line)); scrollx = right - hspace; } else { // a == Layout.Alignment.ALIGN_LEFT (will also be the default) - scrollx = (int) FloatMath.floor(mLayout.getLineLeft(line)); + scrollx = (int) FloatMath.floor(layout.getLineLeft(line)); } if (ht < vspace) { @@ -6416,22 +6370,24 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public boolean bringPointIntoView(int offset) { boolean changed = false; - if (mLayout == null) return changed; + Layout layout = isShowingHint() ? mHintLayout: mLayout; + + if (layout == null) return changed; - int line = mLayout.getLineForOffset(offset); + int line = layout.getLineForOffset(offset); // FIXME: Is it okay to truncate this, or should we round? - final int x = (int)mLayout.getPrimaryHorizontal(offset); - final int top = mLayout.getLineTop(line); - final int bottom = mLayout.getLineTop(line + 1); + final int x = (int)layout.getPrimaryHorizontal(offset); + final int top = layout.getLineTop(line); + final int bottom = layout.getLineTop(line + 1); - int left = (int) FloatMath.floor(mLayout.getLineLeft(line)); - int right = (int) FloatMath.ceil(mLayout.getLineRight(line)); - int ht = mLayout.getHeight(); + int left = (int) FloatMath.floor(layout.getLineLeft(line)); + int right = (int) FloatMath.ceil(layout.getLineRight(line)); + int ht = layout.getHeight(); int grav; - switch (mLayout.getParagraphAlignment(line)) { + switch (layout.getParagraphAlignment(line)) { case ALIGN_LEFT: grav = 1; break; @@ -6439,10 +6395,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener grav = -1; break; case ALIGN_NORMAL: - grav = mLayout.getParagraphDirection(line); + grav = layout.getParagraphDirection(line); break; case ALIGN_OPPOSITE: - grav = -mLayout.getParagraphDirection(line); + grav = -layout.getParagraphDirection(line); break; case ALIGN_CENTER: default: @@ -8189,24 +8145,26 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public boolean performLongClick() { boolean handled = false; - boolean vibrate = true; if (super.performLongClick()) { handled = true; } + if (mEditor == null) { + return handled; + } + // Long press in empty space moves cursor and shows the Paste affordance if available. - if (!handled && mEditor != null && !isPositionOnText(getEditor().mLastDownPositionX, getEditor().mLastDownPositionY) && + if (!handled && !isPositionOnText(getEditor().mLastDownPositionX, getEditor().mLastDownPositionY) && getEditor().mInsertionControllerEnabled) { final int offset = getOffsetForPosition(getEditor().mLastDownPositionX, getEditor().mLastDownPositionY); stopSelectionActionMode(); Selection.setSelection((Spannable) mText, offset); getInsertionController().showWithActionPopup(); handled = true; - vibrate = false; } - if (!handled && mEditor != null && getEditor().mSelectionActionMode != null) { + if (!handled && getEditor().mSelectionActionMode != null) { if (touchPositionIsInSelection()) { // Start a drag final int start = getSelectionStart(); @@ -8225,15 +8183,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } // Start a new selection - if (!handled && mEditor != null) { - vibrate = handled = startSelectionActionMode(); + if (!handled) { + handled = startSelectionActionMode(); } - if (vibrate) { + if (handled) { performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - } - - if (handled && mEditor != null) { getEditor().mDiscardNextActionUp = true; } @@ -8759,6 +8714,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public void onResolveTextDirection() { if (hasPasswordTransformationMethod()) { + // TODO: take care of the content direction to show the password text and dots justified + // to the left or to the right mTextDir = TextDirectionHeuristics.LOCALE; return; } diff --git a/core/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java index 8a8f315..6cfb97d 100644 --- a/core/java/com/android/internal/util/FileRotator.java +++ b/core/java/com/android/internal/util/FileRotator.java @@ -48,7 +48,7 @@ import libcore.io.IoUtils; */ public class FileRotator { private static final String TAG = "FileRotator"; - private static final boolean LOGD = true; + private static final boolean LOGD = false; private final File mBasePath; private final String mPrefix; diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index 9579bce..4cbdf78 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -70,7 +70,7 @@ public class EditableInputConnection extends BaseInputConnection { public boolean endBatchEdit() { synchronized(this) { if (mBatchEditNesting > 0) { - // When the connection is reset by the InputMethodManager and finishComposingText + // When the connection is reset by the InputMethodManager and reportFinish // is called, some endBatchEdit calls may still be asynchronously received from the // IME. Do not take these into account, thus ensuring that this IC's final // contribution to mTextView's nested batch edit count is zero. @@ -83,6 +83,19 @@ public class EditableInputConnection extends BaseInputConnection { } @Override + protected void reportFinish() { + super.reportFinish(); + + synchronized(this) { + while (mBatchEditNesting > 0) { + endBatchEdit(); + } + // Will prevent any further calls to begin or endBatchEdit + mBatchEditNesting = -1; + } + } + + @Override public boolean clearMetaKeyStates(int states) { final Editable content = getEditable(); if (content == null) return false; @@ -99,23 +112,6 @@ public class EditableInputConnection extends BaseInputConnection { } @Override - public boolean finishComposingText() { - final boolean superResult = super.finishComposingText(); - synchronized(this) { - if (mBatchEditNesting < 0) { - // The connection was already finished - return false; - } - while (mBatchEditNesting > 0) { - endBatchEdit(); - } - // Will prevent any further calls to begin or endBatchEdit - mBatchEditNesting = -1; - } - return superResult; - } - - @Override public boolean commitCompletion(CompletionInfo text) { if (DEBUG) Log.v(TAG, "commitCompletion " + text); mTextView.beginBatchEdit(); |
