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
33package jp.co.omronsoft.openwnn.EN;
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 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
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
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
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
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
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
193        void hide() {
194            if (window.isShowing()) {
195                textView.setOnTouchListener(null);
196                window.dismiss();
197            }
198        }
199
200        boolean isShowing() {
201            return window.isShowing();
202        }
203    }
204
205    /** Constructor */
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
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
253    public void start() {
254        mInputView.getLocationInWindow(mLocation);
255        mBubbleIndex = -1;
256        mInputView.setOnTouchListener(this);
257        next();
258    }
259
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
282    void hide() {
283        for (int i = 0; i < mBubbles.size(); i++) {
284            mBubbles.get(i).hide();
285        }
286        mInputView.setOnTouchListener(null);
287    }
288
289    public boolean close() {
290        mHandler.removeMessages(MSG_SHOW_BUBBLE);
291        hide();
292        return true;
293    }
294
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