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.EN; 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 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 Bubble(Context context, View inputView, int backgroundResource, int bx, int by, int description, int guide)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 Bubble(Context context, View inputView, int backgroundResource, int bx, int by, CharSequence description, int guide, boolean leftAlign)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 init(Context context, View inputView, int backgroundResource, int bx, int by, CharSequence description, int guide, boolean leftAlign)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 chooseSize(PopupWindow pop, View parentView, CharSequence text, TextView tv)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 show(int offx, int offy)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 hide()193 void hide() { 194 if (window.isShowing()) { 195 textView.setOnTouchListener(null); 196 window.dismiss(); 197 } 198 } 199 isShowing()200 boolean isShowing() { 201 return window.isShowing(); 202 } 203 } 204 205 /** Constructor */ TutorialEN(OpenWnnEN ime, View inputView, DefaultSoftKeyboardEN inputManager)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 setSpan(SpannableStringBuilder spannable, String marker, int imageResourceId)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 start()253 public void start() { 254 mInputView.getLocationInWindow(mLocation); 255 mBubbleIndex = -1; 256 mInputView.setOnTouchListener(this); 257 next(); 258 } 259 next()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 hide()282 void hide() { 283 for (int i = 0; i < mBubbles.size(); i++) { 284 mBubbles.get(i).hide(); 285 } 286 mInputView.setOnTouchListener(null); 287 } 288 close()289 public boolean close() { 290 mHandler.removeMessages(MSG_SHOW_BUBBLE); 291 hide(); 292 return true; 293 } 294 onTouch(View v, MotionEvent event)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