• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008-2009 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.android.inputmethod.latin;
18 
19 import android.content.Context;
20 import android.graphics.Rect;
21 import android.graphics.drawable.Drawable;
22 import android.os.Handler;
23 import android.os.Message;
24 import android.text.Layout;
25 import android.text.SpannableStringBuilder;
26 import android.text.StaticLayout;
27 import android.view.Gravity;
28 import android.view.LayoutInflater;
29 import android.view.MotionEvent;
30 import android.view.View;
31 import android.view.View.OnTouchListener;
32 import android.widget.PopupWindow;
33 import android.widget.TextView;
34 
35 import java.util.ArrayList;
36 import java.util.List;
37 
38 public class Tutorial implements OnTouchListener {
39 
40     private List<Bubble> mBubbles = new ArrayList<Bubble>();
41     private long mStartTime;
42     private static final long MINIMUM_TIME = 6000;
43     private static final long MAXIMUM_TIME = 20000;
44     private View mInputView;
45     private LatinIME mIme;
46     private int[] mLocation = new int[2];
47     private int mBubblePointerOffset;
48 
49     private static final int MSG_SHOW_BUBBLE = 0;
50 
51     private int mBubbleIndex;
52 
53     Handler mHandler = new Handler() {
54         @Override
55         public void handleMessage(Message msg) {
56             switch (msg.what) {
57                 case MSG_SHOW_BUBBLE:
58                     Bubble bubba = (Bubble) msg.obj;
59                     bubba.show(mLocation[0], mLocation[1]);
60                     break;
61             }
62         }
63     };
64 
65     class Bubble {
66         Drawable bubbleBackground;
67         int x;
68         int y;
69         int width;
70         int gravity;
71         CharSequence text;
72         boolean dismissOnTouch;
73         boolean dismissOnClose;
74         PopupWindow window;
75         TextView textView;
76         View inputView;
77 
Bubble(Context context, View inputView, int backgroundResource, int bx, int by, int textResource1, int textResource2)78         Bubble(Context context, View inputView,
79                 int backgroundResource, int bx, int by, int textResource1, int textResource2) {
80             bubbleBackground = context.getResources().getDrawable(backgroundResource);
81             x = bx;
82             y = by;
83             width = (int) (inputView.getWidth() * 0.9);
84             this.gravity = Gravity.TOP | Gravity.LEFT;
85             text = new SpannableStringBuilder()
86                 .append(context.getResources().getText(textResource1))
87                 .append("\n")
88                 .append(context.getResources().getText(textResource2));
89             this.dismissOnTouch = true;
90             this.dismissOnClose = false;
91             this.inputView = inputView;
92             window = new PopupWindow(context);
93             window.setBackgroundDrawable(null);
94             LayoutInflater inflate =
95                 (LayoutInflater) context
96                         .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
97             textView = (TextView) inflate.inflate(R.layout.bubble_text, null);
98             textView.setBackgroundDrawable(bubbleBackground);
99             textView.setText(text);
100             //textView.setText(textResource1);
101             window.setContentView(textView);
102             window.setFocusable(false);
103             window.setTouchable(true);
104             window.setOutsideTouchable(false);
105         }
106 
chooseSize(PopupWindow pop, View parentView, CharSequence text, TextView tv)107         private int chooseSize(PopupWindow pop, View parentView, CharSequence text, TextView tv) {
108             int wid = tv.getPaddingLeft() + tv.getPaddingRight();
109             int ht = tv.getPaddingTop() + tv.getPaddingBottom();
110 
111             /*
112              * Figure out how big the text would be if we laid it out to the
113              * full width of this view minus the border.
114              */
115             int cap = width - wid;
116 
117             Layout l = new StaticLayout(text, tv.getPaint(), cap,
118                                         Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
119             float max = 0;
120             for (int i = 0; i < l.getLineCount(); i++) {
121                 max = Math.max(max, l.getLineWidth(i));
122             }
123 
124             /*
125              * Now set the popup size to be big enough for the text plus the border.
126              */
127             pop.setWidth(width);
128             pop.setHeight(ht + l.getHeight());
129             return l.getHeight();
130         }
131 
show(int offx, int offy)132         void show(int offx, int offy) {
133             int textHeight = chooseSize(window, inputView, text, textView);
134             offy -= textView.getPaddingTop() + textHeight;
135             if (inputView.getVisibility() == View.VISIBLE
136                     && inputView.getWindowVisibility() == View.VISIBLE) {
137                 try {
138                     if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) offy -= window.getHeight();
139                     if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) offx -= window.getWidth();
140                     textView.setOnTouchListener(new View.OnTouchListener() {
141                         public boolean onTouch(View view, MotionEvent me) {
142                             Tutorial.this.next();
143                             return true;
144                         }
145                     });
146                     window.showAtLocation(inputView, Gravity.NO_GRAVITY, x + offx, y + offy);
147                 } catch (Exception e) {
148                     // Input view is not valid
149                 }
150             }
151         }
152 
hide()153         void hide() {
154             if (window.isShowing()) {
155                 textView.setOnTouchListener(null);
156                 window.dismiss();
157             }
158         }
159 
isShowing()160         boolean isShowing() {
161             return window.isShowing();
162         }
163     }
164 
Tutorial(LatinIME ime, LatinKeyboardView inputView)165     public Tutorial(LatinIME ime, LatinKeyboardView inputView) {
166         Context context = inputView.getContext();
167         mIme = ime;
168         int inputWidth = inputView.getWidth();
169         final int x = inputWidth / 20; // Half of 1/10th
170         mBubblePointerOffset = inputView.getContext().getResources()
171             .getDimensionPixelOffset(R.dimen.bubble_pointer_offset);
172         Bubble bWelcome = new Bubble(context, inputView,
173                 R.drawable.dialog_bubble_step02, x, 0,
174                 R.string.tip_to_open_keyboard, R.string.touch_to_continue);
175         mBubbles.add(bWelcome);
176         Bubble bAccents = new Bubble(context, inputView,
177                 R.drawable.dialog_bubble_step02, x, 0,
178                 R.string.tip_to_view_accents, R.string.touch_to_continue);
179         mBubbles.add(bAccents);
180         Bubble b123 = new Bubble(context, inputView,
181                 R.drawable.dialog_bubble_step07, x, 0,
182                 R.string.tip_to_open_symbols, R.string.touch_to_continue);
183         mBubbles.add(b123);
184         Bubble bABC = new Bubble(context, inputView,
185                 R.drawable.dialog_bubble_step07, x, 0,
186                 R.string.tip_to_close_symbols, R.string.touch_to_continue);
187         mBubbles.add(bABC);
188         Bubble bSettings = new Bubble(context, inputView,
189                 R.drawable.dialog_bubble_step07, x, 0,
190                 R.string.tip_to_launch_settings, R.string.touch_to_continue);
191         mBubbles.add(bSettings);
192         Bubble bDone = new Bubble(context, inputView,
193                 R.drawable.dialog_bubble_step02, x, 0,
194                 R.string.tip_to_start_typing, R.string.touch_to_finish);
195         mBubbles.add(bDone);
196         mInputView = inputView;
197     }
198 
start()199     void start() {
200         mInputView.getLocationInWindow(mLocation);
201         mBubbleIndex = -1;
202         mInputView.setOnTouchListener(this);
203         next();
204     }
205 
next()206     boolean next() {
207         if (mBubbleIndex >= 0) {
208             // If the bubble is not yet showing, don't move to the next.
209             if (!mBubbles.get(mBubbleIndex).isShowing()) {
210                 return true;
211             }
212             // Hide all previous bubbles as well, as they may have had a delayed show
213             for (int i = 0; i <= mBubbleIndex; i++) {
214                 mBubbles.get(i).hide();
215             }
216         }
217         mBubbleIndex++;
218         if (mBubbleIndex >= mBubbles.size()) {
219             mInputView.setOnTouchListener(null);
220             mIme.sendDownUpKeyEvents(-1); // Inform the setupwizard that tutorial is in last bubble
221             mIme.tutorialDone();
222             return false;
223         }
224         if (mBubbleIndex == 3 || mBubbleIndex == 4) {
225             mIme.mKeyboardSwitcher.toggleSymbols();
226         }
227         mHandler.sendMessageDelayed(
228                 mHandler.obtainMessage(MSG_SHOW_BUBBLE, mBubbles.get(mBubbleIndex)), 500);
229         return true;
230     }
231 
hide()232     void hide() {
233         for (int i = 0; i < mBubbles.size(); i++) {
234             mBubbles.get(i).hide();
235         }
236         mInputView.setOnTouchListener(null);
237     }
238 
close()239     boolean close() {
240         mHandler.removeMessages(MSG_SHOW_BUBBLE);
241         hide();
242         return true;
243     }
244 
onTouch(View v, MotionEvent event)245     public boolean onTouch(View v, MotionEvent event) {
246         if (event.getAction() == MotionEvent.ACTION_DOWN) {
247             next();
248         }
249         return true;
250     }
251 }
252