Index: chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java |
index df15c1638c115883800da2309259c17a7c314a7d..83fa3b72047a2a21562f4328744244f8eda9adfe 100644 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java |
@@ -6,9 +6,11 @@ package org.chromium.chrome.browser; |
import android.Manifest; |
import android.app.Activity; |
+import android.content.BroadcastReceiver; |
import android.content.Context; |
import android.content.Intent; |
-import android.content.pm.PackageManager; |
+import android.content.IntentFilter; |
+import android.location.LocationManager; |
import android.text.SpannableString; |
import android.text.TextUtils; |
import android.view.View; |
@@ -28,6 +30,8 @@ import org.chromium.ui.text.SpanApplier.SpanInfo; |
* A dialog for picking available Bluetooth devices. This dialog is shown when a website requests to |
* pair with a certain class of Bluetooth devices (e.g. through a bluetooth.requestDevice Javascript |
* call). |
+ * |
+ * The dialog is shown by create() or show(), and always runs finishDialog() as it's closing. |
*/ |
public class BluetoothChooserDialog |
implements ItemChooserDialog.ItemSelectedCallback, WindowAndroid.PermissionCallback { |
@@ -62,12 +66,27 @@ public class BluetoothChooserDialog |
// A pointer back to the native part of the implementation for this dialog. |
long mNativeBluetoothChooserDialogPtr; |
+ @VisibleForTesting |
+ final BroadcastReceiver mLocationModeBroadcastReceiver = new BroadcastReceiver() { |
+ @Override |
+ public void onReceive(Context context, Intent intent) { |
+ if (!LocationManager.MODE_CHANGED_ACTION.equals(intent.getAction())) { |
+ return; |
+ } |
+ if (checkLocationServicesAndPermission()) { |
+ mItemChooserDialog.clear(); |
+ nativeRestartSearch(mNativeBluetoothChooserDialogPtr); |
+ } |
+ } |
+ }; |
+ |
// The type of link that is shown within the dialog. |
private enum LinkType { |
EXPLAIN_BLUETOOTH, |
ADAPTER_OFF, |
ADAPTER_OFF_HELP, |
REQUEST_LOCATION_PERMISSION, |
+ REQUEST_LOCATION_SERVICES, |
NEED_LOCATION_PERMISSION_HELP, |
RESTART_SEARCH, |
} |
@@ -130,30 +149,35 @@ public class BluetoothChooserDialog |
new ItemChooserDialog.ItemChooserLabels(title, searching, noneFound, |
statusIdleNoneFound, statusIdleSomeFound, positiveButton); |
mItemChooserDialog = new ItemChooserDialog(mActivity, this, labels); |
+ |
+ mActivity.registerReceiver(mLocationModeBroadcastReceiver, |
+ new IntentFilter(LocationManager.MODE_CHANGED_ACTION)); |
+ } |
+ |
+ // Called to report the dialog's results back to native code. |
+ private void finishDialog(int resultCode, String id) { |
+ mActivity.unregisterReceiver(mLocationModeBroadcastReceiver); |
+ if (mNativeBluetoothChooserDialogPtr != 0) { |
+ nativeOnDialogFinished(mNativeBluetoothChooserDialogPtr, resultCode, id); |
+ } |
} |
@Override |
public void onItemSelected(String id) { |
- if (mNativeBluetoothChooserDialogPtr != 0) { |
- if (id.isEmpty()) { |
- nativeOnDialogFinished( |
- mNativeBluetoothChooserDialogPtr, DIALOG_FINISHED_CANCELLED, ""); |
- } else { |
- nativeOnDialogFinished( |
- mNativeBluetoothChooserDialogPtr, DIALOG_FINISHED_SELECTED, id); |
- } |
+ if (id.isEmpty()) { |
+ finishDialog(DIALOG_FINISHED_CANCELLED, ""); |
+ } else { |
+ finishDialog(DIALOG_FINISHED_SELECTED, id); |
} |
} |
@Override |
public void onRequestPermissionsResult(String[] permissions, int[] grantResults) { |
- for (int i = 0; i < grantResults.length; i++) { |
+ for (int i = 0; i < permissions.length; i++) { |
if (permissions[i].equals(Manifest.permission.ACCESS_COARSE_LOCATION)) { |
- if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { |
+ if (checkLocationServicesAndPermission()) { |
mItemChooserDialog.clear(); |
nativeRestartSearch(mNativeBluetoothChooserDialogPtr); |
- } else { |
- checkLocationPermission(); |
} |
return; |
} |
@@ -161,25 +185,50 @@ public class BluetoothChooserDialog |
// If the location permission is not present, leave the currently-shown message in place. |
} |
- private void checkLocationPermission() { |
- if (LocationUtils.getInstance().hasAndroidLocationPermission(mActivity)) { |
- return; |
+ // Returns true if Location Services is on and Chrome has permission to see the user's location. |
+ private boolean checkLocationServicesAndPermission() { |
+ final boolean havePermission = |
+ LocationUtils.getInstance().hasAndroidLocationPermission(mActivity); |
+ final boolean locationServicesOn = |
+ LocationUtils.getInstance().isSystemLocationSettingEnabled(mActivity); |
+ |
+ if (!havePermission |
+ && !mWindowAndroid.canRequestPermission( |
+ Manifest.permission.ACCESS_COARSE_LOCATION)) { |
+ // Immediately close the dialog because the user has asked Chrome not to request the |
+ // location permission. |
+ finishDialog(DIALOG_FINISHED_DENIED_PERMISSION, ""); |
+ return false; |
} |
- if (!mWindowAndroid.canRequestPermission(Manifest.permission.ACCESS_COARSE_LOCATION)) { |
- if (mNativeBluetoothChooserDialogPtr != 0) { |
- nativeOnDialogFinished( |
- mNativeBluetoothChooserDialogPtr, DIALOG_FINISHED_DENIED_PERMISSION, ""); |
- return; |
+ // Compute the message to show the user. |
+ final SpanInfo permissionSpan = new SpanInfo("<permission_link>", "</permission_link>", |
+ new BluetoothClickableSpan(LinkType.REQUEST_LOCATION_PERMISSION, mActivity)); |
+ final SpanInfo servicesSpan = new SpanInfo("<services_link>", "</services_link>", |
+ new BluetoothClickableSpan(LinkType.REQUEST_LOCATION_SERVICES, mActivity)); |
+ final SpannableString needLocationMessage; |
+ if (havePermission) { |
+ if (locationServicesOn) { |
+ // We don't need to request anything. |
+ return true; |
+ } else { |
+ needLocationMessage = SpanApplier.applySpans( |
+ mActivity.getString(R.string.bluetooth_need_location_services_on), |
+ servicesSpan); |
+ } |
+ } else { |
+ if (locationServicesOn) { |
+ needLocationMessage = SpanApplier.applySpans( |
+ mActivity.getString(R.string.bluetooth_need_location_permission), |
+ permissionSpan); |
+ } else { |
+ needLocationMessage = SpanApplier.applySpans( |
+ mActivity.getString( |
+ R.string.bluetooth_need_location_permission_and_services_on), |
+ permissionSpan, servicesSpan); |
} |
} |
- SpannableString needLocationMessage = SpanApplier.applySpans( |
- mActivity.getString(R.string.bluetooth_need_location_permission), |
- new SpanInfo("<link>", "</link>", |
- new BluetoothClickableSpan( |
- LinkType.REQUEST_LOCATION_PERMISSION, mActivity))); |
- |
SpannableString needLocationStatus = SpanApplier.applySpans( |
mActivity.getString(R.string.bluetooth_need_location_permission_help), |
new SpanInfo("<link>", "</link>", |
@@ -187,6 +236,7 @@ public class BluetoothChooserDialog |
LinkType.NEED_LOCATION_PERMISSION_HELP, mActivity))); |
mItemChooserDialog.setErrorState(needLocationMessage, needLocationStatus); |
+ return false; |
} |
private class BluetoothClickableSpan extends NoUnderlineClickableSpan { |
@@ -233,6 +283,11 @@ public class BluetoothChooserDialog |
BluetoothChooserDialog.this); |
break; |
} |
+ case REQUEST_LOCATION_SERVICES: { |
+ mContext.startActivity( |
+ LocationUtils.getInstance().getSystemLocationSettingsIntent()); |
+ break; |
+ } |
case NEED_LOCATION_PERMISSION_HELP: { |
nativeShowNeedLocationPermissionLink(mNativeBluetoothChooserDialogPtr); |
closeDialog(); |
@@ -314,9 +369,10 @@ public class BluetoothChooserDialog |
void notifyDiscoveryState(int discoveryState) { |
switch (discoveryState) { |
case DISCOVERY_FAILED_TO_START: { |
- // FAILED_TO_START might be caused by a missing Location permission. |
+ // FAILED_TO_START might be caused by a missing Location |
+ // permission or by the Location service being turned off. |
// Check, and show a request if so. |
- checkLocationPermission(); |
+ checkLocationServicesAndPermission(); |
break; |
} |
case DISCOVERY_IDLE: { |