1 /* 2 * Copyright (C) 2008 The Android Open Source Project 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 package com.android.browser; 18 19 import android.app.Activity; 20 import android.app.Dialog; 21 import android.content.Context; 22 import android.content.res.Resources; 23 import android.graphics.Bitmap; 24 import android.graphics.BitmapFactory; 25 import android.os.Handler; 26 import android.text.Spannable; 27 import android.text.SpannableString; 28 import android.text.style.UnderlineSpan; 29 import android.util.Log; 30 import android.view.InflateException; 31 import android.view.LayoutInflater; 32 import android.view.View; 33 import android.view.ViewGroup; 34 import android.widget.Button; 35 import android.widget.ImageView; 36 import android.widget.TextView; 37 38 import java.io.InputStream; 39 import java.io.IOException; 40 import java.lang.ClassCastException; 41 import java.net.HttpURLConnection; 42 import java.net.MalformedURLException; 43 import java.net.URL; 44 45 import org.json.JSONException; 46 import org.json.JSONObject; 47 48 /** 49 * Base dialog class for gears 50 */ 51 class GearsBaseDialog { 52 53 private static final String TAG = "GearsNativeDialog"; 54 protected Handler mHandler; 55 protected Activity mActivity; 56 protected String mDialogArguments; 57 58 private Bitmap mIcon; 59 private final int MAX_ICON_SIZE = 64; 60 protected int mChoosenIconSize; 61 62 // Dialog closing types 63 public static final int CANCEL = 0; 64 public static final int ALWAYS_DENY = 1; 65 public static final int ALLOW = 2; 66 public static final int DENY = 3; 67 public static final int NEW_ICON = 4; 68 public static final int UPDATE_ICON = 5; 69 public static final int REQUEST_ICON = 6; 70 public static final int PAUSE_REQUEST_ICON = 7; 71 public static final int CLEAR_REQUEST_ICON = 8; 72 73 protected final String LOCAL_DATA_STRING = "localData"; 74 protected final String LOCAL_STORAGE_STRING = "localStorage"; 75 protected final String LOCATION_DATA_STRING = "locationData"; 76 77 protected String mGearsVersion = "UNDEFINED"; 78 protected boolean mDebug = false; 79 GearsBaseDialog(Activity activity, Handler handler, String arguments)80 public GearsBaseDialog(Activity activity, Handler handler, String arguments) { 81 mActivity = activity; 82 mHandler = handler; 83 mDialogArguments = arguments; 84 } 85 getResources()86 Resources getResources() { 87 return mActivity.getResources(); 88 } 89 getSystemService(String name)90 Object getSystemService(String name) { 91 return mActivity.getSystemService(name); 92 } 93 findViewById(int id)94 View findViewById(int id) { 95 return mActivity.findViewById(id); 96 } 97 getString(int id)98 private String getString(int id) { 99 return mActivity.getString(id); 100 } 101 setDebug(boolean debug)102 public void setDebug(boolean debug) { 103 mDebug = debug; 104 } 105 setGearsVersion(String version)106 public void setGearsVersion(String version) { 107 mGearsVersion = version; 108 } 109 closeDialog(int closingType)110 public String closeDialog(int closingType) { 111 return null; 112 } 113 114 /* 115 * Utility methods for setting up the dialogs elements 116 */ 117 118 /** 119 * Inflate a given layout in a view (which has to be 120 * a ViewGroup, e.g. LinearLayout). 121 * This is used to share the basic dialog outline among 122 * the different dialog types. 123 */ inflate(int layout, int viewID)124 void inflate(int layout, int viewID) { 125 LayoutInflater inflater = (LayoutInflater) getSystemService( 126 Context.LAYOUT_INFLATER_SERVICE); 127 View view = findViewById(viewID); 128 if (view != null) { 129 try { 130 ViewGroup viewGroup = (ViewGroup) view; 131 inflater.inflate(layout, viewGroup); 132 } catch (ClassCastException e) { 133 String msg = "exception, the view (" + view + ")"; 134 msg += " is not a ViewGroup"; 135 Log.e(TAG, msg, e); 136 } catch (InflateException e) { 137 Log.e(TAG, "exception while inflating the layout", e); 138 } 139 } else { 140 String msg = "problem, trying to inflate a non-existent view"; 141 msg += " (" + viewID + ")"; 142 Log.e(TAG, msg); 143 } 144 } 145 146 /** 147 * Button setup. 148 * Set the button's text and its listener. If the text resource's id 149 * is 0, makes the button invisible. 150 */ setupButton(int buttonRscID, int rscString, View.OnClickListener listener, boolean isLink, boolean requestFocus)151 void setupButton(int buttonRscID, 152 int rscString, 153 View.OnClickListener listener, 154 boolean isLink, 155 boolean requestFocus) { 156 View view = findViewById(buttonRscID); 157 if (view == null) { 158 return; 159 } 160 161 Button button = (Button) view; 162 163 if (rscString == 0) { 164 button.setVisibility(View.GONE); 165 } else { 166 CharSequence text = getString(rscString); 167 button.setText(text); 168 button.setOnClickListener(listener); 169 if (isLink) { 170 displayAsLink(button); 171 } 172 if (requestFocus) { 173 button.requestFocus(); 174 } 175 } 176 } 177 178 /** 179 * Button setup: as the above method, except that 'isLink' and 180 * 'requestFocus' default to false. 181 */ setupButton(int buttonRsc, int rsc, View.OnClickListener listener)182 void setupButton(int buttonRsc, int rsc, 183 View.OnClickListener listener) { 184 setupButton(buttonRsc, rsc, listener, false, false); 185 } 186 187 /** 188 * Utility method to setup the three dialog buttons. 189 */ setupButtons(int alwaysDenyRsc, int allowRsc, int denyRsc)190 void setupButtons(int alwaysDenyRsc, int allowRsc, int denyRsc) { 191 setupButton(R.id.button_alwaysdeny, alwaysDenyRsc, 192 new Button.OnClickListener() { 193 public void onClick(View v) { 194 mHandler.sendEmptyMessage(ALWAYS_DENY); 195 } 196 }); 197 198 setupButton(R.id.button_allow, allowRsc, 199 new Button.OnClickListener() { 200 public void onClick(View v) { 201 mHandler.sendEmptyMessage(ALLOW); 202 } 203 }); 204 205 setupButton(R.id.button_deny, denyRsc, 206 new Button.OnClickListener() { 207 public void onClick(View v) { 208 mHandler.sendEmptyMessage(DENY); 209 } 210 }); 211 } 212 213 /** 214 * Display a button as an HTML link. Remove the background, set the 215 * text color to R.color.dialog_link and draw an underline 216 */ displayAsLink(Button button)217 void displayAsLink(Button button) { 218 if (button == null) { 219 return; 220 } 221 222 CharSequence text = button.getText(); 223 button.setBackgroundDrawable(null); 224 int color = getResources().getColor(R.color.dialog_link); 225 button.setTextColor(color); 226 SpannableString str = new SpannableString(text); 227 str.setSpan(new UnderlineSpan(), 0, str.length(), 228 Spannable.SPAN_INCLUSIVE_INCLUSIVE); 229 button.setText(str); 230 button.setFocusable(false); 231 } 232 233 /** 234 * Utility method to set elements' text indicated in 235 * the dialogs' arguments. 236 */ setLabel(JSONObject json, String name, int rsc)237 void setLabel(JSONObject json, String name, int rsc) { 238 try { 239 if (json.has(name)) { 240 String text = json.getString(name); 241 View view = findViewById(rsc); 242 if (view != null && text != null) { 243 TextView textView = (TextView) view; 244 textView.setText(text); 245 textView.setVisibility(View.VISIBLE); 246 } 247 } 248 } catch (JSONException e) { 249 Log.e(TAG, "json exception", e); 250 } 251 } 252 253 /** 254 * Utility method to hide a view. 255 */ hideView(View v, int rsc)256 void hideView(View v, int rsc) { 257 if (rsc == 0) { 258 return; 259 } 260 View view; 261 if (v == null) { 262 view = findViewById(rsc); 263 } else { 264 view = v.findViewById(rsc); 265 } 266 if (view != null) { 267 view.setVisibility(View.GONE); 268 } 269 } 270 271 /** 272 * Utility method to show a view. 273 */ showView(View v, int rsc)274 void showView(View v, int rsc) { 275 if (rsc == 0) { 276 return; 277 } 278 View view; 279 if (v == null) { 280 view = findViewById(rsc); 281 } else { 282 view = v.findViewById(rsc); 283 } 284 if (view != null) { 285 view.setVisibility(View.VISIBLE); 286 } 287 } 288 289 /** 290 * Utility method to set a text. 291 */ setText(View v, int rsc, CharSequence text)292 void setText(View v, int rsc, CharSequence text) { 293 if (rsc == 0) { 294 return; 295 } 296 View view = v.findViewById(rsc); 297 if (view != null) { 298 TextView textView = (TextView) view; 299 textView.setText(text); 300 textView.setVisibility(View.VISIBLE); 301 } 302 } 303 304 /** 305 * Utility method to set a text. 306 */ setText(View v, int rsc, int txtRsc)307 void setText(View v, int rsc, int txtRsc) { 308 if (rsc == 0) { 309 return; 310 } 311 View view = v.findViewById(rsc); 312 if (view != null) { 313 TextView textView = (TextView) view; 314 if (txtRsc == 0) { 315 textView.setVisibility(View.GONE); 316 } else { 317 CharSequence text = getString(txtRsc); 318 textView.setText(text); 319 textView.setVisibility(View.VISIBLE); 320 } 321 } 322 } 323 324 /** 325 * Utility class to download an icon in the background. 326 * Once done ask the UI thread to update the icon. 327 */ 328 class IconDownload implements Runnable { 329 private String mUrlString; 330 IconDownload(String url)331 IconDownload(String url) { 332 mUrlString = url; 333 } 334 run()335 public void run() { 336 if (mUrlString == null) { 337 return; 338 } 339 try { 340 URL url = new URL(mUrlString); 341 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 342 connection.setDoInput(true); 343 connection.connect(); 344 int length = connection.getContentLength(); 345 InputStream is = connection.getInputStream(); 346 Bitmap customIcon = BitmapFactory.decodeStream(is); 347 if (customIcon != null) { 348 mIcon = customIcon; 349 mHandler.sendEmptyMessage(UPDATE_ICON); 350 } 351 } catch (ClassCastException e) { 352 Log.e(TAG, "Class cast exception (" + mUrlString + ")", e); 353 } catch (MalformedURLException e) { 354 Log.e(TAG, "Malformed url (" + mUrlString + ") ", e); 355 } catch (IOException e) { 356 Log.e(TAG, "Exception downloading icon (" + mUrlString + ") ", e); 357 } 358 } 359 } 360 361 /** 362 * Utility method to update the icon. 363 * Called on the UI thread. 364 */ updateIcon()365 public void updateIcon() { 366 if (mIcon == null) { 367 return; 368 } 369 View view = findViewById(R.id.origin_icon); 370 if (view != null) { 371 ImageView imageView = (ImageView) view; 372 imageView.setMaxHeight(MAX_ICON_SIZE); 373 imageView.setMaxWidth(MAX_ICON_SIZE); 374 imageView.setScaleType(ImageView.ScaleType.FIT_XY); 375 imageView.setImageBitmap(mIcon); 376 imageView.setVisibility(View.VISIBLE); 377 } 378 } 379 380 /** 381 * Utility method to download an icon from a url and set 382 * it to the GUI element R.id.origin_icon. 383 * It is used both in the shortcut dialog and the 384 * permission dialog. 385 * The actual download is done in the background via 386 * IconDownload; once the icon is downlowded the UI is updated 387 * via updateIcon(). 388 * The icon size is included in the layout with the choosen 389 * size, although not displayed, to limit text reflow once 390 * the icon is received. 391 */ downloadIcon(String url)392 void downloadIcon(String url) { 393 if (url == null) { 394 return; 395 } 396 View view = findViewById(R.id.origin_icon); 397 if (view != null) { 398 view.setMinimumWidth(mChoosenIconSize); 399 view.setMinimumHeight(mChoosenIconSize); 400 view.setVisibility(View.INVISIBLE); 401 } 402 Thread thread = new Thread(new IconDownload(url)); 403 thread.start(); 404 } 405 406 /** 407 * Utility method that get the dialogMessage 408 * and icon and ask the setupDialog(message,icon) 409 * method to set the values. 410 */ setupDialog()411 public void setupDialog() { 412 TextView dialogMessage = null; 413 ImageView icon = null; 414 415 View view = findViewById(R.id.dialog_message); 416 if (view != null) { 417 dialogMessage = (TextView) view; 418 } 419 420 View iconView = findViewById(R.id.icon); 421 if (iconView != null) { 422 icon = (ImageView) iconView; 423 } 424 425 if ((dialogMessage != null) && (icon != null)) { 426 setupDialog(dialogMessage, icon); 427 dialogMessage.setVisibility(View.VISIBLE); 428 } 429 } 430 431 /* 432 * Set the message and icon of the dialog 433 */ setupDialog(TextView message, ImageView icon)434 public void setupDialog(TextView message, ImageView icon) { 435 message.setText(R.string.unrecognized_dialog_message); 436 icon.setImageResource(R.drawable.ic_dialog_menu_generic); 437 message.setVisibility(View.VISIBLE); 438 } 439 440 /** 441 * Setup the dialog 442 * By default, just display a simple message. 443 */ setup()444 public void setup() { 445 setupButtons(0, 0, R.string.default_button); 446 setupDialog(); 447 } 448 449 /** 450 * Method called when the back button is pressed, 451 * allowing the dialog to intercept the default behaviour. 452 */ handleBackButton()453 public boolean handleBackButton() { 454 return false; 455 } 456 457 /** 458 * Returns the resource string of the notification displayed 459 * after the dialog. By default, does not return one. 460 */ notification()461 public int notification() { 462 return 0; 463 } 464 465 /** 466 * If a secondary dialog (e.g. a confirmation dialog) is created, 467 * GearsNativeDialog will call this method. 468 */ onCreateDialog(int id)469 public Dialog onCreateDialog(int id) { 470 // This should be redefined by subclasses as needed. 471 return null; 472 } 473 474 } 475