Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.content.browser.input; | 5 package org.chromium.content.browser.input; |
| 6 | 6 |
| 7 import android.annotation.TargetApi; | |
| 8 import android.content.Context; | |
| 7 import android.content.res.Configuration; | 9 import android.content.res.Configuration; |
| 10 import android.graphics.Color; | |
| 11 import android.graphics.Rect; | |
| 12 import android.graphics.drawable.ColorDrawable; | |
| 13 import android.graphics.drawable.Drawable; | |
| 8 import android.os.Build; | 14 import android.os.Build; |
| 9 import android.os.ResultReceiver; | 15 import android.os.ResultReceiver; |
| 10 import android.os.SystemClock; | 16 import android.os.SystemClock; |
| 17 import android.text.Spannable; | |
| 18 import android.text.Spanned; | |
| 11 import android.text.SpannableString; | 19 import android.text.SpannableString; |
| 12 import android.text.TextUtils; | 20 import android.text.TextUtils; |
| 13 import android.text.style.BackgroundColorSpan; | 21 import android.text.style.BackgroundColorSpan; |
| 14 import android.text.style.CharacterStyle; | 22 import android.text.style.CharacterStyle; |
| 23 import android.text.style.SuggestionSpan; | |
| 24 import android.text.style.TextAppearanceSpan; | |
| 15 import android.text.style.UnderlineSpan; | 25 import android.text.style.UnderlineSpan; |
| 26 import android.util.DisplayMetrics; | |
| 27 import android.view.Gravity; | |
| 16 import android.view.KeyCharacterMap; | 28 import android.view.KeyCharacterMap; |
| 17 import android.view.KeyEvent; | 29 import android.view.KeyEvent; |
| 30 import android.view.LayoutInflater; | |
| 18 import android.view.View; | 31 import android.view.View; |
| 32 import android.view.ViewGroup; | |
| 33 import android.view.ViewGroup.LayoutParams; | |
| 19 import android.view.inputmethod.BaseInputConnection; | 34 import android.view.inputmethod.BaseInputConnection; |
| 35 import android.view.inputmethod.CursorAnchorInfo; | |
| 20 import android.view.inputmethod.EditorInfo; | 36 import android.view.inputmethod.EditorInfo; |
| 21 import android.view.inputmethod.InputConnection; | 37 import android.view.inputmethod.InputConnection; |
| 38 import android.view.WindowManager; | |
| 39 import android.widget.AdapterView; | |
| 40 import android.widget.AdapterView.OnItemClickListener; | |
| 41 import android.widget.BaseAdapter; | |
| 42 import android.widget.LinearLayout; | |
| 43 import android.widget.ListView; | |
| 44 import android.widget.PopupWindow; | |
| 45 import android.widget.PopupWindow.OnDismissListener; | |
| 46 import android.widget.TextView; | |
| 47 | |
| 48 import java.lang.reflect.InvocationTargetException; | |
| 49 import java.lang.reflect.Method; | |
| 22 | 50 |
| 23 import org.chromium.base.Log; | 51 import org.chromium.base.Log; |
| 24 import org.chromium.base.VisibleForTesting; | 52 import org.chromium.base.VisibleForTesting; |
| 25 import org.chromium.base.annotations.CalledByNative; | 53 import org.chromium.base.annotations.CalledByNative; |
| 26 import org.chromium.base.annotations.JNINamespace; | 54 import org.chromium.base.annotations.JNINamespace; |
| 27 import org.chromium.blink_public.web.WebInputEventModifier; | 55 import org.chromium.blink_public.web.WebInputEventModifier; |
| 28 import org.chromium.blink_public.web.WebInputEventType; | 56 import org.chromium.blink_public.web.WebInputEventType; |
| 29 import org.chromium.blink_public.web.WebTextInputMode; | 57 import org.chromium.blink_public.web.WebTextInputMode; |
| 58 import org.chromium.content.R; | |
| 30 import org.chromium.content.browser.RenderCoordinates; | 59 import org.chromium.content.browser.RenderCoordinates; |
| 31 import org.chromium.content.browser.picker.InputDialogContainer; | 60 import org.chromium.content.browser.picker.InputDialogContainer; |
| 32 import org.chromium.ui.base.ime.TextInputType; | 61 import org.chromium.ui.base.ime.TextInputType; |
| 33 | 62 |
| 34 /** | 63 /** |
| 35 * Adapts and plumbs android IME service onto the chrome text input API. | 64 * Adapts and plumbs android IME service onto the chrome text input API. |
| 36 * ImeAdapter provides an interface in both ways native <-> java: | 65 * ImeAdapter provides an interface in both ways native <-> java: |
| 37 * 1. InputConnectionAdapter notifies native code of text composition state and | 66 * 1. InputConnectionAdapter notifies native code of text composition state and |
| 38 * dispatch key events from java -> WebKit. | 67 * dispatch key events from java -> WebKit. |
| 39 * 2. Native ImeAdapter notifies java side to clear composition text. | 68 * 2. Native ImeAdapter notifies java side to clear composition text. |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 97 private ChromiumBaseInputConnection mInputConnection; | 126 private ChromiumBaseInputConnection mInputConnection; |
| 98 private ChromiumBaseInputConnection.Factory mInputConnectionFactory; | 127 private ChromiumBaseInputConnection.Factory mInputConnectionFactory; |
| 99 | 128 |
| 100 private final ImeAdapterDelegate mViewEmbedder; | 129 private final ImeAdapterDelegate mViewEmbedder; |
| 101 // This holds the information necessary for constructing CursorAnchorInfo, a nd notifies to | 130 // This holds the information necessary for constructing CursorAnchorInfo, a nd notifies to |
| 102 // InputMethodManager on appropriate timing, depending on how IME requested the information | 131 // InputMethodManager on appropriate timing, depending on how IME requested the information |
| 103 // via InputConnection. The update request is per InputConnection, hence for each time it is | 132 // via InputConnection. The update request is per InputConnection, hence for each time it is |
| 104 // re-created, the monitoring status will be reset. | 133 // re-created, the monitoring status will be reset. |
| 105 private final CursorAnchorInfoController mCursorAnchorInfoController; | 134 private final CursorAnchorInfoController mCursorAnchorInfoController; |
| 106 | 135 |
| 136 SuggestionsPopupWindow mSuggestionsPopupWindow; | |
| 137 private SuggestionInfo[] mSuggestionInfos = new SuggestionInfo[0]; | |
| 138 | |
| 107 private int mTextInputType = TextInputType.NONE; | 139 private int mTextInputType = TextInputType.NONE; |
| 108 private int mTextInputFlags; | 140 private int mTextInputFlags; |
| 109 private int mTextInputMode = WebTextInputMode.kDefault; | 141 private int mTextInputMode = WebTextInputMode.kDefault; |
| 110 | 142 |
| 111 // Keep the current configuration to detect the change when onConfigurationC hanged() is called. | 143 // Keep the current configuration to detect the change when onConfigurationC hanged() is called. |
| 112 private Configuration mCurrentConfig; | 144 private Configuration mCurrentConfig; |
| 113 | 145 |
| 114 private int mLastSelectionStart; | 146 private int mLastSelectionStart; |
| 115 private int mLastSelectionEnd; | 147 private int mLastSelectionEnd; |
| 116 private String mLastText; | 148 private String mLastText; |
| 117 private int mLastCompositionStart; | 149 private int mLastCompositionStart; |
| 118 private int mLastCompositionEnd; | 150 private int mLastCompositionEnd; |
| 119 | 151 |
| 152 private CursorAnchorInfo mLastCursorAnchorInfo; | |
| 153 | |
| 120 /** | 154 /** |
| 121 * @param wrapper InputMethodManagerWrapper that should receive all the call directed to | 155 * @param wrapper InputMethodManagerWrapper that should receive all the call directed to |
| 122 * InputMethodManager. | 156 * InputMethodManager. |
| 123 * @param embedder The view that is used for callbacks from ImeAdapter. | 157 * @param embedder The view that is used for callbacks from ImeAdapter. |
| 124 */ | 158 */ |
| 125 public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embe dder) { | 159 public ImeAdapter(InputMethodManagerWrapper wrapper, ImeAdapterDelegate embe dder) { |
| 126 mInputMethodManagerWrapper = wrapper; | 160 mInputMethodManagerWrapper = wrapper; |
| 127 mViewEmbedder = embedder; | 161 mViewEmbedder = embedder; |
| 128 // Deep copy newConfig so that we can notice the difference. | 162 // Deep copy newConfig so that we can notice the difference. |
| 129 mCurrentConfig = new Configuration( | 163 mCurrentConfig = new Configuration( |
| 130 mViewEmbedder.getAttachedView().getResources().getConfiguration( )); | 164 mViewEmbedder.getAttachedView().getResources().getConfiguration( )); |
| 131 // CursorAnchroInfo is supported only after L. | 165 // CursorAnchorInfo is supported only after L. |
| 132 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | 166 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { |
| 133 mCursorAnchorInfoController = CursorAnchorInfoController.create(wrap per, | 167 mCursorAnchorInfoController = CursorAnchorInfoController.create( |
| 134 new CursorAnchorInfoController.ComposingTextDelegate() { | 168 wrapper, new CursorAnchorInfoController.ComposingTextDelegat e() { |
| 135 @Override | 169 @Override |
| 136 public CharSequence getText() { | 170 public CharSequence getText() { |
| 137 return mLastText; | 171 return mLastText; |
| 138 } | 172 } |
| 139 @Override | 173 @Override |
| 140 public int getSelectionStart() { | 174 public int getSelectionStart() { |
| 141 return mLastSelectionStart; | 175 return mLastSelectionStart; |
| 142 } | 176 } |
| 143 @Override | 177 @Override |
| 144 public int getSelectionEnd() { | 178 public int getSelectionEnd() { |
| 145 return mLastSelectionEnd; | 179 return mLastSelectionEnd; |
| 146 } | 180 } |
| 147 @Override | 181 @Override |
| 148 public int getComposingTextStart() { | 182 public int getComposingTextStart() { |
| 149 return mLastCompositionStart; | 183 return mLastCompositionStart; |
| 150 } | 184 } |
| 151 @Override | 185 @Override |
| 152 public int getComposingTextEnd() { | 186 public int getComposingTextEnd() { |
| 153 return mLastCompositionEnd; | 187 return mLastCompositionEnd; |
| 154 } | 188 } |
| 155 }); | 189 }, this); |
| 156 } else { | 190 } else { |
| 157 mCursorAnchorInfoController = null; | 191 mCursorAnchorInfoController = null; |
| 158 } | 192 } |
| 159 } | 193 } |
| 160 | 194 |
| 161 private void createInputConnectionFactory() { | 195 private void createInputConnectionFactory() { |
| 162 if (mInputConnectionFactory != null) return; | 196 if (mInputConnectionFactory != null) return; |
| 163 mInputConnectionFactory = new ThreadedInputConnectionFactory(mInputMetho dManagerWrapper); | 197 mInputConnectionFactory = new ThreadedInputConnectionFactory(mInputMetho dManagerWrapper); |
| 164 } | 198 } |
| 165 | 199 |
| (...skipping 551 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 717 */ | 751 */ |
| 718 public void onUpdateFrameInfo(RenderCoordinates renderCoordinates, boolean h asInsertionMarker, | 752 public void onUpdateFrameInfo(RenderCoordinates renderCoordinates, boolean h asInsertionMarker, |
| 719 boolean isInsertionMarkerVisible, float insertionMarkerHorizontal, | 753 boolean isInsertionMarkerVisible, float insertionMarkerHorizontal, |
| 720 float insertionMarkerTop, float insertionMarkerBottom) { | 754 float insertionMarkerTop, float insertionMarkerBottom) { |
| 721 if (mCursorAnchorInfoController == null) return; | 755 if (mCursorAnchorInfoController == null) return; |
| 722 mCursorAnchorInfoController.onUpdateFrameInfo(renderCoordinates, hasInse rtionMarker, | 756 mCursorAnchorInfoController.onUpdateFrameInfo(renderCoordinates, hasInse rtionMarker, |
| 723 isInsertionMarkerVisible, insertionMarkerHorizontal, insertionMa rkerTop, | 757 isInsertionMarkerVisible, insertionMarkerHorizontal, insertionMa rkerTop, |
| 724 insertionMarkerBottom, mViewEmbedder.getAttachedView()); | 758 insertionMarkerBottom, mViewEmbedder.getAttachedView()); |
| 725 } | 759 } |
| 726 | 760 |
| 761 /** | |
| 762 * Called by CursorAnchorInfoController to notify ImeAdapter when there's | |
| 763 * updated CursorAnchorInfo | |
| 764 */ | |
| 765 public void updateCursorAnchorInfo(View view, CursorAnchorInfo cursorAnchorI nfo) { | |
| 766 mLastCursorAnchorInfo = cursorAnchorInfo; | |
| 767 if (mSuggestionsPopupWindow != null) { | |
| 768 mSuggestionsPopupWindow.updatePosition(); | |
| 769 } | |
| 770 } | |
| 771 | |
| 772 @TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
| 773 private class SuggestionsPopupWindow implements OnItemClickListener, OnDismi ssListener { | |
|
aelias_OOO_until_Jul13
2017/01/25 03:34:26
Please move this into a new .java file.
| |
| 774 protected PopupWindow mPopupWindow; | |
| 775 protected ViewGroup mContentView; | |
| 776 int mPositionX, mPositionY; | |
| 777 int mClippingLimitLeft, mClippingLimitRight; | |
| 778 private Rect mTempRect; | |
| 779 | |
| 780 private SuggestionAdapter mSuggestionsAdapter; | |
| 781 private final TextAppearanceSpan mHighlightSpan; | |
| 782 private TextView mDeleteButton; | |
| 783 private ListView mSuggestionListView; | |
| 784 private int mContainerMarginWidth; | |
| 785 private int mContainerMarginTop; | |
| 786 private double mLastInsertionMarkerBottom = 0.0; | |
| 787 private LinearLayout mContainerView; | |
| 788 | |
| 789 private boolean mDismissedByItemTap = false; | |
| 790 | |
| 791 protected void createPopupWindow() { | |
| 792 mPopupWindow = new PopupWindow(); | |
| 793 mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED) ; | |
| 794 mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARE NT)); | |
| 795 | |
| 796 mPopupWindow.setFocusable(true); | |
| 797 mPopupWindow.setClippingEnabled(false); | |
| 798 | |
| 799 mPopupWindow.setOnDismissListener(this); | |
| 800 } | |
| 801 | |
| 802 protected void initContentView() { | |
| 803 final LayoutInflater inflater = | |
| 804 (LayoutInflater) mInputMethodManagerWrapper.getContext().get SystemService( | |
| 805 Context.LAYOUT_INFLATER_SERVICE); | |
| 806 mContentView = | |
| 807 (ViewGroup) inflater.inflate(R.layout.text_edit_suggestion_c ontainer, null); | |
| 808 mContainerView = | |
| 809 (LinearLayout) mContentView.findViewById(R.id.suggestionWind owContainer); | |
| 810 ViewGroup.MarginLayoutParams lp = | |
| 811 (ViewGroup.MarginLayoutParams) mContainerView.getLayoutParam s(); | |
| 812 mContainerMarginWidth = lp.leftMargin + lp.rightMargin; | |
| 813 mContainerMarginTop = lp.topMargin; | |
| 814 mClippingLimitLeft = lp.leftMargin; | |
| 815 mClippingLimitRight = lp.rightMargin; | |
| 816 | |
| 817 mSuggestionListView = (ListView) mContentView.findViewById(R.id.sugg estionContainer); | |
| 818 | |
| 819 mSuggestionsAdapter = new SuggestionAdapter(); | |
| 820 mSuggestionListView.setAdapter(mSuggestionsAdapter); | |
| 821 mSuggestionListView.setOnItemClickListener(this); | |
| 822 | |
| 823 mDeleteButton = (TextView) mContentView.findViewById(R.id.deleteButt on); | |
| 824 mDeleteButton.setOnClickListener(new View.OnClickListener() { | |
| 825 public void onClick(View v) { | |
| 826 nativeDeleteSuggestionHighlight(mNativeImeAdapterAndroid); | |
| 827 mDismissedByItemTap = true; | |
| 828 mPopupWindow.dismiss(); | |
| 829 } | |
| 830 }); | |
| 831 } | |
| 832 | |
| 833 public SuggestionsPopupWindow() { | |
| 834 mHighlightSpan = new TextAppearanceSpan( | |
| 835 mInputMethodManagerWrapper.getContext(), R.style.SuggestionH ighlight); | |
| 836 | |
| 837 createPopupWindow(); | |
| 838 | |
| 839 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | |
| 840 mPopupWindow.setWindowLayoutType( | |
| 841 WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL); | |
| 842 } | |
| 843 mPopupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT); | |
| 844 mPopupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); | |
| 845 initContentView(); | |
| 846 | |
| 847 LayoutParams wrapContent = new LayoutParams( | |
| 848 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams. WRAP_CONTENT); | |
| 849 mContentView.setLayoutParams(wrapContent); | |
| 850 mPopupWindow.setContentView(mContentView); | |
| 851 } | |
| 852 | |
| 853 public void updatePosition() { | |
| 854 if (!mPopupWindow.isShowing()) { | |
| 855 return; | |
| 856 } | |
| 857 show(); | |
| 858 } | |
| 859 | |
| 860 public boolean isShowing() { | |
| 861 return mPopupWindow.isShowing(); | |
| 862 } | |
| 863 | |
| 864 private class SuggestionAdapter extends BaseAdapter { | |
| 865 private LayoutInflater mInflater = | |
| 866 (LayoutInflater) mInputMethodManagerWrapper.getContext().get SystemService( | |
| 867 Context.LAYOUT_INFLATER_SERVICE); | |
| 868 @Override | |
| 869 public int getCount() { | |
| 870 return mSuggestionInfos.length; | |
| 871 } | |
| 872 @Override | |
| 873 public Object getItem(int position) { | |
| 874 return mSuggestionInfos[position]; | |
| 875 } | |
| 876 @Override | |
| 877 public long getItemId(int position) { | |
| 878 return position; | |
| 879 } | |
| 880 @Override | |
| 881 public View getView(int position, View convertView, ViewGroup parent ) { | |
| 882 TextView textView = (TextView) convertView; | |
| 883 if (textView == null) { | |
| 884 textView = (TextView) mInflater.inflate( | |
| 885 R.layout.text_edit_suggestion_item, parent, false); | |
| 886 } | |
| 887 final SuggestionInfo suggestionInfo = mSuggestionInfos[position] ; | |
| 888 SpannableString textToSet = new SpannableString( | |
| 889 suggestionInfo.mPrefix + suggestionInfo.mText + suggesti onInfo.mSuffix); | |
| 890 textToSet.setSpan(mHighlightSpan, suggestionInfo.mPrefix.length( ), | |
| 891 suggestionInfo.mPrefix.length() + suggestionInfo.mText.l ength(), | |
| 892 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); | |
| 893 textView.setText(textToSet); | |
| 894 return textView; | |
| 895 } | |
| 896 } | |
| 897 | |
| 898 protected void measureContent() { | |
| 899 final DisplayMetrics displayMetrics = | |
| 900 mInputMethodManagerWrapper.getContext().getResources().getDi splayMetrics(); | |
| 901 final int horizontalMeasure = View.MeasureSpec.makeMeasureSpec( | |
| 902 displayMetrics.widthPixels, View.MeasureSpec.AT_MOST); | |
| 903 final int verticalMeasure = View.MeasureSpec.makeMeasureSpec( | |
| 904 displayMetrics.heightPixels, View.MeasureSpec.AT_MOST); | |
| 905 int width = 0; | |
| 906 View view = null; | |
| 907 for (int i = 0; i < mSuggestionInfos.length; i++) { | |
| 908 view = mSuggestionsAdapter.getView(i, view, mContentView); | |
| 909 view.getLayoutParams().width = LayoutParams.WRAP_CONTENT; | |
| 910 view.measure(horizontalMeasure, verticalMeasure); | |
| 911 width = Math.max(width, view.getMeasuredWidth()); | |
| 912 } | |
| 913 mDeleteButton.measure(horizontalMeasure, verticalMeasure); | |
| 914 width = Math.max(width, mDeleteButton.getMeasuredWidth()); | |
| 915 width += mContainerView.getPaddingLeft() + mContainerView.getPadding Right() | |
| 916 + mContainerMarginWidth; | |
| 917 // Enforce the width based on actual text widths | |
| 918 mContentView.measure(View.MeasureSpec.makeMeasureSpec(width, View.Me asureSpec.EXACTLY), | |
| 919 verticalMeasure); | |
| 920 Drawable popupBackground = mPopupWindow.getBackground(); | |
| 921 if (popupBackground != null) { | |
| 922 if (mTempRect == null) mTempRect = new Rect(); | |
| 923 popupBackground.getPadding(mTempRect); | |
| 924 width += mTempRect.left + mTempRect.right; | |
| 925 } | |
| 926 mPopupWindow.setWidth(width); | |
| 927 } | |
| 928 | |
| 929 public void show() { | |
| 930 mSuggestionsAdapter.notifyDataSetChanged(); | |
| 931 // We get the insertion point coords relative to the WebView bounds. | |
| 932 // We need to render the popup relative to the display | |
| 933 int[] embedderViewCoords = new int[2]; | |
| 934 mViewEmbedder.getAttachedView().getLocationOnScreen(embedderViewCoor ds); | |
| 935 | |
| 936 final DisplayMetrics displayMetrics = | |
| 937 mInputMethodManagerWrapper.getContext().getResources().getDi splayMetrics(); | |
| 938 | |
| 939 float density = displayMetrics.density; | |
| 940 | |
| 941 measureContent(); | |
| 942 int width = mContentView.getMeasuredWidth(); | |
| 943 | |
| 944 int positionX = 0; | |
| 945 int positionY = 0; | |
| 946 | |
| 947 positionX = Math.round( | |
| 948 mLastCursorAnchorInfo.getInsertionMarkerHorizontal() * densi ty - width / 2.0f); | |
| 949 double insertionMarkerBottom = mLastCursorAnchorInfo.getInsertionMar kerBottom(); | |
| 950 if (Double.isNaN(insertionMarkerBottom)) { | |
| 951 // certain operations, e.g. rotating the device while the menu | |
|
aelias_OOO_until_Jul13
2017/01/25 03:34:26
Seems this is no longer needed after changwan@'s f
| |
| 952 // is open, can cause getInsertionMarkerBottom() to start | |
| 953 // returning NaN, just keep using the previous value in this cas e | |
| 954 insertionMarkerBottom = mLastInsertionMarkerBottom; | |
| 955 } else { | |
| 956 mLastInsertionMarkerBottom = insertionMarkerBottom; | |
| 957 } | |
| 958 positionY = (int) Math.round( | |
| 959 (embedderViewCoords[1] + insertionMarkerBottom - 24) * densi ty); | |
| 960 | |
| 961 // horizontal clipping | |
| 962 width = mContentView.getMeasuredWidth(); | |
| 963 positionX = | |
| 964 Math.min(displayMetrics.widthPixels - width + mClippingLimit Right, positionX); | |
| 965 positionX = Math.max(-mClippingLimitLeft, positionX); | |
| 966 | |
| 967 // vertical clipping | |
| 968 final int height = mContentView.getMeasuredHeight(); | |
| 969 positionY = Math.min(positionY, displayMetrics.heightPixels - height ); | |
| 970 | |
| 971 if (isShowing()) { | |
| 972 mPopupWindow.update(positionX, positionY, -1, -1); | |
| 973 } else { | |
| 974 mPopupWindow.showAtLocation( | |
| 975 mViewEmbedder.getAttachedView(), Gravity.NO_GRAVITY, pos itionX, positionY); | |
| 976 } | |
| 977 } | |
| 978 | |
| 979 @Override | |
| 980 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { | |
| 981 SuggestionInfo suggestionInfo = mSuggestionInfos[position]; | |
| 982 nativeApplySuggestionReplacement(mNativeImeAdapterAndroid, | |
| 983 suggestionInfo.mDocumentMarkerID, suggestionInfo.mSuggestion Index); | |
| 984 // replaceWithSuggestion(suggestionInfo); | |
| 985 // hideWithCleanUp(); | |
| 986 mDismissedByItemTap = true; | |
|
aelias_OOO_until_Jul13
2017/01/25 03:34:26
Can we instead not dismiss at all at just yet? We
rlanday
2017/01/25 18:48:10
I don't understand what you're asking here, the me
| |
| 987 mPopupWindow.dismiss(); | |
| 988 } | |
| 989 | |
| 990 public void closeMenu() { | |
|
aelias_OOO_until_Jul13
2017/01/25 03:34:26
This has no callsites, please remove.
rlanday
2017/01/25 18:48:10
ah, I added this when I was thinking we might want
| |
| 991 mPopupWindow.dismiss(); | |
| 992 } | |
| 993 | |
| 994 @Override | |
| 995 public void onDismiss() { | |
| 996 if (!mDismissedByItemTap) { | |
| 997 nativeCloseSuggestionMenu(mNativeImeAdapterAndroid); | |
| 998 } | |
| 999 mDismissedByItemTap = false; | |
| 1000 } | |
| 1001 } | |
| 1002 | |
| 727 @CalledByNative | 1003 @CalledByNative |
| 728 private void populateUnderlinesFromSpans(CharSequence text, long underlines) { | 1004 private void populateUnderlinesFromSpans(CharSequence text, long underlines) { |
| 729 if (DEBUG_LOGS) { | 1005 if (DEBUG_LOGS) { |
| 730 Log.w(TAG, "populateUnderlinesFromSpans: text [%s], underlines [%d]" , text, underlines); | 1006 Log.w(TAG, "populateUnderlinesFromSpans: text [%s], underlines [%d]" , text, underlines); |
| 731 } | 1007 } |
| 732 if (!(text instanceof SpannableString)) return; | 1008 if (!(text instanceof SpannableString)) return; |
| 733 | 1009 |
| 734 SpannableString spannableString = ((SpannableString) text); | 1010 SpannableString spannableString = ((SpannableString) text); |
| 735 CharacterStyle spans[] = | 1011 CharacterStyle spans[] = |
| 736 spannableString.getSpans(0, text.length(), CharacterStyle.class) ; | 1012 spannableString.getSpans(0, text.length(), CharacterStyle.class) ; |
| 737 for (CharacterStyle span : spans) { | 1013 for (CharacterStyle span : spans) { |
| 738 if (span instanceof BackgroundColorSpan) { | 1014 if (span instanceof BackgroundColorSpan) { |
| 739 nativeAppendBackgroundColorSpan(underlines, spannableString.getS panStart(span), | 1015 nativeAppendBackgroundColorSpan(underlines, spannableString.getS panStart(span), |
| 740 spannableString.getSpanEnd(span), | 1016 spannableString.getSpanEnd(span), |
| 741 ((BackgroundColorSpan) span).getBackgroundColor()); | 1017 ((BackgroundColorSpan) span).getBackgroundColor()); |
| 1018 } else if (span instanceof SuggestionSpan) { | |
| 1019 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { | |
| 1020 // The suggestion menu is only supported on Lollipop and new er | |
| 1021 continue; | |
| 1022 } | |
| 1023 | |
| 1024 int underlineColor = 0; | |
| 1025 // Use getUnderlineColor method by reflection to avoid having to reimplement | |
|
aelias_OOO_until_Jul13
2017/01/25 03:34:26
The method is public, it's just @hide. You can pr
rlanday
2017/01/25 18:48:10
Hmm I think I tried that but I'll look into it aga
rlanday
2017/01/26 00:23:05
Trying to call span.getUnderlineColor() directly r
| |
| 1026 try { | |
| 1027 Method getUnderlineColor = SuggestionSpan.class.getMethod("g etUnderlineColor"); | |
| 1028 underlineColor = (int) getUnderlineColor.invoke((SuggestionS pan) span); | |
| 1029 } catch (IllegalAccessException | InvocationTargetException | No SuchMethodException | |
| 1030 | RuntimeException e) { | |
| 1031 continue; | |
| 1032 } | |
| 1033 | |
| 1034 SuggestionSpan suggestionSpan = (SuggestionSpan) span; | |
| 1035 nativeAppendSuggestionSpan(underlines, spannableString.getSpanSt art(suggestionSpan), | |
| 1036 spannableString.getSpanEnd(suggestionSpan), underlineCol or, | |
| 1037 suggestionSpan.getFlags(), suggestionSpan.getSuggestions ()); | |
| 1038 | |
| 742 } else if (span instanceof UnderlineSpan) { | 1039 } else if (span instanceof UnderlineSpan) { |
| 743 nativeAppendUnderlineSpan(underlines, spannableString.getSpanSta rt(span), | 1040 nativeAppendUnderlineSpan(underlines, spannableString.getSpanSta rt(span), |
| 744 spannableString.getSpanEnd(span)); | 1041 spannableString.getSpanEnd(span)); |
| 745 } | 1042 } |
| 746 } | 1043 } |
| 747 } | 1044 } |
| 748 | 1045 |
| 749 @CalledByNative | 1046 @CalledByNative |
| 750 private void cancelComposition() { | 1047 private void cancelComposition() { |
| 751 if (DEBUG_LOGS) Log.w(TAG, "cancelComposition"); | 1048 if (DEBUG_LOGS) Log.w(TAG, "cancelComposition"); |
| 752 if (mInputConnection != null) restartInput(); | 1049 if (mInputConnection != null) restartInput(); |
| 753 } | 1050 } |
| 754 | 1051 |
| 755 @CalledByNative | 1052 @CalledByNative |
| 756 private void setCharacterBounds(float[] characterBounds) { | 1053 private void setCharacterBounds(float[] characterBounds) { |
| 757 if (mCursorAnchorInfoController == null) return; | 1054 if (mCursorAnchorInfoController == null) return; |
| 758 mCursorAnchorInfoController.setCompositionCharacterBounds(characterBound s, | 1055 mCursorAnchorInfoController.setCompositionCharacterBounds(characterBound s, |
| 759 mViewEmbedder.getAttachedView()); | 1056 mViewEmbedder.getAttachedView()); |
| 760 } | 1057 } |
| 761 | 1058 |
| 1059 public class SuggestionInfo { | |
| 1060 int mDocumentMarkerID; | |
| 1061 int mSuggestionIndex; | |
| 1062 String mPrefix; | |
| 1063 String mText; | |
| 1064 String mSuffix; | |
| 1065 | |
| 1066 SuggestionInfo(int documentMarkerID, int suggestionIndex, String prefix, String text, | |
| 1067 String suffix) { | |
| 1068 mDocumentMarkerID = documentMarkerID; | |
| 1069 mSuggestionIndex = suggestionIndex; | |
| 1070 mPrefix = prefix; | |
| 1071 mText = text; | |
| 1072 mSuffix = suffix; | |
| 1073 } | |
| 1074 } | |
| 1075 | |
| 1076 @CalledByNative | |
| 1077 private void showSuggestionMenu(SuggestionInfo[] suggestionInfos) { | |
| 1078 mSuggestionInfos = suggestionInfos; | |
| 1079 if (mSuggestionsPopupWindow == null) { | |
| 1080 mSuggestionsPopupWindow = new SuggestionsPopupWindow(); | |
| 1081 } | |
| 1082 | |
| 1083 mSuggestionsPopupWindow.show(); | |
| 1084 } | |
| 1085 | |
| 762 @CalledByNative | 1086 @CalledByNative |
| 763 private void detach() { | 1087 private void detach() { |
| 764 if (DEBUG_LOGS) Log.w(TAG, "detach"); | 1088 if (DEBUG_LOGS) Log.w(TAG, "detach"); |
| 765 mNativeImeAdapterAndroid = 0; | 1089 mNativeImeAdapterAndroid = 0; |
| 766 if (mCursorAnchorInfoController != null) { | 1090 if (mCursorAnchorInfoController != null) { |
| 767 mCursorAnchorInfoController.focusedNodeChanged(false); | 1091 mCursorAnchorInfoController.focusedNodeChanged(false); |
| 768 } | 1092 } |
| 769 } | 1093 } |
| 770 | 1094 |
| 771 private native boolean nativeSendKeyEvent(long nativeImeAdapterAndroid, KeyE vent event, | 1095 private native boolean nativeSendKeyEvent(long nativeImeAdapterAndroid, KeyE vent event, |
| 772 int type, int modifiers, long timestampMs, int keyCode, int scanCode , | 1096 int type, int modifiers, long timestampMs, int keyCode, int scanCode , |
| 773 boolean isSystemKey, int unicodeChar); | 1097 boolean isSystemKey, int unicodeChar); |
| 774 private static native void nativeAppendUnderlineSpan(long underlinePtr, int start, int end); | |
| 775 private static native void nativeAppendBackgroundColorSpan(long underlinePtr , int start, | 1098 private static native void nativeAppendBackgroundColorSpan(long underlinePtr , int start, |
| 776 int end, int backgroundColor); | 1099 int end, int backgroundColor); |
| 1100 private static native void nativeAppendSuggestionSpan(long underlinePtr, int start, int end, | |
| 1101 int underlineColor, int flags, String[] suggestions); | |
| 1102 private static native void nativeAppendUnderlineSpan(long underlinePtr, int start, int end); | |
| 777 private native void nativeSetComposingText(long nativeImeAdapterAndroid, Cha rSequence text, | 1103 private native void nativeSetComposingText(long nativeImeAdapterAndroid, Cha rSequence text, |
| 778 String textStr, int newCursorPosition); | 1104 String textStr, int newCursorPosition); |
| 779 private native void nativeCommitText( | 1105 private native void nativeCommitText( |
| 780 long nativeImeAdapterAndroid, CharSequence text, String textStr, int newCursorPosition); | 1106 long nativeImeAdapterAndroid, CharSequence text, String textStr, int newCursorPosition); |
| 781 private native void nativeFinishComposingText(long nativeImeAdapterAndroid); | 1107 private native void nativeFinishComposingText(long nativeImeAdapterAndroid); |
| 782 private native void nativeAttachImeAdapter(long nativeImeAdapterAndroid); | 1108 private native void nativeAttachImeAdapter(long nativeImeAdapterAndroid); |
| 783 private native void nativeSetEditableSelectionOffsets(long nativeImeAdapterA ndroid, | 1109 private native void nativeSetEditableSelectionOffsets(long nativeImeAdapterA ndroid, |
| 784 int start, int end); | 1110 int start, int end); |
| 785 private native void nativeSetComposingRegion(long nativeImeAdapterAndroid, i nt start, int end); | 1111 private native void nativeSetComposingRegion(long nativeImeAdapterAndroid, i nt start, int end); |
| 786 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid , | 1112 private native void nativeDeleteSurroundingText(long nativeImeAdapterAndroid , |
| 787 int before, int after); | 1113 int before, int after); |
| 788 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid); | 1114 private native void nativeResetImeAdapter(long nativeImeAdapterAndroid); |
| 789 private native boolean nativeRequestTextInputStateUpdate(long nativeImeAdapt erAndroid); | 1115 private native boolean nativeRequestTextInputStateUpdate(long nativeImeAdapt erAndroid); |
| 790 private native void nativeRequestCursorUpdate(long nativeImeAdapterAndroid, | 1116 private native void nativeRequestCursorUpdate(long nativeImeAdapterAndroid, |
| 791 boolean immediateRequest, boolean monitorRequest); | 1117 boolean immediateRequest, boolean monitorRequest); |
| 1118 private native void nativeApplySuggestionReplacement( | |
| 1119 long nativeImeAdapterAndroid, int documentMarkerID, int suggestionIn dex); | |
| 1120 private native void nativeDeleteSuggestionHighlight(long nativeImeAdapterAnd roid); | |
| 1121 private native void nativeCloseSuggestionMenu(long nativeImeAdapterAndroid); | |
| 792 } | 1122 } |
| OLD | NEW |