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
17package com.android.inputmethod.latin;
18
19import android.content.Context;
20import android.graphics.Rect;
21import android.graphics.drawable.Drawable;
22import android.os.Handler;
23import android.os.Message;
24import android.text.Layout;
25import android.text.SpannableStringBuilder;
26import android.text.StaticLayout;
27import android.view.Gravity;
28import android.view.LayoutInflater;
29import android.view.MotionEvent;
30import android.view.View;
31import android.view.View.OnTouchListener;
32import android.widget.PopupWindow;
33import android.widget.TextView;
34
35import java.util.ArrayList;
36import java.util.List;
37
38public 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
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
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
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
153        void hide() {
154            if (window.isShowing()) {
155                textView.setOnTouchListener(null);
156                window.dismiss();
157            }
158        }
159
160        boolean isShowing() {
161            return window.isShowing();
162        }
163    }
164
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
199    void start() {
200        mInputView.getLocationInWindow(mLocation);
201        mBubbleIndex = -1;
202        mInputView.setOnTouchListener(this);
203        next();
204    }
205
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
232    void hide() {
233        for (int i = 0; i < mBubbles.size(); i++) {
234            mBubbles.get(i).hide();
235        }
236        mInputView.setOnTouchListener(null);
237    }
238
239    boolean close() {
240        mHandler.removeMessages(MSG_SHOW_BUBBLE);
241        hide();
242        return true;
243    }
244
245    public boolean onTouch(View v, MotionEvent event) {
246        if (event.getAction() == MotionEvent.ACTION_DOWN) {
247            next();
248        }
249        return true;
250    }
251}
252