1 /*
2  * Copyright (C) 2008-2012  OMRON SOFTWARE Co., Ltd.
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 /*
18  * Copyright (C) 2008-2009 Google Inc.
19  *
20  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
21  * use this file except in compliance with the License. You may obtain a copy of
22  * the License at
23  *
24  * http://www.apache.org/licenses/LICENSE-2.0
25  *
26  * Unless required by applicable law or agreed to in writing, software
27  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
28  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
29  * License for the specific language governing permissions and limitations under
30  * the License.
31  */
32 
33 package jp.co.omronsoft.openwnn.EN;
34 
35 import jp.co.omronsoft.openwnn.*;
36 import android.content.Context;
37 import android.content.res.Resources;
38 import android.graphics.drawable.Drawable;
39 import android.os.Handler;
40 import android.os.Message;
41 import android.text.Layout;
42 import android.text.SpannableStringBuilder;
43 import android.text.StaticLayout;
44 import android.text.Spanned;
45 import android.text.style.ImageSpan;
46 import android.text.style.DynamicDrawableSpan;
47 import android.view.Gravity;
48 import android.view.LayoutInflater;
49 import android.view.MotionEvent;
50 import android.view.View;
51 import android.view.View.OnTouchListener;
52 import android.widget.PopupWindow;
53 import android.widget.TextView;
54 
55 import java.util.ArrayList;
56 import java.util.List;
57 
58 public class TutorialEN implements OnTouchListener {
59 
60     private List<Bubble> mBubbles = new ArrayList<Bubble>();
61     private View mInputView;
62     private OpenWnnEN mIme;
63     private int[] mLocation = new int[2];
64     private static final int MSG_SHOW_BUBBLE = 0;
65 
66     private int mBubbleIndex;
67     private boolean mEnableKeyTouch = false;
68 
69     Handler mHandler = new Handler() {
70         @Override
71         public void handleMessage(Message msg) {
72             switch (msg.what) {
73                 case MSG_SHOW_BUBBLE:
74                     Bubble bubba = (Bubble) msg.obj;
75                     bubba.show(mLocation[0], mLocation[1]);
76                     break;
77             }
78         }
79     };
80 
81     class Bubble {
82         Drawable bubbleBackground;
83         int x;
84         int y;
85         int width;
86         int gravity;
87         CharSequence text;
88         boolean dismissOnTouch;
89         boolean dismissOnClose;
90         PopupWindow window;
91         TextView textView;
92         View inputView;
93 
Bubble(Context context, View inputView, int backgroundResource, int bx, int by, int description, int guide)94         Bubble(Context context, View inputView,
95                 int backgroundResource, int bx, int by, int description, int guide) {
96 
97             CharSequence text = context.getResources().getText(description);
98             init(context, inputView, backgroundResource, bx, by, text, guide, false);
99         }
100 
Bubble(Context context, View inputView, int backgroundResource, int bx, int by, CharSequence description, int guide, boolean leftAlign)101         Bubble(Context context, View inputView, int backgroundResource, int bx, int by,
102                CharSequence description, int guide, boolean leftAlign) {
103             init(context, inputView, backgroundResource, bx, by, description, guide, leftAlign);
104         }
105 
init(Context context, View inputView, int backgroundResource, int bx, int by, CharSequence description, int guide, boolean leftAlign)106         void init(Context context, View inputView, int backgroundResource,
107                   int bx, int by, CharSequence description, int guide, boolean leftAlign) {
108             bubbleBackground = context.getResources().getDrawable(backgroundResource);
109             x = bx;
110             y = by;
111             width = (int) (inputView.getWidth() * 0.9);
112             this.gravity = Gravity.TOP | Gravity.LEFT;
113             text = new SpannableStringBuilder()
114                 .append(description)
115                 .append("\n")
116                 .append(context.getResources().getText(guide));
117             this.dismissOnTouch = true;
118             this.dismissOnClose = false;
119             this.inputView = inputView;
120             window = new PopupWindow(context);
121             window.setBackgroundDrawable(null);
122             LayoutInflater inflate =
123                 (LayoutInflater) context
124                         .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
125             textView = (TextView) inflate.inflate(R.layout.bubble_text, null);
126             textView.setBackgroundDrawable(bubbleBackground);
127             textView.setText(text);
128             if (leftAlign) {
129                 textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
130             }
131             window.setContentView(textView);
132             window.setFocusable(false);
133             window.setTouchable(true);
134             window.setOutsideTouchable(false);
135         }
136 
chooseSize(PopupWindow pop, View parentView, CharSequence text, TextView tv)137         private int chooseSize(PopupWindow pop, View parentView, CharSequence text, TextView tv) {
138             int wid = tv.getPaddingLeft() + tv.getPaddingRight();
139             int ht = tv.getPaddingTop() + tv.getPaddingBottom();
140 
141             /*
142              * Figure out how big the text would be if we laid it out to the
143              * full width of this view minus the border.
144              */
145             int cap = width - wid;
146 
147             Layout l = new StaticLayout(text, tv.getPaint(), cap,
148                                         Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
149             float max = 0;
150             for (int i = 0; i < l.getLineCount(); i++) {
151                 max = Math.max(max, l.getLineWidth(i));
152             }
153 
154             /*
155              * Now set the popup size to be big enough for the text plus the border.
156              */
157             pop.setWidth(width);
158             pop.setHeight(ht + l.getHeight());
159             return l.getHeight();
160         }
161 
show(int offx, int offy)162         void show(int offx, int offy) {
163             int textHeight = chooseSize(window, inputView, text, textView);
164             offy -= textView.getPaddingTop() + textHeight;
165             if (inputView.getVisibility() == View.VISIBLE
166                     && inputView.getWindowVisibility() == View.VISIBLE) {
167                 try {
168                     if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) offy -= window.getHeight();
169                     if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) offx -= window.getWidth();
170                     textView.setOnTouchListener(new View.OnTouchListener() {
171                         public boolean onTouch(View view, MotionEvent me) {
172                             boolean ret = !mEnableKeyTouch;
173                             switch (me.getAction()) {
174                             case MotionEvent.ACTION_UP:
175                                 if (mBubbleIndex >= mBubbles.size()) {
176                                     mInputView.setOnTouchListener(null);
177                                 } else {
178                                     TutorialEN.this.next();
179                                 }
180                                 break;
181                             default:
182                                 break;
183                             }
184                             return ret;
185                         }
186                     });
187                     window.showAtLocation(inputView, Gravity.NO_GRAVITY, x + offx, y + offy);
188                 } catch (Exception e) {
189                 }
190             }
191         }
192 
hide()193         void hide() {
194             if (window.isShowing()) {
195                 textView.setOnTouchListener(null);
196                 window.dismiss();
197             }
198         }
199 
isShowing()200         boolean isShowing() {
201             return window.isShowing();
202         }
203     }
204 
205     /** Constructor */
TutorialEN(OpenWnnEN ime, View inputView, DefaultSoftKeyboardEN inputManager)206     public TutorialEN(OpenWnnEN ime, View inputView, DefaultSoftKeyboardEN inputManager) {
207         mInputView = inputView;
208         mIme = ime;
209 
210         Context context = inputView.getContext();
211         int inputWidth = inputView.getWidth();
212         Resources r = inputView.getContext().getResources();
213         final int x = inputWidth / 20;
214         r.getDimensionPixelOffset(R.dimen.bubble_pointer_offset);
215 
216         SpannableStringBuilder spannable = new SpannableStringBuilder();
217         Bubble button;
218 
219         spannable.clear();
220         spannable.append(r.getText(R.string.tip_en_to_open_keyboard));
221         button = new Bubble(context, inputView,
222                 R.drawable.dialog_bubble, x, 0,
223                 spannable, R.string.touch_to_continue, false);
224         mBubbles.add(button);
225 
226         spannable.clear();
227         spannable.append(r.getText(R.string.tip_en_to_close_keyboard));
228 
229         setSpan(spannable, "\u2190", R.drawable.tutorial_back);
230 
231         button = new Bubble(context, inputView,
232                 R.drawable.dialog_bubble, x, 0,
233                 spannable, R.string.touch_to_continue, false);
234         mBubbles.add(button);
235 
236         button = new Bubble(context, inputView,
237                 R.drawable.dialog_bubble, x, 0,
238                 R.string.tip_en_end_of_tutorial, R.string.touch_to_finish);
239         mBubbles.add(button);
240     }
241 
setSpan(SpannableStringBuilder spannable, String marker, int imageResourceId)242     private void setSpan(SpannableStringBuilder spannable, String marker, int imageResourceId) {
243         String text = spannable.toString();
244         int target = text.indexOf(marker);
245         while (0 <= target) {
246             ImageSpan span = new ImageSpan(mIme, imageResourceId,
247                     DynamicDrawableSpan.ALIGN_BOTTOM);
248             spannable.setSpan(span, target, target + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
249             target = text.indexOf(marker, target + 1);
250         }
251     }
252 
start()253     public void start() {
254         mInputView.getLocationInWindow(mLocation);
255         mBubbleIndex = -1;
256         mInputView.setOnTouchListener(this);
257         next();
258     }
259 
next()260     boolean next() {
261         if (mBubbleIndex >= 0) {
262             if (!mBubbles.get(mBubbleIndex).isShowing()) {
263                 return true;
264             }
265             for (int i = 0; i <= mBubbleIndex; i++) {
266                 mBubbles.get(i).hide();
267             }
268         }
269         mBubbleIndex++;
270         if (mBubbleIndex >= mBubbles.size()) {
271             mEnableKeyTouch = true;
272             mIme.sendDownUpKeyEvents(-1);
273             mIme.tutorialDone();
274             return false;
275         }
276 
277         mHandler.sendMessageDelayed(
278                 mHandler.obtainMessage(MSG_SHOW_BUBBLE, mBubbles.get(mBubbleIndex)), 500);
279         return true;
280     }
281 
hide()282     void hide() {
283         for (int i = 0; i < mBubbles.size(); i++) {
284             mBubbles.get(i).hide();
285         }
286         mInputView.setOnTouchListener(null);
287     }
288 
close()289     public boolean close() {
290         mHandler.removeMessages(MSG_SHOW_BUBBLE);
291         hide();
292         return true;
293     }
294 
onTouch(View v, MotionEvent event)295     public boolean onTouch(View v, MotionEvent event) {
296         boolean ret = !mEnableKeyTouch;
297         if (event.getAction() == MotionEvent.ACTION_UP) {
298             if (mBubbleIndex >= mBubbles.size()) {
299                 mInputView.setOnTouchListener(null);
300             }
301         }
302         return ret;
303     }
304 }
305