diff options
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/hardware/Camera.java | 209 | ||||
| -rw-r--r-- | core/java/android/nfc/NfcAdapter.java | 55 | ||||
| -rw-r--r-- | core/java/android/provider/Settings.java | 10 | ||||
| -rwxr-xr-x | core/java/android/provider/Telephony.java | 10 | ||||
| -rw-r--r-- | core/java/android/view/View.java | 16 | ||||
| -rw-r--r-- | core/java/android/view/textservice/SpellCheckerSession.java | 28 | ||||
| -rw-r--r-- | core/java/android/webkit/BrowserFrame.java | 13 | ||||
| -rw-r--r-- | core/java/android/webkit/WebView.java | 67 | ||||
| -rw-r--r-- | core/java/android/webkit/WebViewCore.java | 7 | ||||
| -rw-r--r-- | core/java/android/widget/ImageView.java | 24 | ||||
| -rw-r--r-- | core/java/android/widget/SpellChecker.java | 4 | ||||
| -rw-r--r-- | core/java/android/widget/TextView.java | 61 |
12 files changed, 428 insertions, 76 deletions
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 48adfad..3becec0 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -22,9 +22,12 @@ import android.graphics.ImageFormat; import android.graphics.Point; import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.media.AudioManager; +import android.media.MediaPlayer; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.SystemProperties; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; @@ -154,6 +157,7 @@ public class Camera { private boolean mOneShot; private boolean mWithBuffer; private boolean mFaceDetectionRunning = false; + private boolean mReleased = false; /** * Broadcast Action: A new picture is taken by the camera, and the entry of @@ -303,7 +307,7 @@ public class Camera { } protected void finalize() { - native_release(); + release(); } private native final void native_setup(Object camera_this, int cameraId); @@ -318,6 +322,15 @@ public class Camera { public final void release() { native_release(); mFaceDetectionRunning = false; + if (mCameraSoundPlayers != null) { + for (CameraSoundPlayer csp: mCameraSoundPlayers) { + if (csp != null) { + csp.release(); + } + } + mCameraSoundPlayers = null; + } + mReleased = true; } /** @@ -2354,7 +2367,7 @@ public class Camera { * * <p>The reference code is as follows. * - * <pre> + * <pre> * public void onOrientationChanged(int orientation) { * if (orientation == ORIENTATION_UNKNOWN) return; * android.hardware.Camera.CameraInfo info = @@ -2369,7 +2382,7 @@ public class Camera { * } * mParameters.setRotation(rotation); * } - * </pre> + * </pre> * * @param rotation The rotation angle in degrees relative to the * orientation of the camera. Rotation can only be 0, @@ -3452,4 +3465,194 @@ public class Camera { return result; } }; + + /** + * <p>The set of default system sounds for camera actions. Use this with + * {@link #playSound} to play an appropriate sound when implementing a + * custom still or video recording mechanism through the preview + * callbacks.</p> + * + * <p>There is no need to play sounds when using {@link #takePicture} or + * {@link android.media.MediaRecorder} for still images or video, + * respectively, as these play their own sounds when needed.</p> + * + * @see #playSound + * @hide + */ + public static class Sound { + /** + * The sound used by {@link android.hardware.Camera#takePicture} to + * indicate still image capture. + */ + public static final int SHUTTER_CLICK = 0; + + /** + * A sound to indicate that focusing has completed. Because deciding + * when this occurs is application-dependent, this sound is not used by + * any methods in the Camera class. + */ + public static final int FOCUS_COMPLETE = 1; + + /** + * The sound used by {@link android.media.MediaRecorder#start} to + * indicate the start of video recording. + */ + public static final int START_VIDEO_RECORDING = 2; + + /** + * The sound used by {@link android.media.MediaRecorder#stop} to + * indicate the end of video recording. + */ + public static final int STOP_VIDEO_RECORDING = 3; + + private static final int NUM_SOUNDS = 4; + }; + + /** + * <p>Play one of the predefined platform sounds for camera actions.</p> + * + * <p>Use this method to play a platform-specific sound for various camera + * actions. The sound playing is done asynchronously, with the same behavior + * and content as the sounds played by {@link #takePicture takePicture}, + * {@link android.media.MediaRecorder#start MediaRecorder.start}, and + * {@link android.media.MediaRecorder#stop MediaRecorder.stop}.</p> + * + * <p>Using this method makes it easy to match the default device sounds + * when recording or capturing data through the preview callbacks + * ({@link #setPreviewCallback setPreviewCallback}, + * {@link #setPreviewTexture setPreviewTexture}).</p> + * + * @param soundId The type of sound to play, selected from the options in + * {@link android.hardware.Camera.Sound} + * @see android.hardware.Camera.Sound + * @see #takePicture + * @see android.media.MediaRecorder + * @hide + */ + public void playSound(int soundId) { + if (mReleased) return; + if (mCameraSoundPlayers == null) { + mCameraSoundPlayers = new CameraSoundPlayer[Sound.NUM_SOUNDS]; + } + if (mCameraSoundPlayers[soundId] == null) { + mCameraSoundPlayers[soundId] = new CameraSoundPlayer(soundId); + } + mCameraSoundPlayers[soundId].play(); + } + + private CameraSoundPlayer[] mCameraSoundPlayers; + + private static class CameraSoundPlayer implements Runnable { + private int mSoundId; + private int mAudioStreamType; + private MediaPlayer mPlayer; + private Thread mThread; + private boolean mExit; + private int mPlayCount; + + private static final String mShutterSound = + "/system/media/audio/ui/camera_click.ogg"; + private static final String mFocusSound = + "/system/media/audio/ui/camera_focus.ogg"; + private static final String mVideoStartSound = + "/system/media/audio/ui/VideoRecord.ogg"; + private static final String mVideoStopSound = + "/system/media/audio/ui/VideoRecord.ogg"; + + @Override + public void run() { + String soundFilePath; + switch (mSoundId) { + case Sound.SHUTTER_CLICK: + soundFilePath = mShutterSound; + break; + case Sound.FOCUS_COMPLETE: + soundFilePath = mFocusSound; + break; + case Sound.START_VIDEO_RECORDING: + soundFilePath = mVideoStartSound; + break; + case Sound.STOP_VIDEO_RECORDING: + soundFilePath = mVideoStopSound; + break; + default: + Log.e(TAG, "Unknown sound " + mSoundId + " requested."); + return; + } + mPlayer = new MediaPlayer(); + try { + mPlayer.setAudioStreamType(mAudioStreamType); + mPlayer.setDataSource(soundFilePath); + mPlayer.setLooping(false); + mPlayer.prepare(); + } catch(IOException e) { + Log.e(TAG, "Error setting up sound " + mSoundId, e); + return; + } + + while(true) { + try { + synchronized (this) { + while(true) { + if (mExit) { + return; + } else if (mPlayCount <= 0) { + wait(); + } else { + mPlayCount--; + break; + } + } + } + mPlayer.start(); + } catch (Exception e) { + Log.e(TAG, "Error playing sound " + mSoundId, e); + } + } + } + + public CameraSoundPlayer(int soundId) { + mSoundId = soundId; + if (SystemProperties.get("ro.camera.sound.forced", "0").equals("0")) { + mAudioStreamType = AudioManager.STREAM_MUSIC; + } else { + mAudioStreamType = AudioManager.STREAM_SYSTEM_ENFORCED; + } + } + + public void play() { + if (mThread == null) { + mThread = new Thread(this); + mThread.start(); + } + synchronized (this) { + mPlayCount++; + notifyAll(); + } + } + + public void release() { + if (mThread != null) { + synchronized (this) { + mExit = true; + notifyAll(); + } + try { + mThread.join(); + } catch (InterruptedException e) { + } + mThread = null; + } + if (mPlayer != null) { + mPlayer.release(); + mPlayer = null; + } + } + + @Override + protected void finalize() { + release(); + } + } + } diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 33310df..fe0106d 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -768,6 +768,61 @@ public final class NfcAdapter { } /** + * TODO: Remove this once pre-built apk's (Maps, Youtube etc) are updated + * @deprecated use {@link CreateNdefMessageCallback} or {@link OnNdefPushCompleteCallback} + * @hide + */ + @Deprecated + public interface NdefPushCallback { + /** + * @deprecated use {@link CreateNdefMessageCallback} instead + */ + @Deprecated + NdefMessage createMessage(); + /** + * @deprecated use{@link OnNdefPushCompleteCallback} instead + */ + @Deprecated + void onMessagePushed(); + } + + /** + * TODO: Remove this + * Converts new callbacks to old callbacks. + */ + static final class LegacyCallbackWrapper implements CreateNdefMessageCallback, + OnNdefPushCompleteCallback { + final NdefPushCallback mLegacyCallback; + LegacyCallbackWrapper(NdefPushCallback legacyCallback) { + mLegacyCallback = legacyCallback; + } + @Override + public void onNdefPushComplete(NfcEvent event) { + mLegacyCallback.onMessagePushed(); + } + @Override + public NdefMessage createNdefMessage(NfcEvent event) { + return mLegacyCallback.createMessage(); + } + } + + /** + * TODO: Remove this once pre-built apk's (Maps, Youtube etc) are updated + * @deprecated use {@link #setNdefPushMessageCallback} instead + * @hide + */ + @Deprecated + public void enableForegroundNdefPush(Activity activity, final NdefPushCallback callback) { + if (activity == null || callback == null) { + throw new NullPointerException(); + } + enforceResumed(activity); + LegacyCallbackWrapper callbackWrapper = new LegacyCallbackWrapper(callback); + mNfcActivityManager.setNdefPushMessageCallback(activity, callbackWrapper); + mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callbackWrapper); + } + + /** * Enable NDEF Push feature. * <p>This API is for the Settings application. * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 769776e..b032169 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4077,13 +4077,6 @@ public final class Settings { "contacts_preauth_uri_expiration"; /** - * Whether the Messaging app posts notifications. - * 0=disabled. 1=enabled. - */ - public static final String MESSAGING_APP_NOTIFICATIONS = "messaging_app_notifications"; - - - /** * This are the settings to be backed up. * * NOTE: Settings are backed up and restored in the order they appear @@ -4120,8 +4113,7 @@ public final class Settings { MOUNT_UMS_NOTIFY_ENABLED, UI_NIGHT_MODE, LOCK_SCREEN_OWNER_INFO, - LOCK_SCREEN_OWNER_INFO_ENABLED, - MESSAGING_APP_NOTIFICATIONS + LOCK_SCREEN_OWNER_INFO_ENABLED }; /** diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 8eb9da1..0e6d07d 100755 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -1838,15 +1838,5 @@ public final class Telephony { public static final String EXTRA_PLMN = "plmn"; public static final String EXTRA_SHOW_SPN = "showSpn"; public static final String EXTRA_SPN = "spn"; - - /** - * Activity Action: Shows a dialog to turn off Messaging app notification. - * <p>Input: Nothing. - * <p>Output: Nothing. - */ - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_MESSAGING_APP_NOTIFICATIONS = - "android.provider.Telephony.MESSAGING_APP_NOTIFICATIONS"; - } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 62e6ebd..dc46d42 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -11402,8 +11402,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link SystemClock#uptimeMillis} timebase. */ public void scheduleDrawable(Drawable who, Runnable what, long when) { - if (verifyDrawable(who) && what != null && mAttachInfo != null) { - mAttachInfo.mHandler.postAtTime(what, who, when); + if (verifyDrawable(who) && what != null) { + if (mAttachInfo != null) { + mAttachInfo.mHandler.postAtTime(what, who, when); + } else { + ViewRootImpl.getRunQueue().postDelayed(what, when - SystemClock.uptimeMillis()); + } } } @@ -11414,8 +11418,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * @param what the action to cancel */ public void unscheduleDrawable(Drawable who, Runnable what) { - if (verifyDrawable(who) && what != null && mAttachInfo != null) { - mAttachInfo.mHandler.removeCallbacks(what, who); + if (verifyDrawable(who) && what != null) { + if (mAttachInfo != null) { + mAttachInfo.mHandler.removeCallbacks(what, who); + } else { + ViewRootImpl.getRunQueue().removeCallbacks(what); + } } } diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java index 01b114c..0eb6e27 100644 --- a/core/java/android/view/textservice/SpellCheckerSession.java +++ b/core/java/android/view/textservice/SpellCheckerSession.java @@ -146,6 +146,13 @@ public class SpellCheckerSession { } /** + * Cancel pending and running spell check tasks + */ + public void cancel() { + mSpellCheckerSessionListenerImpl.cancel(); + } + + /** * Finish this session and allow TextServicesManagerService to disconnect the bound spell * checker. */ @@ -242,6 +249,13 @@ public class SpellCheckerSession { } } + public void cancel() { + if (DBG) { + Log.w(TAG, "cancel"); + } + processOrEnqueueTask(new SpellCheckerParams(TASK_CANCEL, null, 0, false)); + } + public void getSuggestionsMultiple( TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { if (DBG) { @@ -275,8 +289,22 @@ public class SpellCheckerSession { if (DBG) { Log.d(TAG, "process or enqueue task: " + mISpellCheckerSession); } + SpellCheckerParams closeTask = null; if (mISpellCheckerSession == null) { + if (scp.mWhat == TASK_CANCEL) { + while (!mPendingTasks.isEmpty()) { + final SpellCheckerParams tmp = mPendingTasks.poll(); + if (tmp.mWhat == TASK_CLOSE) { + // Only one close task should be processed, while we need to remove all + // close tasks from the queue + closeTask = tmp; + } + } + } mPendingTasks.offer(scp); + if (closeTask != null) { + mPendingTasks.offer(closeTask); + } } else { processTask(scp); } diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index 388920c..c194559 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -1404,4 +1404,17 @@ class BrowserFrame extends Handler { native void nativeSslClientCert(int handle, byte[] pkcs8EncodedPrivateKey, byte[][] asn1DerEncodedCertificateChain); + + /** + * Returns true when the contents of the frame is an RTL or vertical-rl + * page. This is used for determining whether a frame should be initially + * scrolled right-most as opposed to left-most. + * @return true when the frame should be initially scrolled right-most + * based on the text direction and writing mode. + */ + /* package */ boolean getShouldStartScrolledRight() { + return nativeGetShouldStartScrolledRight(mNativeFrame); + } + + private native boolean nativeGetShouldStartScrolledRight(int nativeBrowserFrame); } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 35efabc..03d6511 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -3254,6 +3254,26 @@ public class WebView extends AbsoluteLayout if (mHTML5VideoViewProxy != null) { mHTML5VideoViewProxy.pauseAndDispatch(); } + if (mNativeClass != 0) { + nativeSetPauseDrawing(mNativeClass, true); + } + } + } + + @Override + protected void onWindowVisibilityChanged(int visibility) { + super.onWindowVisibilityChanged(visibility); + updateDrawingState(); + } + + void updateDrawingState() { + if (mNativeClass == 0 || mIsPaused) return; + if (getWindowVisibility() != VISIBLE) { + nativeSetPauseDrawing(mNativeClass, true); + } else if (getVisibility() != VISIBLE) { + nativeSetPauseDrawing(mNativeClass, true); + } else { + nativeSetPauseDrawing(mNativeClass, false); } } @@ -3265,6 +3285,9 @@ public class WebView extends AbsoluteLayout if (mIsPaused) { mIsPaused = false; mWebViewCore.sendMessage(EventHub.ON_RESUME); + if (mNativeClass != 0) { + nativeSetPauseDrawing(mNativeClass, false); + } } } @@ -5599,6 +5622,7 @@ public class WebView extends AbsoluteLayout if (visibility != View.VISIBLE && mZoomManager != null) { mZoomManager.dismissZoomPicker(); } + updateDrawingState(); } /** @@ -8403,6 +8427,9 @@ public class WebView extends AbsoluteLayout setNewPicture(mDelaySetPicture, true); mDelaySetPicture = null; } + if (mIsPaused) { + nativeSetPauseDrawing(mNativeClass, true); + } break; case UPDATE_TEXTFIELD_TEXT_MSG_ID: // Make sure that the textfield is currently focused @@ -8733,27 +8760,6 @@ public class WebView extends AbsoluteLayout isPictureAfterFirstLayout, registerPageSwapCallback); } final Point viewSize = draw.mViewSize; - if (isPictureAfterFirstLayout) { - // Reset the last sent data here since dealing with new page. - mLastWidthSent = 0; - mZoomManager.onFirstLayout(draw); - if (!mDrawHistory) { - // Do not send the scroll event for this particular - // scroll message. Note that a scroll event may - // still be fired if the user scrolls before the - // message can be handled. - mSendScrollEvent = false; - setContentScrollTo(viewState.mScrollX, viewState.mScrollY); - mSendScrollEvent = true; - - // As we are on a new page, remove the WebTextView. This - // is necessary for page loads driven by webkit, and in - // particular when the user was on a password field, so - // the WebTextView was visible. - clearTextEntry(); - } - } - // We update the layout (i.e. request a layout from the // view system) if the last view size that we sent to // WebCore matches the view size of the picture we just @@ -8766,7 +8772,25 @@ public class WebView extends AbsoluteLayout mSendScrollEvent = false; recordNewContentSize(draw.mContentSize.x, draw.mContentSize.y, updateLayout); + + if (isPictureAfterFirstLayout) { + // Reset the last sent data here since dealing with new page. + mLastWidthSent = 0; + mZoomManager.onFirstLayout(draw); + int scrollX = viewState.mShouldStartScrolledRight + ? getContentWidth() : viewState.mScrollX; + int scrollY = viewState.mScrollY; + setContentScrollTo(scrollX, scrollY); + if (!mDrawHistory) { + // As we are on a new page, remove the WebTextView. This + // is necessary for page loads driven by webkit, and in + // particular when the user was on a password field, so + // the WebTextView was visible. + clearTextEntry(); + } + } mSendScrollEvent = true; + if (DebugFlags.WEB_VIEW) { Rect b = draw.mInvalRegion.getBounds(); Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" + @@ -9584,4 +9608,5 @@ public class WebView extends AbsoluteLayout * See {@link ComponentCallbacks2} for the trim levels and descriptions */ private static native void nativeOnTrimMemory(int level); + private static native void nativeSetPauseDrawing(int instance, boolean pause); } diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 754d6e9..cd61481 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -1982,6 +1982,7 @@ public final class WebViewCore { int mScrollY; boolean mMobileSite; boolean mIsRestored; + boolean mShouldStartScrolledRight; } static class DrawData { @@ -2382,6 +2383,7 @@ public final class WebViewCore { viewState.mMobileSite = false; // for non-mobile site, we don't need minPrefWidth, set it as 0 viewState.mScrollX = 0; + viewState.mShouldStartScrolledRight = false; Message.obtain(mWebView.mPrivateHandler, WebView.UPDATE_ZOOM_RANGE, viewState).sendToTarget(); return; @@ -2412,6 +2414,11 @@ public final class WebViewCore { mInitialViewState.mDefaultScale = adjust; mInitialViewState.mScrollX = mRestoredX; mInitialViewState.mScrollY = mRestoredY; + mInitialViewState.mShouldStartScrolledRight = (mRestoredX == 0) + && (mRestoredY == 0) + && (mBrowserFrame != null) + && mBrowserFrame.getShouldStartScrolledRight(); + mInitialViewState.mMobileSite = (0 == mViewportWidth); if (mIsRestored) { mInitialViewState.mIsRestored = true; diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index b24dd69..c1e36ed 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -1035,4 +1035,28 @@ public class ImageView extends View { mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8); } } + + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + if (mDrawable != null) { + mDrawable.setVisible(visibility == VISIBLE, false); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (mDrawable != null) { + mDrawable.setVisible(getVisibility() == VISIBLE, false); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mDrawable != null) { + mDrawable.setVisible(false, false); + } + } } diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index 87c3e9b..ebb2604 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -76,7 +76,7 @@ public class SpellChecker implements SpellCheckerSessionListener { mIds = new int[size]; mSpellCheckSpans = new SpellCheckSpan[size]; - setLocale(mTextView.getLocale()); + setLocale(mTextView.getTextServicesLocale()); mCookie = hashCode(); } @@ -173,7 +173,7 @@ public class SpellChecker implements SpellCheckerSessionListener { } public void spellCheck(int start, int end) { - final Locale locale = mTextView.getLocale(); + final Locale locale = mTextView.getTextServicesLocale(); if (mCurrentLocale == null || (!(mCurrentLocale.equals(locale)))) { setLocale(locale); // Re-check the entire text diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 5833afd..bc8721a 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -36,7 +36,6 @@ import android.graphics.RectF; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.inputmethodservice.ExtractEditText; -import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -101,7 +100,6 @@ import android.util.Log; import android.util.TypedValue; import android.view.ActionMode; import android.view.ActionMode.Callback; -import android.view.ContextMenu; import android.view.DragEvent; import android.view.Gravity; import android.view.HapticFeedbackConstants; @@ -133,6 +131,8 @@ import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; +import android.view.textservice.SpellCheckerSubtype; +import android.view.textservice.TextServicesManager; import android.widget.AdapterView.OnItemClickListener; import android.widget.RemoteViews.RemoteView; @@ -8355,10 +8355,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // When the cursor moves, the word that was typed may need spell check mSpellChecker.onSelectionChanged(); } - if (isCursorInsideEasyCorrectionSpan()) { - showSuggestions(); - } else if (hasInsertionController()) { - getInsertionController().show(); + if (!extractedTextModeWillBeStarted()) { + if (isCursorInsideEasyCorrectionSpan()) { + showSuggestions(); + } else if (hasInsertionController()) { + getInsertionController().show(); + } } } @@ -8904,21 +8906,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * This is a temporary method. Future versions may support multi-locale text. * - * @return The current locale used in this TextView, based on the current IME's locale, - * or the system default locale if this is not defined. + * @return The locale that should be used for a word iterator and a spell checker + * in this TextView, based on the current spell checker settings, + * the current IME's locale, or the system default locale. * @hide */ - public Locale getLocale() { + public Locale getTextServicesLocale() { Locale locale = Locale.getDefault(); - final InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - final InputMethodSubtype currentInputMethodSubtype = imm.getCurrentInputMethodSubtype(); - if (currentInputMethodSubtype != null) { - String localeString = currentInputMethodSubtype.getLocale(); - if (!TextUtils.isEmpty(localeString)) { - locale = new Locale(localeString); - } - } + final TextServicesManager textServicesManager = (TextServicesManager) + mContext.getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE); + final SpellCheckerSubtype subtype = textServicesManager.getCurrentSpellCheckerSubtype(true); + if (subtype != null) { + locale = new Locale(subtype.getLocale()); } return locale; } @@ -8933,7 +8932,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ public WordIterator getWordIterator() { if (mWordIterator == null) { - mWordIterator = new WordIterator(getLocale()); + mWordIterator = new WordIterator(getTextServicesLocale()); } return mWordIterator; } @@ -10113,27 +10112,35 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - final InputMethodManager imm = InputMethodManager.peekInstance(); - boolean extractedTextModeWillBeStartedFullScreen = !(this instanceof ExtractEditText) && - imm != null && imm.isFullscreenMode(); + boolean willExtract = extractedTextModeWillBeStarted(); // Do not start the action mode when extracted text will show up full screen, thus // immediately hiding the newly created action bar, which would be visually distracting. - if (!extractedTextModeWillBeStartedFullScreen) { + if (!willExtract) { ActionMode.Callback actionModeCallback = new SelectionActionModeCallback(); mSelectionActionMode = startActionMode(actionModeCallback); } - final boolean selectionStarted = mSelectionActionMode != null || - extractedTextModeWillBeStartedFullScreen; - if (selectionStarted && !mTextIsSelectable && imm != null && mSoftInputShownOnFocus) { + final boolean selectionStarted = mSelectionActionMode != null || willExtract; + if (selectionStarted && !mTextIsSelectable && mSoftInputShownOnFocus) { // Show the IME to be able to replace text, except when selecting non editable text. - imm.showSoftInput(this, 0, null); + final InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + imm.showSoftInput(this, 0, null); + } } return selectionStarted; } + private boolean extractedTextModeWillBeStarted() { + if (!(this instanceof ExtractEditText)) { + final InputMethodManager imm = InputMethodManager.peekInstance(); + return imm != null && imm.isFullscreenMode(); + } + return false; + } + private void stopSelectionActionMode() { if (mSelectionActionMode != null) { // This will hide the mSelectionModifierCursorController |
