Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(280)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java

Issue 620983002: Add Permissions to the PageInfo dialog on Android (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@page_info_dialog_shell_only_v2
Patch Set: Review feedback, UI feedback and moved Permission Settings strings into Android Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser; 5 package org.chromium.chrome.browser;
6 6
7 import android.app.Dialog; 7 import android.app.Dialog;
8 import android.content.Context; 8 import android.content.Context;
9 import android.content.DialogInterface; 9 import android.content.DialogInterface;
10 import android.graphics.Color; 10 import android.graphics.Color;
11 import android.graphics.drawable.ColorDrawable; 11 import android.graphics.drawable.ColorDrawable;
12 import android.text.Spannable; 12 import android.text.Spannable;
13 import android.text.SpannableStringBuilder; 13 import android.text.SpannableStringBuilder;
14 import android.text.style.ForegroundColorSpan; 14 import android.text.style.ForegroundColorSpan;
15 import android.text.style.StyleSpan;
16 import android.view.Gravity; 15 import android.view.Gravity;
17 import android.view.LayoutInflater; 16 import android.view.LayoutInflater;
17 import android.view.View;
18 import android.view.View.OnClickListener;
18 import android.view.ViewGroup; 19 import android.view.ViewGroup;
19 import android.view.Window; 20 import android.view.Window;
21 import android.widget.AdapterView;
22 import android.widget.AdapterView.OnItemSelectedListener;
23 import android.widget.ArrayAdapter;
24 import android.widget.Button;
25 import android.widget.ImageView;
20 import android.widget.LinearLayout; 26 import android.widget.LinearLayout;
21 import android.widget.ScrollView; 27 import android.widget.ScrollView;
28 import android.widget.Spinner;
22 import android.widget.TextView; 29 import android.widget.TextView;
23 30
24 import org.chromium.base.CalledByNative; 31 import org.chromium.base.CalledByNative;
25 import org.chromium.base.CommandLine; 32 import org.chromium.base.CommandLine;
26 import org.chromium.chrome.ChromeSwitches; 33 import org.chromium.chrome.ChromeSwitches;
27 import org.chromium.chrome.R; 34 import org.chromium.chrome.R;
28 import org.chromium.content.browser.WebContentsObserverAndroid; 35 import org.chromium.content.browser.WebContentsObserverAndroid;
29 import org.chromium.content_public.browser.WebContents; 36 import org.chromium.content_public.browser.WebContents;
37 import org.chromium.ui.base.Clipboard;
38
39 import java.util.Arrays;
40 import java.util.List;
30 41
31 /** 42 /**
32 * Java side of Android implementation of the website settings UI. 43 * Java side of Android implementation of the website settings UI.
44 * TODO(sashab): Rename this, and all its resources, to PageInfo* and page_info_ * instead of
45 * WebsiteSettings* and website_settings_*. Do this on the C++ sid e as well.
33 */ 46 */
34 public class WebsiteSettingsPopup { 47 public class WebsiteSettingsPopup implements OnClickListener, OnItemSelectedList ener {
48 /**
49 * An entry in the settings dropdown for a given permission. There are two o ptions for each
50 * permission: Allow and Block.
51 */
52 private static final class PageInfoPermissionEntry {
53 public final String name;
54 public final int type;
55 public final int value;
56
57 PageInfoPermissionEntry(String name, int type, int value) {
58 this.name = name;
59 this.type = type;
60 this.value = value;
61 }
62
63 public String toString() {
64 return name;
65 }
66 }
67
35 private final Context mContext; 68 private final Context mContext;
36 private final WebContents mWebContents; 69 private final WebContents mWebContents;
37 70
38 // A pointer to the C++ object for this UI. 71 // A pointer to the C++ object for this UI.
39 private final long mNativeWebsiteSettingsPopup; 72 private final long mNativeWebsiteSettingsPopup;
40 73
41 // The outer container, filled with the layout from website_settings.xml. 74 // The outer container, filled with the layout from website_settings.xml.
42 private final LinearLayout mContainer; 75 private final LinearLayout mContainer;
43 76
44 // UI elements in the dialog. 77 // UI elements in the dialog.
45 private final TextView mUrlTitle; 78 private final TextView mUrlTitle;
46 private final TextView mUrlConnectionMessage; 79 private final TextView mUrlConnectionMessage;
80 private final LinearLayout mPermissionsList;
81 private final Button mCopyUrlButton;
82 private final Button mSiteSettingsButton;
83 private final Button mDoneButton;
47 84
48 // The dialog the container is placed in. 85 // The dialog the container is placed in.
49 private final Dialog mDialog; 86 private final Dialog mDialog;
50 87
88 // The full URL from the URL bar, which is copied to the user's clipboard wh en they select 'Copy
89 // URL'.
90 private String mFullUrl;
91
51 /** 92 /**
52 * Creates the WebsiteSettingsPopup, but does not display it. Also 93 * Creates the WebsiteSettingsPopup, but does not display it. Also initializ es the corresponding
53 * initializes the corresponding C++ object and saves a pointer to it. 94 * C++ object and saves a pointer to it.
54 */ 95 */
55 private WebsiteSettingsPopup(Context context, WebContents webContents) { 96 private WebsiteSettingsPopup(Context context, WebContents webContents) {
56 mContext = context; 97 mContext = context;
57 mWebContents = webContents; 98 mWebContents = webContents;
58 99
59 // Find the container and all it's important subviews. 100 // Find the container and all it's important subviews.
60 mContainer = (LinearLayout) LayoutInflater.from(mContext).inflate( 101 mContainer = (LinearLayout) LayoutInflater.from(mContext).inflate(
61 R.layout.website_settings, null); 102 R.layout.website_settings, null);
62 103
63 mUrlTitle = (TextView) mContainer 104 mUrlTitle = (TextView) mContainer
64 .findViewById(R.id.website_settings_url); 105 .findViewById(R.id.website_settings_url);
65 mUrlConnectionMessage = (TextView) mContainer 106 mUrlConnectionMessage = (TextView) mContainer
66 .findViewById(R.id.website_settings_permission_message); 107 .findViewById(R.id.website_settings_connection_message);
108 mPermissionsList = (LinearLayout) mContainer
109 .findViewById(R.id.website_settings_permissions_list);
110
111 mCopyUrlButton = (Button) mContainer.findViewById(R.id.website_settings_ copy_url_button);
112 mCopyUrlButton.setOnClickListener(this);
113
114 mSiteSettingsButton = (Button) mContainer
115 .findViewById(R.id.website_settings_site_settings_button);
116 mSiteSettingsButton.setOnClickListener(this);
117 // Hide the Site Settings button until there's a link to take it to.
118 // TODO(sashab,finnur): Make this button visible once Site Settings is w orking.
119 mSiteSettingsButton.setVisibility(View.GONE);
120
121 mDoneButton = (Button) mContainer.findViewById(R.id.website_settings_don e_button);
122 mDoneButton.setOnClickListener(this);
67 123
68 // Create the dialog. 124 // Create the dialog.
69 mDialog = new Dialog(mContext); 125 mDialog = new Dialog(mContext);
70 mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); 126 mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
71 mDialog.setCanceledOnTouchOutside(true); 127 mDialog.setCanceledOnTouchOutside(true);
72 128
73 // Place the dialog at the top of the screen, and remove its border. 129 // Place the dialog at the top of the screen, and remove its border.
74 Window window = mDialog.getWindow(); 130 Window window = mDialog.getWindow();
75 window.setGravity(Gravity.TOP); 131 window.setGravity(Gravity.TOP);
76 window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 132 window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
77 133
78 // This needs to come after other member initialization. 134 // This needs to come after other member initialization.
79 mNativeWebsiteSettingsPopup = nativeInit(this, webContents); 135 mNativeWebsiteSettingsPopup = nativeInit(this, webContents);
80 final WebContentsObserverAndroid webContentsObserver = new WebContentsOb serverAndroid( 136 final WebContentsObserverAndroid webContentsObserver = new WebContentsOb serverAndroid(
81 mWebContents) { 137 mWebContents) {
82 @Override 138 @Override
83 public void navigationEntryCommitted() { 139 public void navigationEntryCommitted() {
84 // If a navigation is committed (e.g. from in-page redirect), th e data we're 140 // If a navigation is committed (e.g. from in-page redirect), th e data we're showing
85 // showing is stale so dismiss the dialog. 141 // is stale so dismiss the dialog.
86 mDialog.dismiss(); 142 mDialog.dismiss();
87 } 143 }
88 }; 144 };
89 mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { 145 mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
90 @Override 146 @Override
91 public void onDismiss(DialogInterface dialog) { 147 public void onDismiss(DialogInterface dialog) {
92 assert mNativeWebsiteSettingsPopup != 0; 148 assert mNativeWebsiteSettingsPopup != 0;
93 webContentsObserver.detachFromWebContents(); 149 webContentsObserver.detachFromWebContents();
94 nativeDestroy(mNativeWebsiteSettingsPopup); 150 nativeDestroy(mNativeWebsiteSettingsPopup);
95 } 151 }
96 }); 152 });
97 } 153 }
98 154
99 /** 155 /**
100 * Sets the URL in the title to: (scheme)://(domain)(path). Also colors 156 * Finds the Image resource of the icon to use for the given permission.
101 * different parts of the URL depending on connectionType. 157 *
102 * connectionType should be a valid PageInfoConnectionType. 158 * @param permission A valid ContentSettingsType that can be displayed in th e PageInfo dialog to
159 * retrieve the image for.
160 * @return The resource ID of the icon to use for that permission.
161 */
162 private int getImageResourceForPermission(int permission) {
163 switch (permission) {
164 case ContentSettingsType.CONTENT_SETTINGS_TYPE_IMAGES:
165 return R.drawable.page_info_image;
166 case ContentSettingsType.CONTENT_SETTINGS_TYPE_JAVASCRIPT:
167 return R.drawable.page_info_javascript;
168 case ContentSettingsType.CONTENT_SETTINGS_TYPE_GEOLOCATION:
169 return R.drawable.page_info_location;
170 case ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM:
171 return R.drawable.page_info_media;
172 case ContentSettingsType.CONTENT_SETTINGS_TYPE_NOTIFICATIONS:
173 return R.drawable.page_info_notification;
174 case ContentSettingsType.CONTENT_SETTINGS_TYPE_POPUPS:
175 return R.drawable.page_info_popups;
176 default:
177 assert false : "Icon requested for invalid permission: " + permi ssion;
178 return -1;
179 }
180 }
181
182 /**
183 * Sets the URL in the title of the dialog.
184 *
185 * @param url The URL to display, and to copy to the clipboard if the user s elects 'copy'.
186 * @param connectionType The type of connection (determines the colours used for the URL); must
187 * be a valid PageInfoConnectionType.
188 * @param schemeEndIndex The index of the last character in the scheme (e.g. the index of the
189 * 's' in https://www.google.com/)
190 * @param domainEndIndex The index of the last character of the domain (e.g. the index of 'm' in
191 * https://www.google.com/foo).
103 */ 192 */
104 @CalledByNative 193 @CalledByNative
105 private void setURLTitle(String scheme, String domain, String path, int conn ectionType) { 194 private void setURL(String url, int connectionType, int schemeEndIndex, int domainEndIndex) {
106 boolean makeDomainBold = false; 195 mFullUrl = url;
107 int schemeColorId = R.color.website_settings_popup_url_scheme_broken; 196 int schemeColorId = R.color.website_settings_popup_url_scheme_broken;
108 switch (connectionType) { 197 switch (connectionType) {
109 case PageInfoConnectionType.CONNECTION_UNKNOWN: 198 case PageInfoConnectionType.CONNECTION_UNKNOWN:
110 schemeColorId = R.color.website_settings_popup_url_scheme_http; 199 schemeColorId = R.color.website_settings_popup_url_scheme_http;
111 makeDomainBold = true;
112 break; 200 break;
113 case PageInfoConnectionType.CONNECTION_ENCRYPTED: 201 case PageInfoConnectionType.CONNECTION_ENCRYPTED:
114 schemeColorId = R.color.website_settings_popup_url_scheme_https; 202 schemeColorId = R.color.website_settings_popup_url_scheme_https;
115 break; 203 break;
116 case PageInfoConnectionType.CONNECTION_MIXED_CONTENT: 204 case PageInfoConnectionType.CONNECTION_MIXED_CONTENT:
117 schemeColorId = R.color.website_settings_popup_url_scheme_mixed; 205 schemeColorId = R.color.website_settings_popup_url_scheme_mixed;
118 makeDomainBold = true;
119 break; 206 break;
120 case PageInfoConnectionType.CONNECTION_UNENCRYPTED: 207 case PageInfoConnectionType.CONNECTION_UNENCRYPTED:
121 schemeColorId = R.color.website_settings_popup_url_scheme_http; 208 schemeColorId = R.color.website_settings_popup_url_scheme_http;
122 makeDomainBold = true;
123 break; 209 break;
124 case PageInfoConnectionType.CONNECTION_ENCRYPTED_ERROR: 210 case PageInfoConnectionType.CONNECTION_ENCRYPTED_ERROR:
125 schemeColorId = R.color.website_settings_popup_url_scheme_broken ; 211 schemeColorId = R.color.website_settings_popup_url_scheme_broken ;
126 makeDomainBold = true;
127 break; 212 break;
128 case PageInfoConnectionType.CONNECTION_INTERNAL_PAGE: 213 case PageInfoConnectionType.CONNECTION_INTERNAL_PAGE:
129 schemeColorId = R.color.website_settings_popup_url_scheme_http; 214 schemeColorId = R.color.website_settings_popup_url_scheme_http;
130 break; 215 break;
131 default: 216 default:
132 assert false : "Unexpected connection type: " + connectionType; 217 assert false : "Unknown connection type: " + connectionType;
133 } 218 }
134 219
135 SpannableStringBuilder sb = new SpannableStringBuilder(scheme + "://" + domain + path); 220 SpannableStringBuilder sb = new SpannableStringBuilder(url);
136 221
137 ForegroundColorSpan schemeColorSpan = new ForegroundColorSpan( 222 ForegroundColorSpan schemeColorSpan = new ForegroundColorSpan(
138 mContext.getResources().getColor(schemeColorId)); 223 mContext.getResources().getColor(schemeColorId));
139 sb.setSpan(schemeColorSpan, 0, scheme.length(), Spannable.SPAN_INCLUSIVE _INCLUSIVE); 224 sb.setSpan(schemeColorSpan, 0, schemeEndIndex, Spannable.SPAN_INCLUSIVE_ INCLUSIVE);
140 225
141 int domainStartIndex = scheme.length() + 3;
142 ForegroundColorSpan domainColorSpan = new ForegroundColorSpan( 226 ForegroundColorSpan domainColorSpan = new ForegroundColorSpan(
143 mContext.getResources().getColor(R.color.website_settings_popup_ url_domain)); 227 mContext.getResources().getColor(R.color.website_settings_popup_ url_domain));
144 sb.setSpan(domainColorSpan, domainStartIndex, domainStartIndex + domain. length(), 228 sb.setSpan(domainColorSpan, schemeEndIndex + 1, domainEndIndex,
145 Spannable.SPAN_INCLUSIVE_INCLUSIVE); 229 Spannable.SPAN_INCLUSIVE_INCLUSIVE);
146 230
147 if (makeDomainBold) {
148 StyleSpan boldStyleSpan = new StyleSpan(android.graphics.Typeface.BO LD);
149 sb.setSpan(boldStyleSpan, domainStartIndex, domainStartIndex + domai n.length(),
150 Spannable.SPAN_INCLUSIVE_INCLUSIVE);
151 }
152
153 mUrlTitle.setText(sb); 231 mUrlTitle.setText(sb);
154 } 232 }
155 233
156 /** 234 /**
157 * Sets the connection message displayed at the top of the dialog to 235 * Sets the connection message displayed at the top of the dialog (e.g. "Cou ld not securely
158 * connectionMessage (e.g. "Could not securely connect to this site"). 236 * connect to this site").
237 *
238 * @param connectionMessage The connection message to display.
159 */ 239 */
160 @CalledByNative 240 @CalledByNative
161 private void setConnectionMessage(String connectionMessage) { 241 private void setConnectionMessage(String connectionMessage) {
162 mUrlConnectionMessage.setText(connectionMessage); 242 mUrlConnectionMessage.setText(connectionMessage);
163 } 243 }
164 244
165 /** Displays the WebsiteSettingsPopup. */ 245 /**
246 * Adds a new row for the given permission.
247 *
248 * @param name The title of the permission to display to the user.
249 * @param type The ContentSettingsType of the permission.
250 * @param currentSetting The ContentSetting of the currently selected settin g.
251 */
252 @CalledByNative
253 private void addPermissionSection(String name, int type, int currentSetting) {
254 LinearLayout permissionRow = (LinearLayout) LayoutInflater.from(mContext ).inflate(
255 R.layout.website_settings_permission_row, null);
256
257 ImageView permission_icon = (ImageView) permissionRow.findViewById(
258 R.id.website_settings_permission_icon);
259 permission_icon.setImageResource(getImageResourceForPermission(type));
260
261 TextView permission_name = (TextView) permissionRow.findViewById(
262 R.id.website_settings_permission_name);
263 permission_name.setText(name);
264
265 Spinner permission_spinner = (Spinner) permissionRow.findViewById(
266 R.id.website_settings_permission_spinner);
267
268 // Save the permission type in a tag inside the spinner.
269 permission_spinner.setTag(R.id.website_settings_popup_permission_type, t ype);
270
271 // Work out the index of the currently selected setting.
272 int selectedSettingIndex = -1;
273 switch (currentSetting) {
274 case ContentSetting.ALLOW:
275 selectedSettingIndex = 0;
276 break;
277 case ContentSetting.BLOCK:
278 selectedSettingIndex = 1;
279 break;
280 default:
281 assert false : "Invalid current setting specified: " + currentSe tting;
282 }
283
284 List<PageInfoPermissionEntry> settingsChoices = Arrays.asList(
285 new PageInfoPermissionEntry(mContext.getResources().getString(
286 R.string.page_info_permission_allow), type, ContentSetti ng.ALLOW),
287 new PageInfoPermissionEntry(mContext.getResources().getString(
288 R.string.page_info_permission_block), type, ContentSetti ng.BLOCK));
289 ArrayAdapter<PageInfoPermissionEntry> adapter = new ArrayAdapter<PageInf oPermissionEntry>(
290 mContext, R.drawable.website_settings_permission_spinner_item, s ettingsChoices);
291 adapter.setDropDownViewResource(
292 R.drawable.website_settings_permission_spinner_dropdown_item);
293 permission_spinner.setAdapter(adapter);
294 permission_spinner.setSelection(selectedSettingIndex, false);
295 permission_spinner.setOnItemSelectedListener(this);
296 mPermissionsList.addView(permissionRow);
297 }
298
299 /**
300 * Displays the WebsiteSettingsPopup.
301 */
166 @CalledByNative 302 @CalledByNative
167 private void showDialog() { 303 private void showDialog() {
168 // Wrap the dialog in a ScrollView in case the content is too long. 304 // Wrap the dialog in a ScrollView in case the content is too long.
169 ScrollView scrollView = new ScrollView(mContext); 305 ScrollView scrollView = new ScrollView(mContext);
170 scrollView.addView(mContainer); 306 scrollView.addView(mContainer);
171 mDialog.addContentView(scrollView, new LinearLayout.LayoutParams( 307 mDialog.addContentView(scrollView, new LinearLayout.LayoutParams(
172 LinearLayout.LayoutParams.MATCH_PARENT, 308 LinearLayout.LayoutParams.MATCH_PARENT,
173 LinearLayout.LayoutParams.MATCH_PARENT)); 309 LinearLayout.LayoutParams.MATCH_PARENT));
174 310
175 // Make the dialog fill the width of the screen. This must be called 311 // Make the dialog fill the width of the screen. This must be called aft er addContentView,
176 // after addContentView, or it won't fully fill to the edge. 312 // or it won't fully fill to the edge.
177 Window window = mDialog.getWindow(); 313 Window window = mDialog.getWindow();
178 window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, 314 window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
179 ViewGroup.LayoutParams.WRAP_CONTENT); 315 ViewGroup.LayoutParams.WRAP_CONTENT);
180 316
181 mDialog.show(); 317 mDialog.show();
182 } 318 }
183 319
320 @Override
321 public void onItemSelected(AdapterView<?> parent, View view, int pos, long i d) {
322 int settingsType = (Integer) parent.getTag(R.id.website_settings_popup_p ermission_type);
323
324 int selectedSetting = -1;
325 switch (pos) {
326 case 0:
327 selectedSetting = ContentSetting.ALLOW;
328 break;
329 case 1:
330 selectedSetting = ContentSetting.BLOCK;
331 break;
332 default:
333 assert false : "Invalid permission option selected: " + pos;
334 }
335
336 nativeOnPermissionSettingChanged(mNativeWebsiteSettingsPopup, settingsTy pe,
337 selectedSetting);
338 }
339
340 @Override
341 public void onNothingSelected(AdapterView<?> parent) {
342 // Do nothing intentionally.
343 }
344
345 @Override
346 public void onClick(View view) {
347 if (view == mCopyUrlButton) {
348 new Clipboard(mContext).setText(mFullUrl, mFullUrl);
349 } else if (view == mSiteSettingsButton) {
350 // TODO(sashab,finnur): Make this open the Website Settings dialog.
351 assert false : "No Website Settings here!";
352 mDialog.dismiss();
353 } else if (view == mDoneButton) {
354 mDialog.dismiss();
355 }
356 }
357
184 /** 358 /**
185 * Shows a WebsiteSettings dialog for the provided WebContents. The popup 359 * Shows a WebsiteSettings dialog for the provided WebContents. The popup ad ds itself to the
186 * adds itself to the view hierarchy which owns the reference while it's 360 * view hierarchy which owns the reference while it's visible.
187 * visible.
188 * 361 *
189 * @param context Context which is used for launching a dialog. 362 * @param context Context which is used for launching a dialog.
190 * @param webContents The WebContents for which to show Website information. 363 * @param webContents The WebContents for which to show Website information. This information is
191 * This information is retrieved for the visible entry. 364 * retrieved for the visible entry.
192 */ 365 */
193 @SuppressWarnings("unused") 366 @SuppressWarnings("unused")
194 public static void show(Context context, WebContents webContents) { 367 public static void show(Context context, WebContents webContents) {
195 if (CommandLine.getInstance().hasSwitch(ChromeSwitches.ENABLE_NEW_WEBSIT E_SETTINGS)) { 368 if (CommandLine.getInstance().hasSwitch(ChromeSwitches.ENABLE_NEW_WEBSIT E_SETTINGS)) {
196 new WebsiteSettingsPopup(context, webContents); 369 new WebsiteSettingsPopup(context, webContents);
197 } else { 370 } else {
198 WebsiteSettingsPopupLegacy.show(context, webContents); 371 WebsiteSettingsPopupLegacy.show(context, webContents);
199 } 372 }
200 } 373 }
201 374
202 private static native long nativeInit(WebsiteSettingsPopup popup, 375 private static native long nativeInit(WebsiteSettingsPopup popup,
203 WebContents webContents); 376 WebContents webContents);
204 377
205 private native void nativeDestroy(long nativeWebsiteSettingsPopupAndroid); 378 private native void nativeDestroy(long nativeWebsiteSettingsPopupAndroid);
379
380 private native void nativeOnPermissionSettingChanged(long nativeWebsiteSetti ngsPopupAndroid,
381 int type, int setting);
206 } 382 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698