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.content.Context; | 8 import android.content.Context; |
| 9 import android.content.Intent; | 9 import android.content.Intent; |
| 10 import android.content.pm.PackageManager; | 10 import android.content.pm.PackageManager; |
| 11 import android.graphics.Color; | |
| 12 import android.text.SpannableString; | 11 import android.text.SpannableString; |
| 13 import android.text.TextPaint; | |
| 14 import android.text.TextUtils; | 12 import android.text.TextUtils; |
| 15 import android.text.style.ClickableSpan; | |
| 16 import android.view.View; | 13 import android.view.View; |
| 17 | 14 |
| 18 import org.chromium.base.VisibleForTesting; | 15 import org.chromium.base.VisibleForTesting; |
| 19 import org.chromium.base.annotations.CalledByNative; | 16 import org.chromium.base.annotations.CalledByNative; |
| 20 import org.chromium.chrome.R; | 17 import org.chromium.chrome.R; |
| 21 import org.chromium.chrome.browser.omnibox.OmniboxUrlEmphasizer; | 18 import org.chromium.chrome.browser.omnibox.OmniboxUrlEmphasizer; |
| 22 import org.chromium.chrome.browser.profiles.Profile; | 19 import org.chromium.chrome.browser.profiles.Profile; |
| 23 import org.chromium.ui.base.WindowAndroid; | 20 import org.chromium.ui.base.WindowAndroid; |
| 21 import org.chromium.ui.text.NoUnderlineClickableSpan; | |
| 24 import org.chromium.ui.text.SpanApplier; | 22 import org.chromium.ui.text.SpanApplier; |
| 25 import org.chromium.ui.text.SpanApplier.SpanInfo; | 23 import org.chromium.ui.text.SpanApplier.SpanInfo; |
| 26 | 24 |
| 27 import java.util.ArrayList; | |
| 28 import java.util.List; | |
| 29 | |
| 30 /** | 25 /** |
| 31 * A dialog for picking available Bluetooth devices. This dialog is shown when a website requests to | 26 * A dialog for picking available Bluetooth devices. This dialog is shown when a website requests to |
| 32 * pair with a certain class of Bluetooth devices (e.g. through a bluetooth.requ estDevice Javascript | 27 * pair with a certain class of Bluetooth devices (e.g. through a bluetooth.requ estDevice Javascript |
| 33 * call). | 28 * call). |
| 34 */ | 29 */ |
| 35 public class BluetoothChooserDialog | 30 public class BluetoothChooserDialog |
| 36 implements ItemChooserDialog.ItemSelectedCallback, WindowAndroid.Permiss ionCallback { | 31 implements ItemChooserDialog.ItemSelectedCallback, WindowAndroid.Permiss ionCallback { |
| 37 // These constants match BluetoothChooserAndroid::ShowDiscoveryState, and ar e used in | 32 // These constants match BluetoothChooserAndroid::ShowDiscoveryState, and ar e used in |
| 38 // notifyDiscoveryState(). | 33 // notifyDiscoveryState(). |
| 39 static final int DISCOVERY_FAILED_TO_START = 0; | 34 static final int DISCOVERY_FAILED_TO_START = 0; |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 69 private enum LinkType { | 64 private enum LinkType { |
| 70 EXPLAIN_BLUETOOTH, | 65 EXPLAIN_BLUETOOTH, |
| 71 ADAPTER_OFF, | 66 ADAPTER_OFF, |
| 72 ADAPTER_OFF_HELP, | 67 ADAPTER_OFF_HELP, |
| 73 REQUEST_LOCATION_PERMISSION, | 68 REQUEST_LOCATION_PERMISSION, |
| 74 NEED_LOCATION_PERMISSION_HELP, | 69 NEED_LOCATION_PERMISSION_HELP, |
| 75 RESTART_SEARCH, | 70 RESTART_SEARCH, |
| 76 } | 71 } |
| 77 | 72 |
| 78 /** | 73 /** |
| 79 * Creates the BluetoothChooserDialog and displays it (and starts waiting fo r data). | 74 * Creates the BluetoothChooserDialog. |
| 80 * | |
| 81 * @param context Context which is used for launching a dialog. | |
| 82 */ | 75 */ |
| 83 @VisibleForTesting | 76 @VisibleForTesting |
| 84 BluetoothChooserDialog(WindowAndroid windowAndroid, String origin, int secur ityLevel, | 77 BluetoothChooserDialog(WindowAndroid windowAndroid, String origin, int secur ityLevel, |
| 85 long nativeBluetoothChooserDialogPtr) { | 78 long nativeBluetoothChooserDialogPtr) { |
| 86 mWindowAndroid = windowAndroid; | 79 mWindowAndroid = windowAndroid; |
| 87 mContext = windowAndroid.getActivity().get(); | 80 mContext = windowAndroid.getActivity().get(); |
| 88 assert mContext != null; | 81 assert mContext != null; |
| 89 mOrigin = origin; | 82 mOrigin = origin; |
| 90 mSecurityLevel = securityLevel; | 83 mSecurityLevel = securityLevel; |
| 91 mNativeBluetoothChooserDialogPtr = nativeBluetoothChooserDialogPtr; | 84 mNativeBluetoothChooserDialogPtr = nativeBluetoothChooserDialogPtr; |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 103 origin, mContext.getResources(), profile, mSecurityLevel, false, true, true); | 96 origin, mContext.getResources(), profile, mSecurityLevel, false, true, true); |
| 104 // Construct a full string and replace the origin text with emphasized v ersion. | 97 // Construct a full string and replace the origin text with emphasized v ersion. |
| 105 SpannableString title = | 98 SpannableString title = |
| 106 new SpannableString(mContext.getString(R.string.bluetooth_dialog _title, mOrigin)); | 99 new SpannableString(mContext.getString(R.string.bluetooth_dialog _title, mOrigin)); |
| 107 int start = title.toString().indexOf(mOrigin); | 100 int start = title.toString().indexOf(mOrigin); |
| 108 TextUtils.copySpansFrom(origin, 0, origin.length(), Object.class, title, start); | 101 TextUtils.copySpansFrom(origin, 0, origin.length(), Object.class, title, start); |
| 109 | 102 |
| 110 String message = mContext.getString(R.string.bluetooth_not_found); | 103 String message = mContext.getString(R.string.bluetooth_not_found); |
| 111 SpannableString noneFound = SpanApplier.applySpans( | 104 SpannableString noneFound = SpanApplier.applySpans( |
| 112 message, new SpanInfo("<link>", "</link>", | 105 message, new SpanInfo("<link>", "</link>", |
| 113 new NoUnderlineClickableSpan(LinkType.RESTART_SEARCH, mC ontext))); | 106 new BluetoothClickableSpan(LinkType.RESTART_SEARCH, mCon text))); |
| 114 | 107 |
| 115 SpannableString searching = SpanApplier.applySpans( | 108 SpannableString searching = SpanApplier.applySpans( |
| 116 mContext.getString(R.string.bluetooth_searching), | 109 mContext.getString(R.string.bluetooth_searching), |
| 117 new SpanInfo("<link>", "</link>", | 110 new SpanInfo("<link>", "</link>", |
|
newt (away)
2016/03/17 01:53:20
nit: The indentation was correct to begin with (be
juncai
2016/03/17 18:21:35
Done.
| |
| 118 new NoUnderlineClickableSpan(LinkType.EXPLAIN_BLUETOOTH, mContext))); | 111 new BluetoothClickableSpan(LinkType.EXPLAIN_BLUE TOOTH, mContext))); |
| 119 | 112 |
| 120 String positiveButton = mContext.getString(R.string.bluetooth_confirm_bu tton); | 113 String positiveButton = mContext.getString(R.string.bluetooth_confirm_bu tton); |
| 121 | 114 |
| 122 SpannableString statusActive = SpanApplier.applySpans( | |
| 123 mContext.getString(R.string.bluetooth_not_seeing_it), | |
| 124 new SpanInfo("<link>", "</link>", | |
| 125 new NoUnderlineClickableSpan(LinkType.EXPLAIN_BLUETOOTH, mContext))); | |
| 126 | |
| 127 SpannableString statusIdleNoneFound = SpanApplier.applySpans( | 115 SpannableString statusIdleNoneFound = SpanApplier.applySpans( |
| 128 mContext.getString(R.string.bluetooth_not_seeing_it_idle_none_fo und), | 116 mContext.getString(R.string.bluetooth_not_seeing_it_idle_none_fo und), |
| 129 new SpanInfo("<link>", "</link>", | 117 new SpanInfo("<link>", "</link>", |
| 130 new NoUnderlineClickableSpan(LinkType.EXPLAIN_BLUETOOTH, mContext))); | 118 new BluetoothClickableSpan(LinkType.EXPLAIN_BLUE TOOTH, mContext))); |
| 131 | 119 |
| 132 SpannableString statusIdleSomeFound = SpanApplier.applySpans( | 120 SpannableString statusIdleSomeFound = SpanApplier.applySpans( |
| 133 mContext.getString(R.string.bluetooth_not_seeing_it_idle_some_fo und), | 121 mContext.getString(R.string.bluetooth_not_seeing_it_idle_some_fo und), |
| 134 new SpanInfo("<link1>", "</link1>", | 122 new SpanInfo("<link1>", "</link1>", |
| 135 new NoUnderlineClickableSpan(LinkType.EXPLAIN_BLUETOOTH, mContext)), | 123 new BluetoothClickableSpan(LinkType.EXPLAIN_BLUETOOTH, m Context)), |
| 136 new SpanInfo("<link2>", "</link2>", | 124 new SpanInfo("<link2>", "</link2>", |
| 137 new NoUnderlineClickableSpan(LinkType.RESTART_SEARCH, mC ontext))); | 125 new BluetoothClickableSpan(LinkType.RESTART_SEARCH, mCon text))); |
| 138 | 126 |
| 139 ItemChooserDialog.ItemChooserLabels labels = | 127 ItemChooserDialog.ItemChooserLabels labels = |
| 140 new ItemChooserDialog.ItemChooserLabels(title, searching, noneFo und, statusActive, | 128 new ItemChooserDialog.ItemChooserLabels(title, searching, noneFo und, |
| 141 statusIdleNoneFound, statusIdleSomeFound, positiveButton ); | 129 statusIdleNoneFound, statusIdleSomeFound, positiveButton ); |
| 142 mItemChooserDialog = new ItemChooserDialog(mContext, this, labels); | 130 mItemChooserDialog = new ItemChooserDialog(mContext, this, labels); |
| 143 } | 131 } |
| 144 | 132 |
| 145 @Override | 133 @Override |
| 146 public void onItemSelected(String id) { | 134 public void onItemSelected(String id) { |
| 147 if (mNativeBluetoothChooserDialogPtr != 0) { | 135 if (mNativeBluetoothChooserDialogPtr != 0) { |
| 148 if (id.isEmpty()) { | 136 if (id.isEmpty()) { |
| 149 nativeOnDialogFinished( | 137 nativeOnDialogFinished( |
| 150 mNativeBluetoothChooserDialogPtr, DIALOG_FINISHED_CANCEL LED, ""); | 138 mNativeBluetoothChooserDialogPtr, DIALOG_FINISHED_CANCEL LED, ""); |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 181 if (mNativeBluetoothChooserDialogPtr != 0) { | 169 if (mNativeBluetoothChooserDialogPtr != 0) { |
| 182 nativeOnDialogFinished( | 170 nativeOnDialogFinished( |
| 183 mNativeBluetoothChooserDialogPtr, DIALOG_FINISHED_DENIED _PERMISSION, ""); | 171 mNativeBluetoothChooserDialogPtr, DIALOG_FINISHED_DENIED _PERMISSION, ""); |
| 184 return; | 172 return; |
| 185 } | 173 } |
| 186 } | 174 } |
| 187 | 175 |
| 188 SpannableString needLocationMessage = SpanApplier.applySpans( | 176 SpannableString needLocationMessage = SpanApplier.applySpans( |
| 189 mContext.getString(R.string.bluetooth_need_location_permission), | 177 mContext.getString(R.string.bluetooth_need_location_permission), |
| 190 new SpanInfo("<link>", "</link>", | 178 new SpanInfo("<link>", "</link>", |
| 191 new NoUnderlineClickableSpan( | 179 new BluetoothClickableSpan( |
| 192 LinkType.REQUEST_LOCATION_PERMISSION, mCont ext))); | 180 LinkType.REQUEST_LOCATION_PERMISSION, mCont ext))); |
| 193 | 181 |
| 194 SpannableString needLocationStatus = SpanApplier.applySpans( | 182 SpannableString needLocationStatus = SpanApplier.applySpans( |
| 195 mContext.getString(R.string.bluetooth_need_location_permission_h elp), | 183 mContext.getString(R.string.bluetooth_need_location_permission_h elp), |
| 196 new SpanInfo("<link>", "</link>", | 184 new SpanInfo("<link>", "</link>", |
| 197 new NoUnderlineClickableSpan( | 185 new BluetoothClickableSpan( |
| 198 LinkType.NEED_LOCATION_PERMISSION_HELP, mCo ntext))); | 186 LinkType.NEED_LOCATION_PERMISSION_HELP, mCo ntext))); |
| 199 | 187 |
| 200 mItemChooserDialog.setErrorState(needLocationMessage, needLocationStatus ); | 188 mItemChooserDialog.setErrorState(needLocationMessage, needLocationStatus ); |
| 201 } | 189 } |
| 202 | 190 |
| 203 /** | 191 private class BluetoothClickableSpan extends NoUnderlineClickableSpan { |
| 204 * A helper class to show a clickable link with underlines turned off. | |
| 205 */ | |
| 206 private class NoUnderlineClickableSpan extends ClickableSpan { | |
| 207 // The type of link this span represents. | 192 // The type of link this span represents. |
| 208 private LinkType mLinkType; | 193 private LinkType mLinkType; |
| 209 | 194 |
| 210 private Context mContext; | 195 private Context mContext; |
| 211 | 196 |
| 212 NoUnderlineClickableSpan(LinkType linkType, Context context) { | 197 BluetoothClickableSpan(LinkType linkType, Context context) { |
| 213 mLinkType = linkType; | 198 mLinkType = linkType; |
| 214 mContext = context; | 199 mContext = context; |
| 215 } | 200 } |
| 216 | 201 |
| 217 @Override | 202 @Override |
| 218 public void onClick(View view) { | 203 public void onClick(View view) { |
| 219 if (mNativeBluetoothChooserDialogPtr == 0) { | 204 if (mNativeBluetoothChooserDialogPtr == 0) { |
| 220 return; | 205 return; |
| 221 } | 206 } |
| 222 | 207 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 257 nativeRestartSearch(mNativeBluetoothChooserDialogPtr); | 242 nativeRestartSearch(mNativeBluetoothChooserDialogPtr); |
| 258 break; | 243 break; |
| 259 } | 244 } |
| 260 default: | 245 default: |
| 261 assert false; | 246 assert false; |
| 262 } | 247 } |
| 263 | 248 |
| 264 // Get rid of the highlight background on selection. | 249 // Get rid of the highlight background on selection. |
| 265 view.invalidate(); | 250 view.invalidate(); |
| 266 } | 251 } |
| 267 | |
| 268 @Override | |
| 269 public void updateDrawState(TextPaint textPaint) { | |
| 270 super.updateDrawState(textPaint); | |
| 271 textPaint.bgColor = Color.TRANSPARENT; | |
| 272 textPaint.setUnderlineText(false); | |
| 273 } | |
| 274 } | 252 } |
| 275 | 253 |
| 276 @CalledByNative | 254 @CalledByNative |
| 277 private static BluetoothChooserDialog create(WindowAndroid windowAndroid, St ring origin, | 255 private static BluetoothChooserDialog create(WindowAndroid windowAndroid, St ring origin, |
| 278 int securityLevel, long nativeBluetoothChooserDialogPtr) { | 256 int securityLevel, long nativeBluetoothChooserDialogPtr) { |
| 279 if (!windowAndroid.hasPermission(Manifest.permission.ACCESS_COARSE_LOCAT ION) | 257 if (!windowAndroid.hasPermission(Manifest.permission.ACCESS_COARSE_LOCAT ION) |
| 280 && !windowAndroid.hasPermission(Manifest.permission.ACCESS_FINE_ LOCATION) | 258 && !windowAndroid.hasPermission(Manifest.permission.ACCESS_FINE_ LOCATION) |
| 281 && !windowAndroid.canRequestPermission( | 259 && !windowAndroid.canRequestPermission( |
| 282 Manifest.permission.ACCESS_COARSE_LOCATION)) { | 260 Manifest.permission.ACCESS_COARSE_LOCATION)) { |
| 283 // If we can't even ask for enough permission to scan for Bluetooth devices, don't open | 261 // If we can't even ask for enough permission to scan for Bluetooth devices, don't open |
| 284 // the dialog. | 262 // the dialog. |
| 285 return null; | 263 return null; |
| 286 } | 264 } |
| 287 BluetoothChooserDialog dialog = new BluetoothChooserDialog( | 265 BluetoothChooserDialog dialog = new BluetoothChooserDialog( |
| 288 windowAndroid, origin, securityLevel, nativeBluetoothChooserDial ogPtr); | 266 windowAndroid, origin, securityLevel, nativeBluetoothChooserDial ogPtr); |
| 289 dialog.show(); | 267 dialog.show(); |
| 290 return dialog; | 268 return dialog; |
| 291 } | 269 } |
| 292 | 270 |
| 293 @VisibleForTesting | 271 @VisibleForTesting |
| 294 @CalledByNative | 272 @CalledByNative |
| 295 void addDevice(String deviceId, String deviceName) { | 273 void addDevice(String deviceId, String deviceName) { |
| 296 List<ItemChooserDialog.ItemChooserRow> devices = | 274 mItemChooserDialog.addItemToList( |
| 297 new ArrayList<ItemChooserDialog.ItemChooserRow>(); | 275 new ItemChooserDialog.ItemChooserRow(deviceId, deviceName)); |
| 298 devices.add(new ItemChooserDialog.ItemChooserRow(deviceId, deviceName)); | |
| 299 mItemChooserDialog.addItemsToList(devices); | |
| 300 } | 276 } |
| 301 | 277 |
| 302 @VisibleForTesting | 278 @VisibleForTesting |
| 303 @CalledByNative | 279 @CalledByNative |
| 304 void closeDialog() { | 280 void closeDialog() { |
| 305 mNativeBluetoothChooserDialogPtr = 0; | 281 mNativeBluetoothChooserDialogPtr = 0; |
| 306 mItemChooserDialog.dismiss(); | 282 mItemChooserDialog.dismiss(); |
| 307 } | 283 } |
| 308 | 284 |
| 309 @VisibleForTesting | 285 @VisibleForTesting |
| 310 @CalledByNative | 286 @CalledByNative |
| 311 void removeDevice(String deviceId) { | 287 void removeDevice(String deviceId) { |
| 312 mItemChooserDialog.setEnabled(deviceId, false); | 288 mItemChooserDialog.setEnabled(deviceId, false); |
| 313 } | 289 } |
| 314 | 290 |
| 315 @VisibleForTesting | 291 @VisibleForTesting |
| 316 @CalledByNative | 292 @CalledByNative |
| 317 void notifyAdapterTurnedOff() { | 293 void notifyAdapterTurnedOff() { |
| 318 SpannableString adapterOffMessage = SpanApplier.applySpans( | 294 SpannableString adapterOffMessage = SpanApplier.applySpans( |
| 319 mContext.getString(R.string.bluetooth_adapter_off), | 295 mContext.getString(R.string.bluetooth_adapter_off), |
| 320 new SpanInfo("<link>", "</link>", | 296 new SpanInfo("<link>", "</link>", |
| 321 new NoUnderlineClickableSpan(LinkType.ADAPTER_OFF, mCont ext))); | 297 new BluetoothClickableSpan(LinkType.ADAPTER_OFF, mContex t))); |
| 322 SpannableString adapterOffStatus = SpanApplier.applySpans( | 298 SpannableString adapterOffStatus = SpanApplier.applySpans( |
| 323 mContext.getString(R.string.bluetooth_adapter_off_help), | 299 mContext.getString(R.string.bluetooth_adapter_off_help), |
| 324 new SpanInfo("<link>", "</link>", | 300 new SpanInfo("<link>", "</link>", |
| 325 new NoUnderlineClickableSpan(LinkType.ADAPTER_OFF_HELP, mContext))); | 301 new BluetoothClickableSpan(LinkType.ADAPTER_OFF_HELP, mC ontext))); |
| 326 | 302 |
| 327 mItemChooserDialog.setErrorState(adapterOffMessage, adapterOffStatus); | 303 mItemChooserDialog.setErrorState(adapterOffMessage, adapterOffStatus); |
| 328 } | 304 } |
| 329 | 305 |
| 330 @CalledByNative | 306 @CalledByNative |
| 331 private void notifyAdapterTurnedOn() { | 307 private void notifyAdapterTurnedOn() { |
| 332 mItemChooserDialog.clear(); | 308 mItemChooserDialog.clear(); |
| 333 if (mNativeBluetoothChooserDialogPtr != 0) { | 309 if (mNativeBluetoothChooserDialogPtr != 0) { |
| 334 nativeRestartSearch(mNativeBluetoothChooserDialogPtr); | 310 nativeRestartSearch(mNativeBluetoothChooserDialogPtr); |
| 335 } | 311 } |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 362 @VisibleForTesting | 338 @VisibleForTesting |
| 363 native void nativeRestartSearch(long nativeBluetoothChooserAndroid); | 339 native void nativeRestartSearch(long nativeBluetoothChooserAndroid); |
| 364 // Help links. | 340 // Help links. |
| 365 @VisibleForTesting | 341 @VisibleForTesting |
| 366 native void nativeShowBluetoothOverviewLink(long nativeBluetoothChooserAndro id); | 342 native void nativeShowBluetoothOverviewLink(long nativeBluetoothChooserAndro id); |
| 367 @VisibleForTesting | 343 @VisibleForTesting |
| 368 native void nativeShowBluetoothAdapterOffLink(long nativeBluetoothChooserAnd roid); | 344 native void nativeShowBluetoothAdapterOffLink(long nativeBluetoothChooserAnd roid); |
| 369 @VisibleForTesting | 345 @VisibleForTesting |
| 370 native void nativeShowNeedLocationPermissionLink(long nativeBluetoothChooser Android); | 346 native void nativeShowNeedLocationPermissionLink(long nativeBluetoothChooser Android); |
| 371 } | 347 } |
| OLD | NEW |