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