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; | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |