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

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: Fix merge conflicts 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 20 matching lines...) Expand all
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
65 // The type of link that is shown within the dialog. 69 // The type of link that is shown within the dialog.
66 private enum LinkType { 70 private enum LinkType {
67 EXPLAIN_BLUETOOTH, 71 EXPLAIN_BLUETOOTH,
68 ADAPTER_OFF, 72 ADAPTER_OFF,
69 ADAPTER_OFF_HELP, 73 ADAPTER_OFF_HELP,
70 REQUEST_LOCATION_PERMISSION, 74 REQUEST_LOCATION_PERMISSION,
75 REQUEST_LOCATION_SERVICES,
71 NEED_LOCATION_PERMISSION_HELP, 76 NEED_LOCATION_PERMISSION_HELP,
72 RESTART_SEARCH, 77 RESTART_SEARCH,
73 } 78 }
74 79
75 /** 80 /**
76 * Creates the BluetoothChooserDialog. 81 * Creates the BluetoothChooserDialog.
77 */ 82 */
78 @VisibleForTesting 83 @VisibleForTesting
79 BluetoothChooserDialog(WindowAndroid windowAndroid, String origin, int secur ityLevel, 84 BluetoothChooserDialog(WindowAndroid windowAndroid, String origin, int secur ityLevel,
80 long nativeBluetoothChooserDialogPtr) { 85 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), 128 mActivity.getString(R.string.bluetooth_not_seeing_it_idle_some_f ound),
124 new SpanInfo("<link1>", "</link1>", 129 new SpanInfo("<link1>", "</link1>",
125 new BluetoothClickableSpan(LinkType.EXPLAIN_BLUETOOTH, m Activity)), 130 new BluetoothClickableSpan(LinkType.EXPLAIN_BLUETOOTH, m Activity)),
126 new SpanInfo("<link2>", "</link2>", 131 new SpanInfo("<link2>", "</link2>",
127 new BluetoothClickableSpan(LinkType.RESTART_SEARCH, mAct ivity))); 132 new BluetoothClickableSpan(LinkType.RESTART_SEARCH, mAct ivity)));
128 133
129 ItemChooserDialog.ItemChooserLabels labels = 134 ItemChooserDialog.ItemChooserLabels labels =
130 new ItemChooserDialog.ItemChooserLabels(title, searching, noneFo und, 135 new ItemChooserDialog.ItemChooserLabels(title, searching, noneFo und,
131 statusIdleNoneFound, statusIdleSomeFound, positiveButton ); 136 statusIdleNoneFound, statusIdleSomeFound, positiveButton );
132 mItemChooserDialog = new ItemChooserDialog(mActivity, this, labels); 137 mItemChooserDialog = new ItemChooserDialog(mActivity, this, labels);
138
139 mActivity.registerReceiver(mLocationModeBroadcastReceiver,
140 new IntentFilter(LocationManager.MODE_CHANGED_ACTION));
Ted C 2016/07/01 20:18:34 does this give a lint warning? MODE_CHANGED_ACTIO
ortuno 2016/07/01 22:08:50 Hmm no lint errors. Is there anything I should do
Ted C 2016/07/01 22:22:56 While we lack the bluetooth permission prior to M,
141 }
142
143 // Called to report the dialog's results back to native code.
144 private void finishDialog(int resultCode, String id) {
145 mActivity.unregisterReceiver(mLocationModeBroadcastReceiver);
146 if (mNativeBluetoothChooserDialogPtr != 0) {
147 nativeOnDialogFinished(mNativeBluetoothChooserDialogPtr, resultCode, id);
148 }
133 } 149 }
134 150
135 @Override 151 @Override
136 public void onItemSelected(String id) { 152 public void onItemSelected(String id) {
137 if (mNativeBluetoothChooserDialogPtr != 0) { 153 if (id.isEmpty()) {
138 if (id.isEmpty()) { 154 finishDialog(DIALOG_FINISHED_CANCELLED, "");
139 nativeOnDialogFinished( 155 } else {
140 mNativeBluetoothChooserDialogPtr, DIALOG_FINISHED_CANCEL LED, ""); 156 finishDialog(DIALOG_FINISHED_SELECTED, id);
141 } else {
142 nativeOnDialogFinished(
143 mNativeBluetoothChooserDialogPtr, DIALOG_FINISHED_SELECT ED, id);
144 }
145 } 157 }
146 } 158 }
147 159
148 @Override 160 @Override
149 public void onRequestPermissionsResult(String[] permissions, int[] grantResu lts) { 161 public void onRequestPermissionsResult(String[] permissions, int[] grantResu lts) {
150 for (int i = 0; i < grantResults.length; i++) { 162 for (int i = 0; i < permissions.length; i++) {
151 if (permissions[i].equals(Manifest.permission.ACCESS_COARSE_LOCATION )) { 163 if (permissions[i].equals(Manifest.permission.ACCESS_COARSE_LOCATION )) {
152 if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { 164 if (checkLocationServicesAndPermission()) {
153 mItemChooserDialog.clear(); 165 mItemChooserDialog.clear();
154 nativeRestartSearch(mNativeBluetoothChooserDialogPtr); 166 nativeRestartSearch(mNativeBluetoothChooserDialogPtr);
155 } else {
156 checkLocationPermission();
157 } 167 }
158 return; 168 return;
159 } 169 }
160 } 170 }
161 // If the location permission is not present, leave the currently-shown message in place. 171 // If the location permission is not present, leave the currently-shown message in place.
162 } 172 }
163 173
164 private void checkLocationPermission() { 174 @VisibleForTesting
165 if (LocationUtils.getInstance().hasAndroidLocationPermission(mActivity)) { 175 final BroadcastReceiver mLocationModeBroadcastReceiver = new BroadcastReceiv er() {
Ted C 2016/07/01 20:18:34 move this up to be with the other params (but at t
ortuno 2016/07/01 22:08:50 Done.
166 return; 176 @Override
177 public void onReceive(Context context, Intent intent) {
178 if (LocationManager.MODE_CHANGED_ACTION.equals(intent.getAction())) {
Ted C 2016/07/01 20:18:34 make this an early return to avoid indenting
ortuno 2016/07/01 22:08:50 Done.
179 if (checkLocationServicesAndPermission()) {
180 mItemChooserDialog.clear();
181 nativeRestartSearch(mNativeBluetoothChooserDialogPtr);
182 }
183 }
184 }
185 };
186
187 // Returns true if Location Services is on and Chrome has permission to see the user's location.
188 private boolean checkLocationServicesAndPermission() {
189 final boolean havePermission =
190 LocationUtils.getInstance().hasAndroidLocationPermission(mActivi ty);
191 final boolean locationServicesOn =
192 LocationUtils.getInstance().isSystemLocationSettingEnabled(mActi vity);
193
194 if (!havePermission
195 && !mWindowAndroid.canRequestPermission(
196 Manifest.permission.ACCESS_COARSE_LOCATION)) {
197 // Immediately close the dialog because the user has asked Chrome no t to request the
198 // location permission.
199 finishDialog(DIALOG_FINISHED_DENIED_PERMISSION, "");
200 return false;
167 } 201 }
168 202
169 if (!mWindowAndroid.canRequestPermission(Manifest.permission.ACCESS_COAR SE_LOCATION)) { 203 // Compute the message to show the user.
170 if (mNativeBluetoothChooserDialogPtr != 0) { 204 final SpanInfo permissionSpan = new SpanInfo("<permission_link>", "</per mission_link>",
171 nativeOnDialogFinished( 205 new BluetoothClickableSpan(LinkType.REQUEST_LOCATION_PERMISSION, mActivity));
172 mNativeBluetoothChooserDialogPtr, DIALOG_FINISHED_DENIED _PERMISSION, ""); 206 final SpanInfo servicesSpan = new SpanInfo("<services_link>", "</service s_link>",
173 return; 207 new BluetoothClickableSpan(LinkType.REQUEST_LOCATION_SERVICES, m Activity));
208 final SpannableString needLocationMessage;
209 if (havePermission) {
210 if (locationServicesOn) {
211 // We don't need to request anything.
212 return true;
213 } else {
214 needLocationMessage = SpanApplier.applySpans(
215 mActivity.getString(R.string.bluetooth_need_location_ser vices_on),
216 servicesSpan);
217 }
218 } else {
219 if (locationServicesOn) {
220 needLocationMessage = SpanApplier.applySpans(
221 mActivity.getString(R.string.bluetooth_need_location_per mission),
222 permissionSpan);
223 } else {
224 needLocationMessage = SpanApplier.applySpans(
225 mActivity.getString(
226 R.string.bluetooth_need_location_permission_and_ services_on),
227 permissionSpan, servicesSpan);
174 } 228 }
175 } 229 }
176 230
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( 231 SpannableString needLocationStatus = SpanApplier.applySpans(
184 mActivity.getString(R.string.bluetooth_need_location_permission_ help), 232 mActivity.getString(R.string.bluetooth_need_location_permission_ help),
185 new SpanInfo("<link>", "</link>", 233 new SpanInfo("<link>", "</link>",
186 new BluetoothClickableSpan( 234 new BluetoothClickableSpan(
187 LinkType.NEED_LOCATION_PERMISSION_HELP, mAc tivity))); 235 LinkType.NEED_LOCATION_PERMISSION_HELP, mAc tivity)));
188 236
189 mItemChooserDialog.setErrorState(needLocationMessage, needLocationStatus ); 237 mItemChooserDialog.setErrorState(needLocationMessage, needLocationStatus );
238 return false;
190 } 239 }
191 240
192 private class BluetoothClickableSpan extends NoUnderlineClickableSpan { 241 private class BluetoothClickableSpan extends NoUnderlineClickableSpan {
193 // The type of link this span represents. 242 // The type of link this span represents.
194 private LinkType mLinkType; 243 private LinkType mLinkType;
195 244
196 private Context mContext; 245 private Context mContext;
197 246
198 BluetoothClickableSpan(LinkType linkType, Context context) { 247 BluetoothClickableSpan(LinkType linkType, Context context) {
199 mLinkType = linkType; 248 mLinkType = linkType;
(...skipping 26 matching lines...) Expand all
226 nativeShowBluetoothAdapterOffLink(mNativeBluetoothChooserDia logPtr); 275 nativeShowBluetoothAdapterOffLink(mNativeBluetoothChooserDia logPtr);
227 closeDialog(); 276 closeDialog();
228 break; 277 break;
229 } 278 }
230 case REQUEST_LOCATION_PERMISSION: { 279 case REQUEST_LOCATION_PERMISSION: {
231 mWindowAndroid.requestPermissions( 280 mWindowAndroid.requestPermissions(
232 new String[] {Manifest.permission.ACCESS_COARSE_LOCA TION}, 281 new String[] {Manifest.permission.ACCESS_COARSE_LOCA TION},
233 BluetoothChooserDialog.this); 282 BluetoothChooserDialog.this);
234 break; 283 break;
235 } 284 }
285 case REQUEST_LOCATION_SERVICES: {
286 mContext.startActivity(
287 LocationUtils.getInstance().getSystemLocationSetting sIntent());
288 break;
289 }
236 case NEED_LOCATION_PERMISSION_HELP: { 290 case NEED_LOCATION_PERMISSION_HELP: {
237 nativeShowNeedLocationPermissionLink(mNativeBluetoothChooser DialogPtr); 291 nativeShowNeedLocationPermissionLink(mNativeBluetoothChooser DialogPtr);
238 closeDialog(); 292 closeDialog();
239 break; 293 break;
240 } 294 }
241 case RESTART_SEARCH: { 295 case RESTART_SEARCH: {
242 mItemChooserDialog.clear(); 296 mItemChooserDialog.clear();
243 nativeRestartSearch(mNativeBluetoothChooserDialogPtr); 297 nativeRestartSearch(mNativeBluetoothChooserDialogPtr);
244 break; 298 break;
245 } 299 }
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
307 @CalledByNative 361 @CalledByNative
308 private void notifyAdapterTurnedOn() { 362 private void notifyAdapterTurnedOn() {
309 mItemChooserDialog.clear(); 363 mItemChooserDialog.clear();
310 } 364 }
311 365
312 @VisibleForTesting 366 @VisibleForTesting
313 @CalledByNative 367 @CalledByNative
314 void notifyDiscoveryState(int discoveryState) { 368 void notifyDiscoveryState(int discoveryState) {
315 switch (discoveryState) { 369 switch (discoveryState) {
316 case DISCOVERY_FAILED_TO_START: { 370 case DISCOVERY_FAILED_TO_START: {
317 // FAILED_TO_START might be caused by a missing Location permiss ion. 371 // FAILED_TO_START might be caused by a missing Location
372 // permission or by the Location service being turned off.
318 // Check, and show a request if so. 373 // Check, and show a request if so.
319 checkLocationPermission(); 374 checkLocationServicesAndPermission();
320 break; 375 break;
321 } 376 }
322 case DISCOVERY_IDLE: { 377 case DISCOVERY_IDLE: {
323 mItemChooserDialog.setIdleState(); 378 mItemChooserDialog.setIdleState();
324 break; 379 break;
325 } 380 }
326 default: { 381 default: {
327 // TODO(jyasskin): Report the new state to the user. 382 // TODO(jyasskin): Report the new state to the user.
328 break; 383 break;
329 } 384 }
330 } 385 }
331 } 386 }
332 387
333 @VisibleForTesting 388 @VisibleForTesting
334 native void nativeOnDialogFinished( 389 native void nativeOnDialogFinished(
335 long nativeBluetoothChooserAndroid, int eventType, String deviceId); 390 long nativeBluetoothChooserAndroid, int eventType, String deviceId);
336 @VisibleForTesting 391 @VisibleForTesting
337 native void nativeRestartSearch(long nativeBluetoothChooserAndroid); 392 native void nativeRestartSearch(long nativeBluetoothChooserAndroid);
338 // Help links. 393 // Help links.
339 @VisibleForTesting 394 @VisibleForTesting
340 native void nativeShowBluetoothOverviewLink(long nativeBluetoothChooserAndro id); 395 native void nativeShowBluetoothOverviewLink(long nativeBluetoothChooserAndro id);
341 @VisibleForTesting 396 @VisibleForTesting
342 native void nativeShowBluetoothAdapterOffLink(long nativeBluetoothChooserAnd roid); 397 native void nativeShowBluetoothAdapterOffLink(long nativeBluetoothChooserAnd roid);
343 @VisibleForTesting 398 @VisibleForTesting
344 native void nativeShowNeedLocationPermissionLink(long nativeBluetoothChooser Android); 399 native void nativeShowNeedLocationPermissionLink(long nativeBluetoothChooser Android);
345 } 400 }
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