• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.inputmethod.pinyin;
18 
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.graphics.Canvas;
22 import android.graphics.Paint;
23 import android.graphics.Paint.FontMetricsInt;
24 import android.graphics.drawable.Drawable;
25 import android.util.AttributeSet;
26 import android.view.KeyEvent;
27 import android.view.View;
28 import android.view.ViewGroup.LayoutParams;
29 
30 /**
31  * View used to show composing string (The Pinyin string for the unselected
32  * syllables and the Chinese string for the selected syllables.)
33  */
34 public class ComposingView extends View {
35     /**
36      * <p>
37      * There are three statuses for the composing view.
38      * </p>
39      *
40      * <p>
41      * {@link #SHOW_PINYIN} is used to show the current Pinyin string without
42      * highlighted effect. When user inputs Pinyin characters one by one, the
43      * Pinyin string will be shown in this mode.
44      * </p>
45      * <p>
46      * {@link #SHOW_STRING_LOWERCASE} is used to show the Pinyin string in
47      * lowercase with highlighted effect. When user presses UP key and there is
48      * no fixed Chinese characters, composing view will switch from
49      * {@link #SHOW_PINYIN} to this mode, and in this mode, user can press
50      * confirm key to input the lower-case string, so that user can input
51      * English letter in Chinese mode.
52      * </p>
53      * <p>
54      * {@link #EDIT_PINYIN} is used to edit the Pinyin string (shown with
55      * highlighted effect). When current status is {@link #SHOW_PINYIN} and user
56      * presses UP key, if there are fixed Characters, the input method will
57      * switch to {@link #EDIT_PINYIN} thus user can modify some characters in
58      * the middle of the Pinyin string. If the current status is
59      * {@link #SHOW_STRING_LOWERCASE} and user presses LEFT and RIGHT key, it
60      * will also switch to {@link #EDIT_PINYIN}.
61      * </p>
62      * <p>
63      * Whenever user presses down key, the status switches to
64      * {@link #SHOW_PINYIN}.
65      * </p>
66      * <p>
67      * When composing view's status is {@link #SHOW_PINYIN}, the IME's status is
68      * {@link PinyinIME.ImeState#STATE_INPUT}, otherwise, the IME's status
69      * should be {@link PinyinIME.ImeState#STATE_COMPOSING}.
70      * </p>
71      */
72     public enum ComposingStatus {
73         SHOW_PINYIN, SHOW_STRING_LOWERCASE, EDIT_PINYIN,
74     }
75 
76     private static final int LEFT_RIGHT_MARGIN = 5;
77 
78     /**
79      * Used to draw composing string. When drawing the active and idle part of
80      * the spelling(Pinyin) string, the color may be changed.
81      */
82     private Paint mPaint;
83 
84     /**
85      * Drawable used to draw highlight effect.
86      */
87     private Drawable mHlDrawable;
88 
89     /**
90      * Drawable used to draw cursor for editing mode.
91      */
92     private Drawable mCursor;
93 
94     /**
95      * Used to estimate dimensions to show the string .
96      */
97     private FontMetricsInt mFmi;
98 
99     private int mStrColor;
100     private int mStrColorHl;
101     private int mStrColorIdle;
102 
103     private int mFontSize;
104 
105     private ComposingStatus mComposingStatus;
106 
107     PinyinIME.DecodingInfo mDecInfo;
108 
ComposingView(Context context, AttributeSet attrs)109     public ComposingView(Context context, AttributeSet attrs) {
110         super(context, attrs);
111 
112         Resources r = context.getResources();
113         mHlDrawable = r.getDrawable(R.drawable.composing_hl_bg);
114         mCursor = r.getDrawable(R.drawable.composing_area_cursor);
115 
116         mStrColor = r.getColor(R.color.composing_color);
117         mStrColorHl = r.getColor(R.color.composing_color_hl);
118         mStrColorIdle = r.getColor(R.color.composing_color_idle);
119 
120         mFontSize = r.getDimensionPixelSize(R.dimen.composing_height);
121 
122         mPaint = new Paint();
123         mPaint.setColor(mStrColor);
124         mPaint.setAntiAlias(true);
125         mPaint.setTextSize(mFontSize);
126 
127         mFmi = mPaint.getFontMetricsInt();
128     }
129 
reset()130     public void reset() {
131         mComposingStatus = ComposingStatus.SHOW_PINYIN;
132     }
133 
134     /**
135      * Set the composing string to show. If the IME status is
136      * {@link PinyinIME.ImeState#STATE_INPUT}, the composing view's status will
137      * be set to {@link ComposingStatus#SHOW_PINYIN}, otherwise the composing
138      * view will set its status to {@link ComposingStatus#SHOW_STRING_LOWERCASE}
139      * or {@link ComposingStatus#EDIT_PINYIN} automatically.
140      */
setDecodingInfo(PinyinIME.DecodingInfo decInfo, PinyinIME.ImeState imeStatus)141     public void setDecodingInfo(PinyinIME.DecodingInfo decInfo,
142             PinyinIME.ImeState imeStatus) {
143         mDecInfo = decInfo;
144 
145         if (PinyinIME.ImeState.STATE_INPUT == imeStatus) {
146             mComposingStatus = ComposingStatus.SHOW_PINYIN;
147             mDecInfo.moveCursorToEdge(false);
148         } else {
149             if (decInfo.getFixedLen() != 0
150                     || ComposingStatus.EDIT_PINYIN == mComposingStatus) {
151                 mComposingStatus = ComposingStatus.EDIT_PINYIN;
152             } else {
153                 mComposingStatus = ComposingStatus.SHOW_STRING_LOWERCASE;
154             }
155             mDecInfo.moveCursor(0);
156         }
157 
158         measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
159         requestLayout();
160         invalidate();
161     }
162 
moveCursor(int keyCode)163     public boolean moveCursor(int keyCode) {
164         if (keyCode != KeyEvent.KEYCODE_DPAD_LEFT
165                 && keyCode != KeyEvent.KEYCODE_DPAD_RIGHT) return false;
166 
167         if (ComposingStatus.EDIT_PINYIN == mComposingStatus) {
168             int offset = 0;
169             if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT)
170                 offset = -1;
171             else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) offset = 1;
172             mDecInfo.moveCursor(offset);
173         } else if (ComposingStatus.SHOW_STRING_LOWERCASE == mComposingStatus) {
174             if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT
175                     || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
176                 mComposingStatus = ComposingStatus.EDIT_PINYIN;
177 
178                 measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
179                 requestLayout();
180             }
181 
182         }
183         invalidate();
184         return true;
185     }
186 
getComposingStatus()187     public ComposingStatus getComposingStatus() {
188         return mComposingStatus;
189     }
190 
191     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)192     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
193         float width;
194         int height;
195         height = mFmi.bottom - mFmi.top + mPaddingTop + mPaddingBottom;
196 
197         if (null == mDecInfo) {
198             width = 0;
199         } else {
200             width = mPaddingLeft + mPaddingRight + LEFT_RIGHT_MARGIN * 2;
201 
202             String str;
203             if (ComposingStatus.SHOW_STRING_LOWERCASE == mComposingStatus) {
204                 str = mDecInfo.getOrigianlSplStr().toString();
205             } else {
206                 str = mDecInfo.getComposingStrForDisplay();
207             }
208             width += mPaint.measureText(str, 0, str.length());
209         }
210         setMeasuredDimension((int) (width + 0.5f), height);
211     }
212 
213     @Override
onDraw(Canvas canvas)214     protected void onDraw(Canvas canvas) {
215         if (ComposingStatus.EDIT_PINYIN == mComposingStatus
216                 || ComposingStatus.SHOW_PINYIN == mComposingStatus) {
217             drawForPinyin(canvas);
218             return;
219         }
220 
221         float x, y;
222         x = mPaddingLeft + LEFT_RIGHT_MARGIN;
223         y = -mFmi.top + mPaddingTop;
224 
225         mPaint.setColor(mStrColorHl);
226         mHlDrawable.setBounds(mPaddingLeft, mPaddingTop, getWidth()
227                 - mPaddingRight, getHeight() - mPaddingBottom);
228         mHlDrawable.draw(canvas);
229 
230         String splStr = mDecInfo.getOrigianlSplStr().toString();
231         canvas.drawText(splStr, 0, splStr.length(), x, y, mPaint);
232     }
233 
drawCursor(Canvas canvas, float x)234     private void drawCursor(Canvas canvas, float x) {
235         mCursor.setBounds((int) x, mPaddingTop, (int) x
236                 + mCursor.getIntrinsicWidth(), getHeight() - mPaddingBottom);
237         mCursor.draw(canvas);
238     }
239 
drawForPinyin(Canvas canvas)240     private void drawForPinyin(Canvas canvas) {
241         float x, y;
242         x = mPaddingLeft + LEFT_RIGHT_MARGIN;
243         y = -mFmi.top + mPaddingTop;
244 
245         mPaint.setColor(mStrColor);
246 
247         int cursorPos = mDecInfo.getCursorPosInCmpsDisplay();
248         int cmpsPos = cursorPos;
249         String cmpsStr = mDecInfo.getComposingStrForDisplay();
250         int activeCmpsLen = mDecInfo.getActiveCmpsDisplayLen();
251         if (cursorPos > activeCmpsLen) cmpsPos = activeCmpsLen;
252         canvas.drawText(cmpsStr, 0, cmpsPos, x, y, mPaint);
253         x += mPaint.measureText(cmpsStr, 0, cmpsPos);
254         if (cursorPos <= activeCmpsLen) {
255             if (ComposingStatus.EDIT_PINYIN == mComposingStatus) {
256                 drawCursor(canvas, x);
257             }
258             canvas.drawText(cmpsStr, cmpsPos, activeCmpsLen, x, y, mPaint);
259         }
260 
261         x += mPaint.measureText(cmpsStr, cmpsPos, activeCmpsLen);
262 
263         if (cmpsStr.length() > activeCmpsLen) {
264             mPaint.setColor(mStrColorIdle);
265             int oriPos = activeCmpsLen;
266             if (cursorPos > activeCmpsLen) {
267                 if (cursorPos > cmpsStr.length()) cursorPos = cmpsStr.length();
268                 canvas.drawText(cmpsStr, oriPos, cursorPos, x, y, mPaint);
269                 x += mPaint.measureText(cmpsStr, oriPos, cursorPos);
270 
271                 if (ComposingStatus.EDIT_PINYIN == mComposingStatus) {
272                     drawCursor(canvas, x);
273                 }
274 
275                 oriPos = cursorPos;
276             }
277             canvas.drawText(cmpsStr, oriPos, cmpsStr.length(), x, y, mPaint);
278         }
279     }
280 }
281