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

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

Issue 2102813002: If location services are turned off, have the BT chooser prompt the user to turn them on. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@bluetooth-fix-testing
Patch Set: Address tedchoc's comments Created 4 years, 5 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
« no previous file with comments | « no previous file | chrome/android/java/src/org/chromium/chrome/browser/ItemChooserDialog.java » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 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.Manifest; 7 import android.Manifest;
8 import android.app.Activity; 8 import android.app.Activity;
9 import android.content.BroadcastReceiver;
9 import android.content.Context; 10 import android.content.Context;
10 import android.content.Intent; 11 import android.content.Intent;
11 import android.content.pm.PackageManager; 12 import android.content.IntentFilter;
13 import android.location.LocationManager;
12 import android.text.SpannableString; 14 import android.text.SpannableString;
13 import android.text.TextUtils; 15 import android.text.TextUtils;
14 import android.view.View; 16 import android.view.View;
15 17
16 import org.chromium.base.VisibleForTesting; 18 import org.chromium.base.VisibleForTesting;
17 import org.chromium.base.annotations.CalledByNative; 19 import org.chromium.base.annotations.CalledByNative;
18 import org.chromium.chrome.R; 20 import org.chromium.chrome.R;
19 import org.chromium.chrome.browser.omnibox.OmniboxUrlEmphasizer; 21 import org.chromium.chrome.browser.omnibox.OmniboxUrlEmphasizer;
20 import org.chromium.chrome.browser.profiles.Profile; 22 import org.chromium.chrome.browser.profiles.Profile;
21 import org.chromium.components.location.LocationUtils; 23 import org.chromium.components.location.LocationUtils;
22 import org.chromium.ui.base.WindowAndroid; 24 import org.chromium.ui.base.WindowAndroid;
23 import org.chromium.ui.text.NoUnderlineClickableSpan; 25 import org.chromium.ui.text.NoUnderlineClickableSpan;
24 import org.chromium.ui.text.SpanApplier; 26 import org.chromium.ui.text.SpanApplier;
25 import org.chromium.ui.text.SpanApplier.SpanInfo; 27 import org.chromium.ui.text.SpanApplier.SpanInfo;
26 28
27 /** 29 /**
28 * A dialog for picking available Bluetooth devices. This dialog is shown when a website requests to 30 * A dialog for picking available Bluetooth devices. This dialog is shown when a website requests to
29 * pair with a certain class of Bluetooth devices (e.g. through a bluetooth.requ estDevice Javascript 31 * pair with a certain class of Bluetooth devices (e.g. through a bluetooth.requ estDevice Javascript
30 * call). 32 * call).
33 *
34 * The dialog is shown by create() or show(), and always runs finishDialog() as it's closing.
31 */ 35 */
32 public class BluetoothChooserDialog 36 public class BluetoothChooserDialog
33 implements ItemChooserDialog.ItemSelectedCallback, WindowAndroid.Permiss ionCallback { 37 implements ItemChooserDialog.ItemSelectedCallback, WindowAndroid.Permiss ionCallback {
34 // These constants match BluetoothChooserAndroid::ShowDiscoveryState, and ar e used in 38 // These constants match BluetoothChooserAndroid::ShowDiscoveryState, and ar e used in
35 // notifyDiscoveryState(). 39 // notifyDiscoveryState().
36 static final int DISCOVERY_FAILED_TO_START = 0; 40 static final int DISCOVERY_FAILED_TO_START = 0;
37 static final int DISCOVERING = 1; 41 static final int DISCOVERING = 1;
38 static final int DISCOVERY_IDLE = 2; 42 static final int DISCOVERY_IDLE = 2;
39 43
40 // Values passed to nativeOnDialogFinished:eventType, and only used in the n ative function. 44 // Values passed to nativeOnDialogFinished:eventType, and only used in the n ative function.
(...skipping 14 matching lines...) Expand all
55 // The origin for the site wanting to pair with the bluetooth devices. 59 // The origin for the site wanting to pair with the bluetooth devices.
56 String mOrigin; 60 String mOrigin;
57 61
58 // The security level of the connection to the site wanting to pair with the 62 // The security level of the connection to the site wanting to pair with the
59 // bluetooth devices. For valid values see SecurityStateModel::SecurityLevel . 63 // bluetooth devices. For valid values see SecurityStateModel::SecurityLevel .
60 int mSecurityLevel; 64 int mSecurityLevel;
61 65
62 // A pointer back to the native part of the implementation for this dialog. 66 // A pointer back to the native part of the implementation for this dialog.
63 long mNativeBluetoothChooserDialogPtr; 67 long mNativeBluetoothChooserDialogPtr;
64 68
69 @VisibleForTesting
70 final BroadcastReceiver mLocationModeBroadcastReceiver = new BroadcastReceiv er() {
71 @Override
72 public void onReceive(Context context, Intent intent) {
73 if (!LocationManager.MODE_CHANGED_ACTION.equals(intent.getAction())) {
74 return;
75 }
76 if (checkLocationServicesAndPermission()) {
77 mItemChooserDialog.clear();
78 nativeRestartSearch(mNativeBluetoothChooserDialogPtr);
79 }
80 }
81 };
82
65 // The type of link that is shown within the dialog. 83 // The type of link that is shown within the dialog.
66 private enum LinkType { 84 private enum LinkType {
67 EXPLAIN_BLUETOOTH, 85 EXPLAIN_BLUETOOTH,
68 ADAPTER_OFF, 86 ADAPTER_OFF,
69 ADAPTER_OFF_HELP, 87 ADAPTER_OFF_HELP,
70 REQUEST_LOCATION_PERMISSION, 88 REQUEST_LOCATION_PERMISSION,
89 REQUEST_LOCATION_SERVICES,
71 NEED_LOCATION_PERMISSION_HELP, 90 NEED_LOCATION_PERMISSION_HELP,
72 RESTART_SEARCH, 91 RESTART_SEARCH,
73 } 92 }
74 93
75 /** 94 /**
76 * Creates the BluetoothChooserDialog. 95 * Creates the BluetoothChooserDialog.
77 */ 96 */
78 @VisibleForTesting 97 @VisibleForTesting
79 BluetoothChooserDialog(WindowAndroid windowAndroid, String origin, int secur ityLevel, 98 BluetoothChooserDialog(WindowAndroid windowAndroid, String origin, int secur ityLevel,
80 long nativeBluetoothChooserDialogPtr) { 99 long nativeBluetoothChooserDialogPtr) {
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
123 mActivity.getString(R.string.bluetooth_not_seeing_it_idle_some_f ound), 142 mActivity.getString(R.string.bluetooth_not_seeing_it_idle_some_f ound),
124 new SpanInfo("<link1>", "</link1>", 143 new SpanInfo("<link1>", "</link1>",
125 new BluetoothClickableSpan(LinkType.EXPLAIN_BLUETOOTH, m Activity)), 144 new BluetoothClickableSpan(LinkType.EXPLAIN_BLUETOOTH, m Activity)),
126 new SpanInfo("<link2>", "</link2>", 145 new SpanInfo("<link2>", "</link2>",
127 new BluetoothClickableSpan(LinkType.RESTART_SEARCH, mAct ivity))); 146 new BluetoothClickableSpan(LinkType.RESTART_SEARCH, mAct ivity)));
128 147
129 ItemChooserDialog.ItemChooserLabels labels = 148 ItemChooserDialog.ItemChooserLabels labels =
130 new ItemChooserDialog.ItemChooserLabels(title, searching, noneFo und, 149 new ItemChooserDialog.ItemChooserLabels(title, searching, noneFo und,
131 statusIdleNoneFound, statusIdleSomeFound, positiveButton ); 150 statusIdleNoneFound, statusIdleSomeFound, positiveButton );
132 mItemChooserDialog = new ItemChooserDialog(mActivity, this, labels); 151 mItemChooserDialog = new ItemChooserDialog(mActivity, this, labels);
152
153 mActivity.registerReceiver(mLocationModeBroadcastReceiver,
154 new IntentFilter(LocationManager.MODE_CHANGED_ACTION));
155 }
156
157 // Called to report the dialog's results back to native code.
158 private void finishDialog(int resultCode, String id) {
159 mActivity.unregisterReceiver(mLocationModeBroadcastReceiver);
160 if (mNativeBluetoothChooserDialogPtr != 0) {
161 nativeOnDialogFinished(mNativeBluetoothChooserDialogPtr, resultCode, id);
162 }
133 } 163 }
134 164
135 @Override 165 @Override
136 public void onItemSelected(String id) { 166 public void onItemSelected(String id) {
137 if (mNativeBluetoothChooserDialogPtr != 0) { 167 if (id.isEmpty()) {
138 if (id.isEmpty()) { 168 finishDialog(DIALOG_FINISHED_CANCELLED, "");
139 nativeOnDialogFinished( 169 } else {
140 mNativeBluetoothChooserDialogPtr, DIALOG_FINISHED_CANCEL LED, ""); 170 finishDialog(DIALOG_FINISHED_SELECTED, id);
141 } else {
142 nativeOnDialogFinished(
143 mNativeBluetoothChooserDialogPtr, DIALOG_FINISHED_SELECT ED, id);
144 }
145 } 171 }
146 } 172 }
147 173
148 @Override 174 @Override
149 public void onRequestPermissionsResult(String[] permissions, int[] grantResu lts) { 175 public void onRequestPermissionsResult(String[] permissions, int[] grantResu lts) {
150 for (int i = 0; i < grantResults.length; i++) { 176 for (int i = 0; i < permissions.length; i++) {
151 if (permissions[i].equals(Manifest.permission.ACCESS_COARSE_LOCATION )) { 177 if (permissions[i].equals(Manifest.permission.ACCESS_COARSE_LOCATION )) {
152 if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { 178 if (checkLocationServicesAndPermission()) {
153 mItemChooserDialog.clear(); 179 mItemChooserDialog.clear();
154 nativeRestartSearch(mNativeBluetoothChooserDialogPtr); 180 nativeRestartSearch(mNativeBluetoothChooserDialogPtr);
155 } else {
156 checkLocationPermission();
157 } 181 }
158 return; 182 return;
159 } 183 }
160 } 184 }
161 // If the location permission is not present, leave the currently-shown message in place. 185 // If the location permission is not present, leave the currently-shown message in place.
162 } 186 }
163 187
164 private void checkLocationPermission() { 188 // Returns true if Location Services is on and Chrome has permission to see the user's location.
165 if (LocationUtils.getInstance().hasAndroidLocationPermission(mActivity)) { 189 private boolean checkLocationServicesAndPermission() {
166 return; 190 final boolean havePermission =
191 LocationUtils.getInstance().hasAndroidLocationPermission(mActivi ty);
192 final boolean locationServicesOn =
193 LocationUtils.getInstance().isSystemLocationSettingEnabled(mActi vity);
194
195 if (!havePermission
196 && !mWindowAndroid.canRequestPermission(
197 Manifest.permission.ACCESS_COARSE_LOCATION)) {
198 // Immediately close the dialog because the user has asked Chrome no t to request the
199 // location permission.
200 finishDialog(DIALOG_FINISHED_DENIED_PERMISSION, "");
201 return false;
167 } 202 }
168 203
169 if (!mWindowAndroid.canRequestPermission(Manifest.permission.ACCESS_COAR SE_LOCATION)) { 204 // Compute the message to show the user.
170 if (mNativeBluetoothChooserDialogPtr != 0) { 205 final SpanInfo permissionSpan = new SpanInfo("<permission_link>", "</per mission_link>",
171 nativeOnDialogFinished( 206 new BluetoothClickableSpan(LinkType.REQUEST_LOCATION_PERMISSION, mActivity));
172 mNativeBluetoothChooserDialogPtr, DIALOG_FINISHED_DENIED _PERMISSION, ""); 207 final SpanInfo servicesSpan = new SpanInfo("<services_link>", "</service s_link>",
173 return; 208 new BluetoothClickableSpan(LinkType.REQUEST_LOCATION_SERVICES, m Activity));
209 final SpannableString needLocationMessage;
210 if (havePermission) {
211 if (locationServicesOn) {
212 // We don't need to request anything.
213 return true;
214 } else {
215 needLocationMessage = SpanApplier.applySpans(
216 mActivity.getString(R.string.bluetooth_need_location_ser vices_on),
217 servicesSpan);
218 }
219 } else {
220 if (locationServicesOn) {
221 needLocationMessage = SpanApplier.applySpans(
222 mActivity.getString(R.string.bluetooth_need_location_per mission),
223 permissionSpan);
224 } else {
225 needLocationMessage = SpanApplier.applySpans(
226 mActivity.getString(
227 R.string.bluetooth_need_location_permission_and_ services_on),
228 permissionSpan, servicesSpan);
174 } 229 }
175 } 230 }
176 231
177 SpannableString needLocationMessage = SpanApplier.applySpans(
178 mActivity.getString(R.string.bluetooth_need_location_permission) ,
179 new SpanInfo("<link>", "</link>",
180 new BluetoothClickableSpan(
181 LinkType.REQUEST_LOCATION_PERMISSION, mActi vity)));
182
183 SpannableString needLocationStatus = SpanApplier.applySpans( 232 SpannableString needLocationStatus = SpanApplier.applySpans(
184 mActivity.getString(R.string.bluetooth_need_location_permission_ help), 233 mActivity.getString(R.string.bluetooth_need_location_permission_ help),
185 new SpanInfo("<link>", "</link>", 234 new SpanInfo("<link>", "</link>",
186 new BluetoothClickableSpan( 235 new BluetoothClickableSpan(
187 LinkType.NEED_LOCATION_PERMISSION_HELP, mAc tivity))); 236 LinkType.NEED_LOCATION_PERMISSION_HELP, mAc tivity)));
188 237
189 mItemChooserDialog.setErrorState(needLocationMessage, needLocationStatus ); 238 mItemChooserDialog.setErrorState(needLocationMessage, needLocationStatus );
239 return false;
190 } 240 }
191 241
192 private class BluetoothClickableSpan extends NoUnderlineClickableSpan { 242 private class BluetoothClickableSpan extends NoUnderlineClickableSpan {
193 // The type of link this span represents. 243 // The type of link this span represents.
194 private LinkType mLinkType; 244 private LinkType mLinkType;
195 245
196 private Context mContext; 246 private Context mContext;
197 247
198 BluetoothClickableSpan(LinkType linkType, Context context) { 248 BluetoothClickableSpan(LinkType linkType, Context context) {
199 mLinkType = linkType; 249 mLinkType = linkType;
(...skipping 26 matching lines...) Expand all
226 nativeShowBluetoothAdapterOffLink(mNativeBluetoothChooserDia logPtr); 276 nativeShowBluetoothAdapterOffLink(mNativeBluetoothChooserDia logPtr);
227 closeDialog(); 277 closeDialog();
228 break; 278 break;
229 } 279 }
230 case REQUEST_LOCATION_PERMISSION: { 280 case REQUEST_LOCATION_PERMISSION: {
231 mWindowAndroid.requestPermissions( 281 mWindowAndroid.requestPermissions(
232 new String[] {Manifest.permission.ACCESS_COARSE_LOCA TION}, 282 new String[] {Manifest.permission.ACCESS_COARSE_LOCA TION},
233 BluetoothChooserDialog.this); 283 BluetoothChooserDialog.this);
234 break; 284 break;
235 } 285 }
286 case REQUEST_LOCATION_SERVICES: {
287 mContext.startActivity(
288 LocationUtils.getInstance().getSystemLocationSetting sIntent());
289 break;
290 }
236 case NEED_LOCATION_PERMISSION_HELP: { 291 case NEED_LOCATION_PERMISSION_HELP: {
237 nativeShowNeedLocationPermissionLink(mNativeBluetoothChooser DialogPtr); 292 nativeShowNeedLocationPermissionLink(mNativeBluetoothChooser DialogPtr);
238 closeDialog(); 293 closeDialog();
239 break; 294 break;
240 } 295 }
241 case RESTART_SEARCH: { 296 case RESTART_SEARCH: {
242 mItemChooserDialog.clear(); 297 mItemChooserDialog.clear();
243 nativeRestartSearch(mNativeBluetoothChooserDialogPtr); 298 nativeRestartSearch(mNativeBluetoothChooserDialogPtr);
244 break; 299 break;
245 } 300 }
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
307 @CalledByNative 362 @CalledByNative
308 private void notifyAdapterTurnedOn() { 363 private void notifyAdapterTurnedOn() {
309 mItemChooserDialog.clear(); 364 mItemChooserDialog.clear();
310 } 365 }
311 366
312 @VisibleForTesting 367 @VisibleForTesting
313 @CalledByNative 368 @CalledByNative
314 void notifyDiscoveryState(int discoveryState) { 369 void notifyDiscoveryState(int discoveryState) {
315 switch (discoveryState) { 370 switch (discoveryState) {
316 case DISCOVERY_FAILED_TO_START: { 371 case DISCOVERY_FAILED_TO_START: {
317 // FAILED_TO_START might be caused by a missing Location permiss ion. 372 // FAILED_TO_START might be caused by a missing Location
373 // permission or by the Location service being turned off.
318 // Check, and show a request if so. 374 // Check, and show a request if so.
319 checkLocationPermission(); 375 checkLocationServicesAndPermission();
320 break; 376 break;
321 } 377 }
322 case DISCOVERY_IDLE: { 378 case DISCOVERY_IDLE: {
323 mItemChooserDialog.setIdleState(); 379 mItemChooserDialog.setIdleState();
324 break; 380 break;
325 } 381 }
326 default: { 382 default: {
327 // TODO(jyasskin): Report the new state to the user. 383 // TODO(jyasskin): Report the new state to the user.
328 break; 384 break;
329 } 385 }
330 } 386 }
331 } 387 }
332 388
333 @VisibleForTesting 389 @VisibleForTesting
334 native void nativeOnDialogFinished( 390 native void nativeOnDialogFinished(
335 long nativeBluetoothChooserAndroid, int eventType, String deviceId); 391 long nativeBluetoothChooserAndroid, int eventType, String deviceId);
336 @VisibleForTesting 392 @VisibleForTesting
337 native void nativeRestartSearch(long nativeBluetoothChooserAndroid); 393 native void nativeRestartSearch(long nativeBluetoothChooserAndroid);
338 // Help links. 394 // Help links.
339 @VisibleForTesting 395 @VisibleForTesting
340 native void nativeShowBluetoothOverviewLink(long nativeBluetoothChooserAndro id); 396 native void nativeShowBluetoothOverviewLink(long nativeBluetoothChooserAndro id);
341 @VisibleForTesting 397 @VisibleForTesting
342 native void nativeShowBluetoothAdapterOffLink(long nativeBluetoothChooserAnd roid); 398 native void nativeShowBluetoothAdapterOffLink(long nativeBluetoothChooserAnd roid);
343 @VisibleForTesting 399 @VisibleForTesting
344 native void nativeShowNeedLocationPermissionLink(long nativeBluetoothChooser Android); 400 native void nativeShowNeedLocationPermissionLink(long nativeBluetoothChooser Android);
345 } 401 }
OLDNEW
« no previous file with comments | « no previous file | chrome/android/java/src/org/chromium/chrome/browser/ItemChooserDialog.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698