1/*
2 * Copyright (C) 2008,2009  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
33package jp.co.omronsoft.openwnn.JAJP;
34
35import jp.co.omronsoft.openwnn.*;
36import android.content.Context;
37import android.content.res.Resources;
38import android.graphics.drawable.Drawable;
39import android.os.Handler;
40import android.os.Message;
41import android.text.Layout;
42import android.text.SpannableStringBuilder;
43import android.text.StaticLayout;
44import android.text.Spanned;
45import android.text.style.ImageSpan;
46import android.text.style.DynamicDrawableSpan;
47import android.view.Gravity;
48import android.view.LayoutInflater;
49import android.view.MotionEvent;
50import android.view.View;
51import android.view.View.OnTouchListener;
52import android.widget.PopupWindow;
53import android.widget.TextView;
54
55import java.util.ArrayList;
56import java.util.List;
57
58public class TutorialJAJP implements OnTouchListener {
59
60    private List<Bubble> mBubbles = new ArrayList<Bubble>();
61    private static final int LONG_PRESS_INDEX = 8;
62    private View mInputView;
63    private OpenWnnJAJP mIme;
64    private int[] mLocation = new int[2];
65    private static final int MSG_SHOW_BUBBLE = 0;
66
67    private int mBubbleIndex;
68    private DefaultSoftKeyboardJAJP mInputManager;
69    private boolean mEnableKeyTouch = false;
70
71    Handler mHandler = new Handler() {
72        @Override
73        public void handleMessage(Message msg) {
74            switch (msg.what) {
75                case MSG_SHOW_BUBBLE:
76                    Bubble bubba = (Bubble) msg.obj;
77                    bubba.show(mLocation[0], mLocation[1]);
78                    break;
79            }
80        }
81    };
82
83    class Bubble {
84        Drawable bubbleBackground;
85        int x;
86        int y;
87        int width;
88        int gravity;
89        CharSequence text;
90        boolean dismissOnTouch;
91        boolean dismissOnClose;
92        PopupWindow window;
93        TextView textView;
94        View inputView;
95
96        Bubble(Context context, View inputView,
97                int backgroundResource, int bx, int by, int description, int guide) {
98
99            CharSequence text = context.getResources().getText(description);
100            init(context, inputView, backgroundResource, bx, by, text, guide, false);
101        }
102
103        Bubble(Context context, View inputView, int backgroundResource, int bx, int by,
104               CharSequence description, int guide, boolean leftAlign) {
105            init(context, inputView, backgroundResource, bx, by, description, guide, leftAlign);
106        }
107
108        void init(Context context, View inputView, int backgroundResource,
109                  int bx, int by, CharSequence description, int guide, boolean leftAlign) {
110            bubbleBackground = context.getResources().getDrawable(backgroundResource);
111            x = bx;
112            y = by;
113            width = (int) (inputView.getWidth() * 0.9);
114            this.gravity = Gravity.TOP | Gravity.LEFT;
115            text = new SpannableStringBuilder()
116                .append(description)
117                .append("\n")
118                .append(context.getResources().getText(guide));
119            this.dismissOnTouch = true;
120            this.dismissOnClose = false;
121            this.inputView = inputView;
122            window = new PopupWindow(context);
123            window.setBackgroundDrawable(null);
124            LayoutInflater inflate =
125                (LayoutInflater) context
126                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
127            textView = (TextView) inflate.inflate(R.layout.bubble_text, null);
128            textView.setBackgroundDrawable(bubbleBackground);
129            textView.setText(text);
130            if (leftAlign) {
131                textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
132            }
133            window.setContentView(textView);
134            window.setFocusable(false);
135            window.setTouchable(true);
136            window.setOutsideTouchable(false);
137        }
138
139        private int chooseSize(PopupWindow pop, View parentView, CharSequence text, TextView tv) {
140            int wid = tv.getPaddingLeft() + tv.getPaddingRight();
141            int ht = tv.getPaddingTop() + tv.getPaddingBottom();
142
143            /*
144             * Figure out how big the text would be if we laid it out to the
145             * full width of this view minus the border.
146             */
147            int cap = width - wid;
148
149            Layout l = new StaticLayout(text, tv.getPaint(), cap,
150                                        Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
151            float max = 0;
152            for (int i = 0; i < l.getLineCount(); i++) {
153                max = Math.max(max, l.getLineWidth(i));
154            }
155
156            /*
157             * Now set the popup size to be big enough for the text plus the border.
158             */
159            pop.setWidth(width);
160            pop.setHeight(ht + l.getHeight());
161            return l.getHeight();
162        }
163
164        void show(int offx, int offy) {
165            int textHeight = chooseSize(window, inputView, text, textView);
166            offy -= textView.getPaddingTop() + textHeight;
167            if (inputView.getVisibility() == View.VISIBLE
168                    && inputView.getWindowVisibility() == View.VISIBLE) {
169                try {
170                    if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) offy -= window.getHeight();
171                    if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) offx -= window.getWidth();
172                    textView.setOnTouchListener(new View.OnTouchListener() {
173                        public boolean onTouch(View view, MotionEvent me) {
174
175                            boolean ret = !mEnableKeyTouch;
176                            switch (me.getAction()) {
177                            case MotionEvent.ACTION_UP:
178                                if (mBubbleIndex >= mBubbles.size()) {
179                                    mInputView.setOnTouchListener(null);
180                                } else {
181                                    TutorialJAJP.this.next();
182                                }
183                                break;
184                            default:
185                                break;
186                            }
187                            return ret;
188                        }
189                    });
190                    window.showAtLocation(inputView, Gravity.NO_GRAVITY, x + offx, y + offy);
191                } catch (Exception e) {
192                }
193            }
194        }
195
196        void hide() {
197            if (window.isShowing()) {
198                textView.setOnTouchListener(null);
199                window.dismiss();
200            }
201        }
202
203        boolean isShowing() {
204            return window.isShowing();
205        }
206    }
207
208    /** Constructor */
209    public TutorialJAJP(OpenWnnJAJP ime, View inputView, DefaultSoftKeyboardJAJP inputManager) {
210        mInputManager = inputManager;
211        mInputView = inputView;
212        mIme = ime;
213
214        Context context = inputView.getContext();
215        int inputWidth = inputView.getWidth();
216        Resources r = inputView.getContext().getResources();
217        final int x = inputWidth / 20;
218        r.getDimensionPixelOffset(R.dimen.bubble_pointer_offset);
219
220        SpannableStringBuilder spannable = new SpannableStringBuilder();
221        Bubble button;
222
223        spannable.clear();
224        spannable.append(r.getText(R.string.tip_to_step1));
225
226        setSpan(spannable, "\u25cb", R.drawable.tutorial_12key_key);
227        button = new Bubble(context, inputView,
228                R.drawable.dialog_bubble, x, 0,
229                spannable, R.string.touch_to_continue, false);
230        mBubbles.add(button);
231
232        spannable.clear();
233        spannable.append(r.getText(R.string.tip_to_step2_a));
234
235        setSpan(spannable, "\u25cb", R.drawable.tutorial_12key_toggle);
236        button = new Bubble(context, inputView,
237                R.drawable.dialog_bubble, x, 0,
238                spannable, R.string.touch_to_continue, true);
239        mBubbles.add(button);
240
241        spannable.append(r.getText(R.string.tip_to_step2_b));
242
243        setSpan(spannable, "\u2192", R.drawable.tutorial_12key_right);
244
245        button = new Bubble(context, inputView,
246                R.drawable.dialog_bubble, x, 0,
247                spannable, R.string.touch_to_continue, true);
248        mBubbles.add(button);
249
250        spannable.append(r.getText(R.string.tip_to_step2_c));
251
252        setSpan(spannable, "\u25cb", R.drawable.tutorial_12key_toggle);
253
254        button = new Bubble(context, inputView,
255                R.drawable.dialog_bubble, x, 0,
256                spannable, R.string.touch_to_continue, true);
257        mBubbles.add(button);
258
259        spannable.append(r.getText(R.string.tip_to_step2_d));
260
261        setSpan(spannable, "\u25a0", R.drawable.tutorial_12key_space_jp);
262
263        setSpan(spannable, "\u2193", R.drawable.tutorial_12key_enter);
264
265        button = new Bubble(context, inputView,
266                R.drawable.dialog_bubble, x, 0,
267                spannable, R.string.touch_to_continue, true);
268        mBubbles.add(button);
269
270        spannable.clear();
271        spannable.append(r.getText(R.string.tip_to_step3_a));
272
273        setSpan(spannable, "\u25a0", R.drawable.tutorial_12key_mode);
274        button = new Bubble(context, inputView,
275                R.drawable.dialog_bubble_moji, x, 0,
276                spannable, R.string.touch_to_continue, false);
277        mBubbles.add(button);
278
279        button = new Bubble(context, inputView,
280                R.drawable.dialog_bubble_moji, x, 0,
281                R.string.tip_to_step3_b, R.string.touch_to_continue);
282        mBubbles.add(button);
283
284        button = new Bubble(context, inputView,
285                R.drawable.dialog_bubble_moji, x, 0,
286                R.string.tip_to_step3_c, R.string.touch_to_continue);
287        mBubbles.add(button);
288
289        spannable.clear();
290        spannable.append(r.getText(R.string.tip_to_step4));
291
292        setSpan(spannable, "\u25a0", R.drawable.tutorial_12key_mode);
293        button = new Bubble(context, inputView,
294                R.drawable.dialog_bubble_moji, x, 0,
295                spannable, R.string.touch_to_try, false);
296        mBubbles.add(button);
297
298        spannable.clear();
299        spannable.append(r.getText(R.string.tip_to_step5));
300
301        setSpan(spannable, "\u2190", R.drawable.tutorial_back);
302
303        button = new Bubble(context, inputView,
304                R.drawable.dialog_bubble, x, 0,
305                spannable, R.string.touch_to_continue, false);
306        mBubbles.add(button);
307
308        button = new Bubble(context, inputView,
309                R.drawable.dialog_bubble, x, 0,
310                R.string.tip_to_step6, R.string.touch_to_finish);
311        mBubbles.add(button);
312    }
313
314    private void setSpan(SpannableStringBuilder spannable, String marker, int imageResourceId) {
315        String text = spannable.toString();
316        int target = text.indexOf(marker);
317        while (0 <= target) {
318            ImageSpan span = new ImageSpan(mIme, imageResourceId,
319                    DynamicDrawableSpan.ALIGN_BOTTOM);
320            spannable.setSpan(span, target, target + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
321            target = text.indexOf(marker, target + 1);
322        }
323    }
324
325    public void start() {
326        mInputView.getLocationInWindow(mLocation);
327        mBubbleIndex = -1;
328        mInputView.setOnTouchListener(this);
329        next();
330    }
331
332    boolean next() {
333        if (mBubbleIndex >= 0) {
334            if (!mBubbles.get(mBubbleIndex).isShowing()) {
335                return true;
336            }
337            for (int i = 0; i <= mBubbleIndex; i++) {
338                mBubbles.get(i).hide();
339            }
340        }
341        mBubbleIndex++;
342        if (mBubbleIndex >= mBubbles.size()) {
343            mEnableKeyTouch = true;
344            mIme.sendDownUpKeyEvents(-1);
345            mIme.tutorialDone();
346            return false;
347        }
348
349        if ((6 <= mBubbleIndex) && (mBubbleIndex <= 8)) {
350            mInputManager.nextKeyMode();
351        }
352
353        if (mBubbleIndex == LONG_PRESS_INDEX) {
354            mEnableKeyTouch = true;
355        } else if (LONG_PRESS_INDEX < mBubbleIndex) {
356            mEnableKeyTouch = false;
357        }
358
359        mHandler.sendMessageDelayed(
360                mHandler.obtainMessage(MSG_SHOW_BUBBLE, mBubbles.get(mBubbleIndex)), 500);
361        return true;
362    }
363
364    void hide() {
365        for (int i = 0; i < mBubbles.size(); i++) {
366            mBubbles.get(i).hide();
367        }
368        mInputView.setOnTouchListener(null);
369    }
370
371    public boolean close() {
372        mHandler.removeMessages(MSG_SHOW_BUBBLE);
373        hide();
374        return true;
375    }
376
377    public boolean onTouch(View v, MotionEvent event) {
378        boolean ret = !mEnableKeyTouch;
379        if (event.getAction() == MotionEvent.ACTION_UP) {
380            if (mBubbleIndex >= mBubbles.size()) {
381                mInputView.setOnTouchListener(null);
382            } else {
383                if (mBubbleIndex != LONG_PRESS_INDEX) {
384                    next();
385                }
386            }
387        }
388        return ret;
389    }
390}
391