Chromium Code Reviews| OLD | NEW |
|---|---|
| 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; | |
| 14 import android.provider.Settings; | |
| 12 import android.text.SpannableString; | 15 import android.text.SpannableString; |
| 13 import android.text.TextUtils; | 16 import android.text.TextUtils; |
| 14 import android.view.View; | 17 import android.view.View; |
| 15 | 18 |
| 16 import org.chromium.base.VisibleForTesting; | 19 import org.chromium.base.VisibleForTesting; |
| 17 import org.chromium.base.annotations.CalledByNative; | 20 import org.chromium.base.annotations.CalledByNative; |
| 18 import org.chromium.chrome.R; | 21 import org.chromium.chrome.R; |
| 19 import org.chromium.chrome.browser.omnibox.OmniboxUrlEmphasizer; | 22 import org.chromium.chrome.browser.omnibox.OmniboxUrlEmphasizer; |
| 23 import org.chromium.chrome.browser.preferences.LocationSettings; | |
| 20 import org.chromium.chrome.browser.profiles.Profile; | 24 import org.chromium.chrome.browser.profiles.Profile; |
| 21 import org.chromium.ui.base.WindowAndroid; | 25 import org.chromium.ui.base.WindowAndroid; |
| 22 import org.chromium.ui.text.NoUnderlineClickableSpan; | 26 import org.chromium.ui.text.NoUnderlineClickableSpan; |
| 23 import org.chromium.ui.text.SpanApplier; | 27 import org.chromium.ui.text.SpanApplier; |
| 24 import org.chromium.ui.text.SpanApplier.SpanInfo; | 28 import org.chromium.ui.text.SpanApplier.SpanInfo; |
| 25 | 29 |
| 26 /** | 30 /** |
| 27 * A dialog for picking available Bluetooth devices. This dialog is shown when a website requests to | 31 * A dialog for picking available Bluetooth devices. This dialog is shown when a website requests to |
| 28 * pair with a certain class of Bluetooth devices (e.g. through a bluetooth.requ estDevice Javascript | 32 * pair with a certain class of Bluetooth devices (e.g. through a bluetooth.requ estDevice Javascript |
| 29 * call). | 33 * call). |
| 34 * | |
| 35 * The dialog is shown by create() or show(), and always runs finishDialog() as it's closing. | |
| 30 */ | 36 */ |
| 31 public class BluetoothChooserDialog | 37 public class BluetoothChooserDialog |
| 32 implements ItemChooserDialog.ItemSelectedCallback, WindowAndroid.Permiss ionCallback { | 38 implements ItemChooserDialog.ItemSelectedCallback, WindowAndroid.Permiss ionCallback { |
| 33 // These constants match BluetoothChooserAndroid::ShowDiscoveryState, and ar e used in | 39 // These constants match BluetoothChooserAndroid::ShowDiscoveryState, and ar e used in |
| 34 // notifyDiscoveryState(). | 40 // notifyDiscoveryState(). |
| 35 static final int DISCOVERY_FAILED_TO_START = 0; | 41 static final int DISCOVERY_FAILED_TO_START = 0; |
| 36 static final int DISCOVERING = 1; | 42 static final int DISCOVERING = 1; |
| 37 static final int DISCOVERY_IDLE = 2; | 43 static final int DISCOVERY_IDLE = 2; |
| 38 | 44 |
| 39 // Values passed to nativeOnDialogFinished:eventType, and only used in the n ative function. | 45 // Values passed to nativeOnDialogFinished:eventType, and only used in the n ative function. |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 60 | 66 |
| 61 // A pointer back to the native part of the implementation for this dialog. | 67 // A pointer back to the native part of the implementation for this dialog. |
| 62 long mNativeBluetoothChooserDialogPtr; | 68 long mNativeBluetoothChooserDialogPtr; |
| 63 | 69 |
| 64 // The type of link that is shown within the dialog. | 70 // The type of link that is shown within the dialog. |
| 65 private enum LinkType { | 71 private enum LinkType { |
| 66 EXPLAIN_BLUETOOTH, | 72 EXPLAIN_BLUETOOTH, |
| 67 ADAPTER_OFF, | 73 ADAPTER_OFF, |
| 68 ADAPTER_OFF_HELP, | 74 ADAPTER_OFF_HELP, |
| 69 REQUEST_LOCATION_PERMISSION, | 75 REQUEST_LOCATION_PERMISSION, |
| 76 REQUEST_LOCATION_SERVICES, | |
| 70 NEED_LOCATION_PERMISSION_HELP, | 77 NEED_LOCATION_PERMISSION_HELP, |
| 71 RESTART_SEARCH, | 78 RESTART_SEARCH, |
| 72 } | 79 } |
| 73 | 80 |
| 74 /** | 81 /** |
| 75 * Creates the BluetoothChooserDialog. | 82 * Creates the BluetoothChooserDialog. |
| 76 */ | 83 */ |
| 77 @VisibleForTesting | 84 @VisibleForTesting |
| 78 BluetoothChooserDialog(WindowAndroid windowAndroid, String origin, int secur ityLevel, | 85 BluetoothChooserDialog(WindowAndroid windowAndroid, String origin, int secur ityLevel, |
| 79 long nativeBluetoothChooserDialogPtr) { | 86 long nativeBluetoothChooserDialogPtr) { |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 122 mActivity.getString(R.string.bluetooth_not_seeing_it_idle_some_f ound), | 129 mActivity.getString(R.string.bluetooth_not_seeing_it_idle_some_f ound), |
| 123 new SpanInfo("<link1>", "</link1>", | 130 new SpanInfo("<link1>", "</link1>", |
| 124 new BluetoothClickableSpan(LinkType.EXPLAIN_BLUETOOTH, m Activity)), | 131 new BluetoothClickableSpan(LinkType.EXPLAIN_BLUETOOTH, m Activity)), |
| 125 new SpanInfo("<link2>", "</link2>", | 132 new SpanInfo("<link2>", "</link2>", |
| 126 new BluetoothClickableSpan(LinkType.RESTART_SEARCH, mAct ivity))); | 133 new BluetoothClickableSpan(LinkType.RESTART_SEARCH, mAct ivity))); |
| 127 | 134 |
| 128 ItemChooserDialog.ItemChooserLabels labels = | 135 ItemChooserDialog.ItemChooserLabels labels = |
| 129 new ItemChooserDialog.ItemChooserLabels(title, searching, noneFo und, | 136 new ItemChooserDialog.ItemChooserLabels(title, searching, noneFo und, |
| 130 statusIdleNoneFound, statusIdleSomeFound, positiveButton ); | 137 statusIdleNoneFound, statusIdleSomeFound, positiveButton ); |
| 131 mItemChooserDialog = new ItemChooserDialog(mActivity, this, labels); | 138 mItemChooserDialog = new ItemChooserDialog(mActivity, this, labels); |
| 139 | |
| 140 mActivity.registerReceiver(mLocationModeBroadcastReceiver, | |
| 141 new IntentFilter(LocationManager.MODE_CHANGED_ACTION)); | |
| 142 } | |
| 143 | |
| 144 // Called to report the dialog's results back to native code. | |
| 145 private void finishDialog(int resultCode, String id) { | |
| 146 mActivity.unregisterReceiver(mLocationModeBroadcastReceiver); | |
| 147 if (mNativeBluetoothChooserDialogPtr != 0) { | |
| 148 nativeOnDialogFinished(mNativeBluetoothChooserDialogPtr, resultCode, id); | |
| 149 } | |
| 132 } | 150 } |
| 133 | 151 |
| 134 @Override | 152 @Override |
| 135 public void onItemSelected(String id) { | 153 public void onItemSelected(String id) { |
| 136 if (mNativeBluetoothChooserDialogPtr != 0) { | 154 if (id.isEmpty()) { |
| 137 if (id.isEmpty()) { | 155 finishDialog(DIALOG_FINISHED_CANCELLED, ""); |
| 138 nativeOnDialogFinished( | 156 } else { |
| 139 mNativeBluetoothChooserDialogPtr, DIALOG_FINISHED_CANCEL LED, ""); | 157 finishDialog(DIALOG_FINISHED_SELECTED, id); |
| 140 } else { | |
| 141 nativeOnDialogFinished( | |
| 142 mNativeBluetoothChooserDialogPtr, DIALOG_FINISHED_SELECT ED, id); | |
| 143 } | |
| 144 } | 158 } |
| 145 } | 159 } |
| 146 | 160 |
| 147 @Override | 161 @Override |
| 148 public void onRequestPermissionsResult(String[] permissions, int[] grantResu lts) { | 162 public void onRequestPermissionsResult(String[] permissions, int[] grantResu lts) { |
| 149 for (int i = 0; i < grantResults.length; i++) { | 163 for (int i = 0; i < permissions.length; i++) { |
| 150 if (permissions[i].equals(Manifest.permission.ACCESS_COARSE_LOCATION )) { | 164 if (permissions[i].equals(Manifest.permission.ACCESS_COARSE_LOCATION )) { |
| 151 if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { | 165 if (checkLocationServicesAndPermission()) { |
| 152 mItemChooserDialog.clear(); | 166 mItemChooserDialog.clear(); |
| 153 nativeRestartSearch(mNativeBluetoothChooserDialogPtr); | 167 nativeRestartSearch(mNativeBluetoothChooserDialogPtr); |
| 154 } else { | |
| 155 checkLocationPermission(); | |
| 156 } | 168 } |
| 157 return; | 169 return; |
| 158 } | 170 } |
| 159 } | 171 } |
| 160 // If the location permission is not present, leave the currently-shown message in place. | 172 // If the location permission is not present, leave the currently-shown message in place. |
| 161 } | 173 } |
| 162 | 174 |
| 163 private void checkLocationPermission() { | 175 @VisibleForTesting |
| 164 if (mWindowAndroid.hasPermission(Manifest.permission.ACCESS_COARSE_LOCAT ION) | 176 final BroadcastReceiver mLocationModeBroadcastReceiver = new BroadcastReceiv er() { |
| 165 || mWindowAndroid.hasPermission(Manifest.permission.ACCESS_FINE_ LOCATION)) { | 177 @Override |
| 166 return; | 178 public void onReceive(Context context, Intent intent) { |
| 179 if (LocationManager.MODE_CHANGED_ACTION.equals(intent.getAction())) { | |
| 180 if (checkLocationServicesAndPermission()) { | |
| 181 mItemChooserDialog.clear(); | |
| 182 nativeRestartSearch(mNativeBluetoothChooserDialogPtr); | |
| 183 } | |
| 184 } | |
| 185 } | |
| 186 }; | |
| 187 | |
| 188 // Returns true if Location Services is on and Chrome has permission to see the user's location. | |
| 189 private boolean checkLocationServicesAndPermission() { | |
| 190 final boolean havePermission = | |
| 191 mWindowAndroid.hasPermission(Manifest.permission.ACCESS_COARSE_L OCATION) | |
| 192 || mWindowAndroid.hasPermission(Manifest.permission.ACCESS_FINE_ LOCATION); | |
| 193 final boolean locationServicesOn = | |
| 194 LocationSettings.getInstance().isSystemLocationSettingEnabled(); | |
|
Jeffrey Yasskin
2016/06/03 01:52:48
It's a little odd for this chooser to call into pr
Finnur
2016/06/03 02:25:04
These questions are probably more appropriate for
Ted C
2016/06/07 00:30:14
It seems to only be used in preferences right now.
| |
| 195 | |
| 196 if (!havePermission | |
| 197 && !mWindowAndroid.canRequestPermission( | |
| 198 Manifest.permission.ACCESS_COARSE_LOCATION)) { | |
| 199 // Immediately close the dialog because the user has asked Chrome no t to request the | |
| 200 // location permission. | |
| 201 finishDialog(DIALOG_FINISHED_DENIED_PERMISSION, ""); | |
| 202 return false; | |
| 167 } | 203 } |
| 168 | 204 |
| 169 if (!mWindowAndroid.canRequestPermission(Manifest.permission.ACCESS_COAR SE_LOCATION)) { | 205 // Compute the message to show the user. |
| 170 if (mNativeBluetoothChooserDialogPtr != 0) { | 206 final SpanInfo permissionSpan = new SpanInfo("<permission_link>", "</per mission_link>", |
| 171 nativeOnDialogFinished( | 207 new BluetoothClickableSpan(LinkType.REQUEST_LOCATION_PERMISSION, mActivity)); |
| 172 mNativeBluetoothChooserDialogPtr, DIALOG_FINISHED_DENIED _PERMISSION, ""); | 208 final SpanInfo servicesSpan = new SpanInfo("<services_link>", "</service s_link>", |
| 173 return; | 209 new BluetoothClickableSpan(LinkType.REQUEST_LOCATION_SERVICES, m Activity)); |
| 210 final SpannableString needLocationMessage; | |
| 211 if (havePermission) { | |
| 212 if (locationServicesOn) { | |
| 213 // We don't need to request anything. | |
| 214 return true; | |
| 215 } else { | |
| 216 needLocationMessage = SpanApplier.applySpans( | |
| 217 mActivity.getString(R.string.bluetooth_need_location_ser vices_on), | |
| 218 servicesSpan); | |
| 219 } | |
| 220 } else { | |
| 221 if (locationServicesOn) { | |
| 222 needLocationMessage = SpanApplier.applySpans( | |
| 223 mActivity.getString(R.string.bluetooth_need_location_per mission), | |
| 224 permissionSpan); | |
| 225 } else { | |
| 226 needLocationMessage = SpanApplier.applySpans( | |
| 227 mActivity.getString( | |
| 228 R.string.bluetooth_need_location_permission_and_ services_on), | |
| 229 permissionSpan, servicesSpan); | |
| 174 } | 230 } |
| 175 } | 231 } |
| 176 | 232 |
| 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( | 233 SpannableString needLocationStatus = SpanApplier.applySpans( |
| 184 mActivity.getString(R.string.bluetooth_need_location_permission_ help), | 234 mActivity.getString(R.string.bluetooth_need_location_permission_ help), |
| 185 new SpanInfo("<link>", "</link>", | 235 new SpanInfo("<link>", "</link>", |
| 186 new BluetoothClickableSpan( | 236 new BluetoothClickableSpan( |
| 187 LinkType.NEED_LOCATION_PERMISSION_HELP, mAc tivity))); | 237 LinkType.NEED_LOCATION_PERMISSION_HELP, mAc tivity))); |
| 188 | 238 |
| 189 mItemChooserDialog.setErrorState(needLocationMessage, needLocationStatus ); | 239 mItemChooserDialog.setErrorState(needLocationMessage, needLocationStatus ); |
| 240 return false; | |
| 190 } | 241 } |
| 191 | 242 |
| 192 private class BluetoothClickableSpan extends NoUnderlineClickableSpan { | 243 private class BluetoothClickableSpan extends NoUnderlineClickableSpan { |
| 193 // The type of link this span represents. | 244 // The type of link this span represents. |
| 194 private LinkType mLinkType; | 245 private LinkType mLinkType; |
| 195 | 246 |
| 196 private Context mContext; | 247 private Context mContext; |
| 197 | 248 |
| 198 BluetoothClickableSpan(LinkType linkType, Context context) { | 249 BluetoothClickableSpan(LinkType linkType, Context context) { |
| 199 mLinkType = linkType; | 250 mLinkType = linkType; |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 210 case EXPLAIN_BLUETOOTH: { | 261 case EXPLAIN_BLUETOOTH: { |
| 211 // No need to close the dialog here because | 262 // No need to close the dialog here because |
| 212 // ShowBluetoothOverviewLink will close it. | 263 // ShowBluetoothOverviewLink will close it. |
| 213 // TODO(ortuno): The BluetoothChooserDialog should dismiss | 264 // TODO(ortuno): The BluetoothChooserDialog should dismiss |
| 214 // itself when a new tab is opened or the current tab naviga tes. | 265 // itself when a new tab is opened or the current tab naviga tes. |
| 215 // https://crbug.com/588127 | 266 // https://crbug.com/588127 |
| 216 nativeShowBluetoothOverviewLink(mNativeBluetoothChooserDialo gPtr); | 267 nativeShowBluetoothOverviewLink(mNativeBluetoothChooserDialo gPtr); |
| 217 break; | 268 break; |
| 218 } | 269 } |
| 219 case ADAPTER_OFF: { | 270 case ADAPTER_OFF: { |
| 220 Intent intent = new Intent(); | 271 mContext.startActivity( |
| 221 intent.setAction(android.provider.Settings.ACTION_BLUETOOTH_ SETTINGS); | 272 new Intent(android.provider.Settings.ACTION_BLUETOOT H_SETTINGS)); |
| 222 mContext.startActivity(intent); | |
| 223 break; | 273 break; |
| 224 } | 274 } |
| 225 case ADAPTER_OFF_HELP: { | 275 case ADAPTER_OFF_HELP: { |
| 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(new Intent(Settings.ACTION_LOCATION_S OURCE_SETTINGS)); | |
| 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 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 310 if (mNativeBluetoothChooserDialogPtr != 0) { | 364 if (mNativeBluetoothChooserDialogPtr != 0) { |
| 311 nativeRestartSearch(mNativeBluetoothChooserDialogPtr); | 365 nativeRestartSearch(mNativeBluetoothChooserDialogPtr); |
| 312 } | 366 } |
| 313 } | 367 } |
| 314 | 368 |
| 315 @VisibleForTesting | 369 @VisibleForTesting |
| 316 @CalledByNative | 370 @CalledByNative |
| 317 void notifyDiscoveryState(int discoveryState) { | 371 void notifyDiscoveryState(int discoveryState) { |
| 318 switch (discoveryState) { | 372 switch (discoveryState) { |
| 319 case DISCOVERY_FAILED_TO_START: { | 373 case DISCOVERY_FAILED_TO_START: { |
| 320 // FAILED_TO_START might be caused by a missing Location permiss ion. | 374 // FAILED_TO_START might be caused by a missing Location |
| 375 // permission or by the Location service being turned off. | |
| 321 // Check, and show a request if so. | 376 // Check, and show a request if so. |
| 322 checkLocationPermission(); | 377 checkLocationServicesAndPermission(); |
| 323 break; | 378 break; |
| 324 } | 379 } |
| 325 case DISCOVERY_IDLE: { | 380 case DISCOVERY_IDLE: { |
| 326 mItemChooserDialog.setIdleState(); | 381 mItemChooserDialog.setIdleState(); |
| 327 break; | 382 break; |
| 328 } | 383 } |
| 329 default: { | 384 default: { |
| 330 // TODO(jyasskin): Report the new state to the user. | 385 // TODO(jyasskin): Report the new state to the user. |
| 331 break; | 386 break; |
| 332 } | 387 } |
| 333 } | 388 } |
| 334 } | 389 } |
| 335 | 390 |
| 336 @VisibleForTesting | 391 @VisibleForTesting |
| 337 native void nativeOnDialogFinished( | 392 native void nativeOnDialogFinished( |
| 338 long nativeBluetoothChooserAndroid, int eventType, String deviceId); | 393 long nativeBluetoothChooserAndroid, int eventType, String deviceId); |
| 339 @VisibleForTesting | 394 @VisibleForTesting |
| 340 native void nativeRestartSearch(long nativeBluetoothChooserAndroid); | 395 native void nativeRestartSearch(long nativeBluetoothChooserAndroid); |
| 341 // Help links. | 396 // Help links. |
| 342 @VisibleForTesting | 397 @VisibleForTesting |
| 343 native void nativeShowBluetoothOverviewLink(long nativeBluetoothChooserAndro id); | 398 native void nativeShowBluetoothOverviewLink(long nativeBluetoothChooserAndro id); |
| 344 @VisibleForTesting | 399 @VisibleForTesting |
| 345 native void nativeShowBluetoothAdapterOffLink(long nativeBluetoothChooserAnd roid); | 400 native void nativeShowBluetoothAdapterOffLink(long nativeBluetoothChooserAnd roid); |
| 346 @VisibleForTesting | 401 @VisibleForTesting |
| 347 native void nativeShowNeedLocationPermissionLink(long nativeBluetoothChooser Android); | 402 native void nativeShowNeedLocationPermissionLink(long nativeBluetoothChooser Android); |
| 348 } | 403 } |
| OLD | NEW |