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 |