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.AlertDialog; 21 import android.app.Dialog; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.os.Handler; 25 import android.util.Log; 26 import android.view.LayoutInflater; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.widget.ArrayAdapter; 30 import android.widget.AdapterView; 31 import android.widget.AdapterView.OnItemClickListener; 32 import android.widget.BaseAdapter; 33 import android.widget.Button; 34 import android.widget.CheckBox; 35 import android.widget.CompoundButton; 36 import android.widget.ImageView; 37 import android.widget.ListAdapter; 38 import android.widget.ListView; 39 import android.widget.RadioButton; 40 import android.widget.TextView; 41 42 import com.android.browser.GearsPermissions.OriginPermissions; 43 import com.android.browser.GearsPermissions.Permission; 44 import com.android.browser.GearsPermissions.PermissionsChangesListener; 45 import com.android.browser.GearsPermissions.PermissionType; 46 47 import java.util.Vector; 48 import java.util.List; 49 50 import org.json.JSONArray; 51 import org.json.JSONException; 52 import org.json.JSONObject; 53 54 /** 55 * Gears Settings dialog 56 */ 57 class GearsSettingsDialog extends GearsBaseDialog 58 implements PermissionsChangesListener { 59 60 private static final String TAG = "GearsPermissionsDialog"; 61 private Vector<OriginPermissions> mSitesPermissions = null; 62 private Vector<OriginPermissions> mOriginalPermissions = null; 63 private Vector<OriginPermissions> mCurrentPermissions = null; 64 65 private Vector<PermissionType> mPermissions; 66 private static final int CONFIRMATION_REMOVE_DIALOG = 1; 67 68 // We declare the permissions globally to simplify the code 69 private final PermissionType LOCAL_STORAGE = 70 new PermissionType(LOCAL_STORAGE_STRING); 71 private final PermissionType LOCATION_DATA = 72 new PermissionType(LOCATION_DATA_STRING); 73 74 private boolean mChanges = false; 75 76 SettingsAdapter mListAdapter; 77 GearsSettingsDialog(Activity activity, Handler handler, String arguments)78 public GearsSettingsDialog(Activity activity, 79 Handler handler, 80 String arguments) { 81 super (activity, handler, arguments); 82 activity.setContentView(R.layout.gears_settings); 83 } 84 setup()85 public void setup() { 86 // First let's add the permissions' resources 87 LOCAL_STORAGE.setResources(R.string.settings_storage_title, 88 R.string.settings_storage_subtitle_on, 89 R.string.settings_storage_subtitle_off); 90 LOCATION_DATA.setResources(R.string.settings_location_title, 91 R.string.settings_location_subtitle_on, 92 R.string.settings_location_subtitle_off); 93 // add the permissions to the list of permissions. 94 mPermissions = new Vector<PermissionType>(); 95 mPermissions.add(LOCAL_STORAGE); 96 mPermissions.add(LOCATION_DATA); 97 OriginPermissions.setListener(this); 98 99 100 setupDialog(); 101 102 // We manage the permissions using three vectors, mSitesPermissions, 103 // mOriginalPermissions and mCurrentPermissions. 104 // The dialog's arguments are parsed and a list of permissions is 105 // generated and stored in those three vectors. 106 // mOriginalPermissions is a separate copy and will not be modified; 107 // mSitesPermissions contains the current permissions _only_ -- 108 // if an origin is removed, it is also removed from mSitesPermissions. 109 // Finally, mCurrentPermissions contains the current permissions and 110 // is a clone of mSitesPermissions, but removed sites aren't removed, 111 // their permissions are simply set to PERMISSION_NOT_SET. This 112 // allows us to easily generate the final difference between the 113 // original permissions and the final permissions, while directly 114 // using mSitesPermissions for the listView adapter (SettingsAdapter). 115 116 mSitesPermissions = new Vector<OriginPermissions>(); 117 mOriginalPermissions = new Vector<OriginPermissions>(); 118 119 try { 120 JSONObject json = new JSONObject(mDialogArguments); 121 if (json.has("permissions")) { 122 JSONArray jsonArray = json.getJSONArray("permissions"); 123 for (int i = 0; i < jsonArray.length(); i++) { 124 JSONObject infos = jsonArray.getJSONObject(i); 125 String name = null; 126 int localStorage = PermissionType.PERMISSION_NOT_SET; 127 int locationData = PermissionType.PERMISSION_NOT_SET; 128 if (infos.has("name")) { 129 name = infos.getString("name"); 130 } 131 if (infos.has(LOCAL_STORAGE_STRING)) { 132 JSONObject perm = infos.getJSONObject(LOCAL_STORAGE_STRING); 133 if (perm.has("permissionState")) { 134 localStorage = perm.getInt("permissionState"); 135 } 136 } 137 if (infos.has(LOCATION_DATA_STRING)) { 138 JSONObject perm = infos.getJSONObject(LOCATION_DATA_STRING); 139 if (perm.has("permissionState")) { 140 locationData = perm.getInt("permissionState"); 141 } 142 } 143 OriginPermissions perms = new OriginPermissions(name); 144 perms.setPermission(LOCAL_STORAGE, localStorage); 145 perms.setPermission(LOCATION_DATA, locationData); 146 147 mSitesPermissions.add(perms); 148 mOriginalPermissions.add(new OriginPermissions(perms)); 149 } 150 } 151 } catch (JSONException e) { 152 Log.e(TAG, "JSON exception ", e); 153 } 154 mCurrentPermissions = (Vector<OriginPermissions>)mSitesPermissions.clone(); 155 156 View listView = findViewById(R.id.sites_list); 157 if (listView != null) { 158 ListView list = (ListView) listView; 159 mListAdapter = new SettingsAdapter(mActivity, mSitesPermissions); 160 list.setAdapter(mListAdapter); 161 list.setScrollBarStyle(android.view.View.SCROLLBARS_OUTSIDE_INSET); 162 list.setOnItemClickListener(mListAdapter); 163 } 164 if (mDebug) { 165 printPermissions(); 166 } 167 } 168 setMainTitle()169 private void setMainTitle() { 170 String windowTitle = mActivity.getString(R.string.pref_extras_gears_settings); 171 mActivity.setTitle(windowTitle); 172 } 173 setupDialog()174 public void setupDialog() { 175 setMainTitle(); 176 } 177 178 /** 179 * GearsPermissions.PermissionsChangesListener delegate 180 */ setPermission(PermissionType type, int perm)181 public boolean setPermission(PermissionType type, int perm) { 182 if (mChanges == false) { 183 mChanges = true; 184 } 185 return mChanges; 186 } 187 handleBackButton()188 public boolean handleBackButton() { 189 return mListAdapter.backButtonPressed(); 190 } 191 192 /** 193 * We use this to create a confirmation dialog when the user 194 * clicks on "remove this site from gears" 195 */ onCreateDialog(int id)196 public Dialog onCreateDialog(int id) { 197 return new AlertDialog.Builder(mActivity) 198 .setTitle(R.string.settings_confirmation_remove_title) 199 .setMessage(R.string.settings_confirmation_remove) 200 .setPositiveButton(android.R.string.ok, 201 new AlertDialog.OnClickListener() { 202 public void onClick(DialogInterface dlg, int which) { 203 mListAdapter.removeCurrentSite(); 204 } 205 }) 206 .setNegativeButton(android.R.string.cancel, null) 207 .setIcon(android.R.drawable.ic_dialog_alert) 208 .create(); 209 } 210 211 /** 212 * Adapter class for the list view in the settings dialog 213 * 214 * We first display a list of all the origins (sites), or 215 * a message saying that no permission is set if the list is empty. 216 * When the user click on one of the origin, we then display 217 * the list of the permissions existing for that origin. 218 * Each permission can be either allowed or denied by clicking 219 * on the checkbox. 220 * The last row is a special case, allowing to remove the entire origin. 221 */ 222 class SettingsAdapter extends BaseAdapter 223 implements AdapterView.OnItemClickListener { 224 private Activity mContext; 225 private List mItems; 226 private OriginPermissions mCurrentSite; 227 private Vector mCurrentPermissions; 228 private int MAX_ROW_HEIGHT = 64; 229 230 SettingsAdapter(Activity context, List items) { 231 mContext = context; 232 mItems = items; 233 mCurrentSite = null; 234 } 235 236 public int getCount() { 237 if (mCurrentSite == null) { 238 int size = mItems.size(); 239 if (size == 0) { 240 return 1; 241 } else { 242 return size; 243 } 244 } 245 return mCurrentPermissions.size() + 1; 246 } 247 248 public long getItemId(int position) { 249 return position; 250 } 251 252 private String shortName(String url) { 253 // We remove the http and https prefix 254 if (url.startsWith("http://")) { 255 return url.substring(7); 256 } 257 if (url.startsWith("https://")) { 258 return url.substring(8); 259 } 260 return url; 261 } 262 263 public Object getItem(int position) { 264 if (mCurrentSite == null) { 265 if (mItems.size() == 0) { 266 return null; 267 } else { 268 return mItems.get(position); 269 } 270 } 271 return mCurrentPermissions.get(position); 272 } 273 274 public View getView(int position, View convertView, ViewGroup parent) { 275 View row = convertView; 276 if (row == null) { // no cached view, we create one 277 LayoutInflater inflater = (LayoutInflater) getSystemService( 278 Context.LAYOUT_INFLATER_SERVICE); 279 row = inflater.inflate(R.layout.gears_settings_row, null); 280 } 281 row.setMinimumHeight(MAX_ROW_HEIGHT); 282 283 if (mCurrentSite == null) { 284 if (mItems.size() == 0) { 285 hideView(row, R.id.title); 286 hideView(row, R.id.subtitle); 287 hideView(row, R.id.checkbox); 288 hideView(row, R.id.icon); 289 setText(row, R.id.info, R.string.settings_empty); 290 } else { 291 hideView(row, R.id.subtitle); 292 hideView(row, R.id.info); 293 hideView(row, R.id.checkbox); 294 OriginPermissions perms = (OriginPermissions) mItems.get(position); 295 setText(row, R.id.title, shortName(perms.getOrigin())); 296 showView(row, R.id.icon); 297 } 298 } else { 299 if (position == getCount() - 1) { 300 // last position: "remove this site from gears" 301 hideView(row, R.id.subtitle); 302 hideView(row, R.id.info); 303 hideView(row, R.id.checkbox); 304 hideView(row, R.id.icon); 305 setText(row, R.id.title, R.string.settings_remove_site); 306 } else { 307 hideView(row, R.id.info); 308 hideView(row, R.id.icon); 309 showView(row, R.id.checkbox); 310 311 PermissionType type = 312 (PermissionType) mCurrentPermissions.get(position); 313 setText(row, R.id.title, type.getTitleRsc()); 314 315 View checkboxView = row.findViewById(R.id.checkbox); 316 if (checkboxView != null) { 317 CheckBox checkbox = (CheckBox) checkboxView; 318 int perm = mCurrentSite.getPermission(type); 319 if (perm == PermissionType.PERMISSION_DENIED) { 320 setText(row, R.id.subtitle, type.getSubtitleOffRsc()); 321 checkbox.setChecked(false); 322 } else { 323 setText(row, R.id.subtitle, type.getSubtitleOnRsc()); 324 checkbox.setChecked(true); 325 } 326 } 327 } 328 } 329 return row; 330 } 331 332 public void removeCurrentSite() { 333 mCurrentSite.setPermission(LOCAL_STORAGE, 334 PermissionType.PERMISSION_NOT_SET); 335 mCurrentSite.setPermission(LOCATION_DATA, 336 PermissionType.PERMISSION_NOT_SET); 337 mSitesPermissions.remove(mCurrentSite); 338 mCurrentSite = null; 339 setMainTitle(); 340 notifyDataSetChanged(); 341 } 342 343 public void onItemClick(AdapterView<?> parent, 344 View view, 345 int position, 346 long id) { 347 if (mItems.size() == 0) { 348 return; 349 } 350 if (mCurrentSite == null) { 351 mCurrentSite = (OriginPermissions) mItems.get(position); 352 mCurrentPermissions = new Vector(); 353 for (int i = 0; i < mPermissions.size(); i++) { 354 PermissionType type = mPermissions.get(i); 355 int perm = mCurrentSite.getPermission(type); 356 if (perm != PermissionType.PERMISSION_NOT_SET) { 357 mCurrentPermissions.add(type); 358 } 359 } 360 mContext.setTitle(shortName(mCurrentSite.getOrigin())); 361 } else { 362 if (position == getCount() - 1) { // last item (remove site) 363 // Ask the user to confirm 364 // If yes, removeCurrentSite() will be called via the dialog callback. 365 mActivity.showDialog(CONFIRMATION_REMOVE_DIALOG); 366 } else { 367 PermissionType type = 368 (PermissionType) mCurrentPermissions.get(position); 369 if (mCurrentSite.getPermission(type) == 370 PermissionType.PERMISSION_ALLOWED) { 371 mCurrentSite.setPermission(type, PermissionType.PERMISSION_DENIED); 372 } else { 373 mCurrentSite.setPermission(type, PermissionType.PERMISSION_ALLOWED); 374 } 375 } 376 } 377 notifyDataSetChanged(); 378 } 379 380 public boolean backButtonPressed() { 381 if (mCurrentSite != null) { // we intercept the back button 382 mCurrentSite = null; 383 setMainTitle(); 384 notifyDataSetChanged(); 385 return true; 386 } 387 return false; 388 } 389 390 } 391 392 /** 393 * Utility method used in debug mode to print the list of 394 * permissions (original values and current values). 395 */ 396 public void printPermissions() { 397 Log.v(TAG, "Original Permissions: "); 398 for (int i = 0; i < mOriginalPermissions.size(); i++) { 399 OriginPermissions p = mOriginalPermissions.get(i); 400 p.print(); 401 } 402 Log.v(TAG, "Current Permissions: "); 403 for (int i = 0; i < mSitesPermissions.size(); i++) { 404 OriginPermissions p = mSitesPermissions.get(i); 405 p.print(); 406 } 407 } 408 409 /** 410 * Computes the difference between the original permissions and the 411 * current ones. Returns a json-formatted string. 412 * It is used by the Settings dialog. 413 */ 414 public String computeDiff(boolean modif) { 415 String ret = null; 416 try { 417 JSONObject results = new JSONObject(); 418 JSONArray permissions = new JSONArray(); 419 420 for (int i = 0; modif && i < mOriginalPermissions.size(); i++) { 421 OriginPermissions original = mOriginalPermissions.get(i); 422 OriginPermissions current = mCurrentPermissions.get(i); 423 JSONObject permission = new JSONObject(); 424 boolean modifications = false; 425 426 for (int j = 0; j < mPermissions.size(); j++) { 427 PermissionType type = mPermissions.get(j); 428 429 if (current.getPermission(type) != original.getPermission(type)) { 430 JSONObject state = new JSONObject(); 431 state.put("permissionState", current.getPermission(type)); 432 permission.put(type.getName(), state); 433 modifications = true; 434 } 435 } 436 437 if (modifications) { 438 permission.put("name", current.getOrigin()); 439 permissions.put(permission); 440 } 441 } 442 results.put("modifiedOrigins", permissions); 443 ret = results.toString(); 444 } catch (JSONException e) { 445 Log.e(TAG, "JSON exception ", e); 446 } 447 return ret; 448 } 449 450 public String closeDialog(int closingType) { 451 String ret = computeDiff(mChanges); 452 453 if (mDebug) { 454 printPermissions(); 455 } 456 457 return ret; 458 } 459 460 } 461