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 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