1 /* 2 * Copyright (C) 2008,2009 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 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 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 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 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 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 193 void hide() { 194 if (window.isShowing()) { 195 textView.setOnTouchListener(null); 196 window.dismiss(); 197 } 198 } 199 200 boolean isShowing() { 201 return window.isShowing(); 202 } 203 } 204 205 /** Constructor */ 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 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 253 public void start() { 254 mInputView.getLocationInWindow(mLocation); 255 mBubbleIndex = -1; 256 mInputView.setOnTouchListener(this); 257 next(); 258 } 259 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 282 void hide() { 283 for (int i = 0; i < mBubbles.size(); i++) { 284 mBubbles.get(i).hide(); 285 } 286 mInputView.setOnTouchListener(null); 287 } 288 289 public boolean close() { 290 mHandler.removeMessages(MSG_SHOW_BUBBLE); 291 hide(); 292 return true; 293 } 294 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