2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.browser;
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;
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;
47 import java.util.Vector;
48 import java.util.List;
50 import org.json.JSONArray;
51 import org.json.JSONException;
52 import org.json.JSONObject;
55 * Gears Settings dialog
57 class GearsSettingsDialog extends GearsBaseDialog
58 implements PermissionsChangesListener {
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;
65 private Vector<PermissionType> mPermissions;
66 private static final int CONFIRMATION_REMOVE_DIALOG = 1;
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);
74 private boolean mChanges = false;
76 SettingsAdapter mListAdapter;
78 public GearsSettingsDialog(Activity activity,
81 super (activity, handler, arguments);
82 activity.setContentView(R.layout.gears_settings);
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);
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).
116 mSitesPermissions = new Vector<OriginPermissions>();
117 mOriginalPermissions = new Vector<OriginPermissions>();
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);
126 int localStorage = PermissionType.PERMISSION_NOT_SET;
127 int locationData = PermissionType.PERMISSION_NOT_SET;
128 if (infos.has("name")) {
129 name = infos.getString("name");
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");
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");
143 OriginPermissions perms = new OriginPermissions(name);
144 perms.setPermission(LOCAL_STORAGE, localStorage);
145 perms.setPermission(LOCATION_DATA, locationData);
147 mSitesPermissions.add(perms);
148 mOriginalPermissions.add(new OriginPermissions(perms));
151 } catch (JSONException e) {
152 Log.e(TAG, "JSON exception ", e);
154 mCurrentPermissions = (Vector<OriginPermissions>)mSitesPermissions.clone();
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);
169 private void setMainTitle() {
170 String windowTitle = mActivity.getString(R.string.pref_extras_gears_settings);
171 mActivity.setTitle(windowTitle);
174 public void setupDialog() {
179 * GearsPermissions.PermissionsChangesListener delegate
181 public boolean setPermission(PermissionType type, int perm) {
182 if (mChanges == false) {
188 public boolean handleBackButton() {
189 return mListAdapter.backButtonPressed();
193 * We use this to create a confirmation dialog when the user
194 * clicks on "remove this site from gears"
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();
206 .setNegativeButton(android.R.string.cancel, null)
207 .setIcon(android.R.drawable.ic_dialog_alert)
212 * Adapter class for the list view in the settings dialog
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
220 * The last row is a special case, allowing to remove the entire origin.
222 class SettingsAdapter extends BaseAdapter
223 implements AdapterView.OnItemClickListener {
224 private Activity mContext;
226 private OriginPermissions mCurrentSite;
227 private Vector mCurrentPermissions;
228 private int MAX_ROW_HEIGHT = 64;
230 SettingsAdapter(Activity context, List items) {
236 public int getCount() {
237 if (mCurrentSite == null) {
238 int size = mItems.size();
245 return mCurrentPermissions.size() + 1;
248 public long getItemId(int position) {
252 private String shortName(String url) {
253 // We remove the http and https prefix
254 if (url.startsWith("http://")) {
255 return url.substring(7);
257 if (url.startsWith("https://")) {
258 return url.substring(8);
263 public Object getItem(int position) {
264 if (mCurrentSite == null) {
265 if (mItems.size() == 0) {
268 return mItems.get(position);
271 return mCurrentPermissions.get(position);
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);
281 row.setMinimumHeight(MAX_ROW_HEIGHT);
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);
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);
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);
307 hideView(row, R.id.info);
308 hideView(row, R.id.icon);
309 showView(row, R.id.checkbox);
311 PermissionType type =
312 (PermissionType) mCurrentPermissions.get(position);
313 setText(row, R.id.title, type.getTitleRsc());
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);
323 setText(row, R.id.subtitle, type.getSubtitleOnRsc());
324 checkbox.setChecked(true);
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);
340 notifyDataSetChanged();
343 public void onItemClick(AdapterView<?> parent,
347 if (mItems.size() == 0) {
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);
360 mContext.setTitle(shortName(mCurrentSite.getOrigin()));
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);
367 PermissionType type =
368 (PermissionType) mCurrentPermissions.get(position);
369 if (mCurrentSite.getPermission(type) ==
370 PermissionType.PERMISSION_ALLOWED) {
371 mCurrentSite.setPermission(type, PermissionType.PERMISSION_DENIED);
373 mCurrentSite.setPermission(type, PermissionType.PERMISSION_ALLOWED);
377 notifyDataSetChanged();
380 public boolean backButtonPressed() {
381 if (mCurrentSite != null) { // we intercept the back button
384 notifyDataSetChanged();
393 * Utility method used in debug mode to print the list of
394 * permissions (original values and current values).
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);
402 Log.v(TAG, "Current Permissions: ");
403 for (int i = 0; i < mSitesPermissions.size(); i++) {
404 OriginPermissions p = mSitesPermissions.get(i);
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.
414 public String computeDiff(boolean modif) {
417 JSONObject results = new JSONObject();
418 JSONArray permissions = new JSONArray();
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;
426 for (int j = 0; j < mPermissions.size(); j++) {
427 PermissionType type = mPermissions.get(j);
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;
438 permission.put("name", current.getOrigin());
439 permissions.put(permission);
442 results.put("modifiedOrigins", permissions);
443 ret = results.toString();
444 } catch (JSONException e) {
445 Log.e(TAG, "JSON exception ", e);
450 public String closeDialog(int closingType) {
451 String ret = computeDiff(mChanges);