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 33 package jp.co.omronsoft.openwnn.JAJP; 34 35 import jp.co.omronsoft.openwnn.*; 36 import android.content.Context; 37 import android.content.res.Resources; 38 import android.graphics.drawable.Drawable; 39 import android.os.Handler; 40 import android.os.Message; 41 import android.text.Layout; 42 import android.text.SpannableStringBuilder; 43 import android.text.StaticLayout; 44 import android.text.Spanned; 45 import android.text.style.ImageSpan; 46 import android.text.style.DynamicDrawableSpan; 47 import android.view.Gravity; 48 import android.view.LayoutInflater; 49 import android.view.MotionEvent; 50 import android.view.View; 51 import android.view.View.OnTouchListener; 52 import android.widget.PopupWindow; 53 import android.widget.TextView; 54 55 import java.util.ArrayList; 56 import java.util.List; 57 58 public 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 Bubble(Context context, View inputView, int backgroundResource, int bx, int by, int description, int guide)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 Bubble(Context context, View inputView, int backgroundResource, int bx, int by, CharSequence description, int guide, boolean leftAlign)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 init(Context context, View inputView, int backgroundResource, int bx, int by, CharSequence description, int guide, boolean leftAlign)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 chooseSize(PopupWindow pop, View parentView, CharSequence text, TextView tv)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 show(int offx, int offy)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 hide()196 void hide() { 197 if (window.isShowing()) { 198 textView.setOnTouchListener(null); 199 window.dismiss(); 200 } 201 } 202 isShowing()203 boolean isShowing() { 204 return window.isShowing(); 205 } 206 } 207 208 /** Constructor */ TutorialJAJP(OpenWnnJAJP ime, View inputView, DefaultSoftKeyboardJAJP inputManager)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 setSpan(SpannableStringBuilder spannable, String marker, int imageResourceId)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 start()325 public void start() { 326 mInputView.getLocationInWindow(mLocation); 327 mBubbleIndex = -1; 328 mInputView.setOnTouchListener(this); 329 next(); 330 } 331 next()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 hide()364 void hide() { 365 for (int i = 0; i < mBubbles.size(); i++) { 366 mBubbles.get(i).hide(); 367 } 368 mInputView.setOnTouchListener(null); 369 } 370 close()371 public boolean close() { 372 mHandler.removeMessages(MSG_SHOW_BUBBLE); 373 hide(); 374 return true; 375 } 376 onTouch(View v, MotionEvent event)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