1
1
package org .mozilla .vrbrowser .ui .widgets ;
2
2
3
+ import android .content .ClipData ;
4
+ import android .content .ClipboardManager ;
3
5
import android .content .Context ;
6
+ import android .graphics .Rect ;
7
+ import android .graphics .RectF ;
4
8
import android .graphics .Typeface ;
9
+ import android .net .Uri ;
5
10
import android .text .Spannable ;
6
11
import android .text .SpannableStringBuilder ;
7
12
import android .text .style .StyleSpan ;
12
17
import android .view .ViewGroup ;
13
18
import android .view .animation .Animation ;
14
19
import android .view .animation .AnimationUtils ;
20
+ import android .widget .AdapterView ;
15
21
import android .widget .ArrayAdapter ;
16
- import android .widget .ImageButton ;
17
22
import android .widget .ImageView ;
18
- import android .widget .ListView ;
19
23
import android .widget .TextView ;
20
24
21
25
import androidx .annotation .NonNull ;
22
26
23
- import org .mozilla .gecko . util . ThreadUtils ;
27
+ import org .mozilla .geckoview . GeckoSession ;
24
28
import org .mozilla .vrbrowser .R ;
25
29
import org .mozilla .vrbrowser .audio .AudioEngine ;
26
30
import org .mozilla .vrbrowser .ui .views .CustomListView ;
31
+ import org .mozilla .vrbrowser .ui .widgets .dialogs .SelectionActionWidget ;
27
32
import org .mozilla .vrbrowser .utils .ViewUtils ;
28
33
29
34
import java .util .ArrayList ;
35
+ import java .util .Collections ;
30
36
import java .util .List ;
31
37
32
38
public class SuggestionsWidget extends UIWidget implements WidgetManagerDelegate .FocusChangeListener {
@@ -38,10 +44,12 @@ public class SuggestionsWidget extends UIWidget implements WidgetManagerDelegate
38
44
private URLBarPopupDelegate mURLBarDelegate ;
39
45
private String mHighlightedText ;
40
46
private AudioEngine mAudio ;
47
+ private ClipboardManager mClipboard ;
48
+ private SelectionActionWidget mSelectionMenu ;
41
49
42
50
public interface URLBarPopupDelegate {
43
- default void OnItemClicked (SuggestionItem item ) {};
44
- default void OnItemDeleted (SuggestionItem item ) {};
51
+ default void OnItemClicked (SuggestionItem item ) {}
52
+ default void OnItemLongClicked (SuggestionItem item ) {}
45
53
}
46
54
47
55
public SuggestionsWidget (Context aContext ) {
@@ -87,8 +95,12 @@ public void onAnimationRepeat(Animation animation) {
87
95
88
96
mAdapter = new SuggestionsAdapter (getContext (), R .layout .list_popup_window_item , new ArrayList <>());
89
97
mList .setAdapter (mAdapter );
98
+ mList .setOnItemClickListener (mClickListener );
99
+ mList .setOnItemLongClickListener (mLongClickListener );
100
+ mList .setOnScrollChangeListener ((v , scrollX , scrollY , oldScrollX , oldScrollY ) -> hideMenu ());
90
101
91
102
mAudio = AudioEngine .fromContext (aContext );
103
+ mClipboard = (ClipboardManager ) getContext ().getSystemService (Context .CLIPBOARD_SERVICE );
92
104
93
105
mHighlightedText = "" ;
94
106
}
@@ -135,6 +147,7 @@ public void hideNoAnim(@HideFlags int aHideFlags) {
135
147
@ Override
136
148
public void onGlobalFocusChanged (View oldFocus , View newFocus ) {
137
149
if (!ViewUtils .isEqualOrChildrenOf (this , newFocus )) {
150
+ hideMenu ();
138
151
onDismiss ();
139
152
}
140
153
}
@@ -200,7 +213,6 @@ private class ItemViewHolder {
200
213
ImageView favicon ;
201
214
TextView title ;
202
215
TextView url ;
203
- ImageButton delete ;
204
216
View divider ;
205
217
}
206
218
@@ -220,24 +232,18 @@ public View getView(int position, View convertView, ViewGroup parent) {
220
232
221
233
itemViewHolder .layout = listItem .findViewById (R .id .layout );
222
234
itemViewHolder .layout .setTag (R .string .position_tag , position );
223
- itemViewHolder .layout .setOnClickListener (mRowListener );
224
235
itemViewHolder .favicon = listItem .findViewById (R .id .favicon );
225
236
itemViewHolder .title = listItem .findViewById (R .id .title );
226
237
itemViewHolder .url = listItem .findViewById (R .id .url );
227
- itemViewHolder .delete = listItem .findViewById (R .id .delete );
228
- itemViewHolder .delete .setTag (R .string .position_tag , position );
229
- itemViewHolder .delete .setOnClickListener (mDeleteButtonListener );
230
238
itemViewHolder .divider = listItem .findViewById (R .id .divider );
231
239
232
240
listItem .setTag (R .string .list_item_view_tag , itemViewHolder );
233
241
234
242
listItem .setOnHoverListener (mHoverListener );
235
- listItem .setOnTouchListener (mTouchListener );
236
243
237
244
} else {
238
245
itemViewHolder = (ItemViewHolder ) listItem .getTag (R .string .list_item_view_tag );
239
246
itemViewHolder .layout .setTag (R .string .position_tag , position );
240
- itemViewHolder .delete .setTag (R .string .position_tag , position );
241
247
}
242
248
243
249
SuggestionItem selectedItem = getItem (position );
@@ -274,7 +280,6 @@ public View getView(int position, View convertView, ViewGroup parent) {
274
280
itemViewHolder .favicon .setImageResource (R .drawable .ic_icon_bookmark );
275
281
}
276
282
277
- itemViewHolder .delete .setVisibility (GONE );
278
283
itemViewHolder .favicon .setVisibility (VISIBLE );
279
284
280
285
if (position == 0 ) {
@@ -286,59 +291,6 @@ public View getView(int position, View convertView, ViewGroup parent) {
286
291
return listItem ;
287
292
}
288
293
289
- OnClickListener mDeleteButtonListener = v -> {
290
- if (mAudio != null ) {
291
- mAudio .playSound (AudioEngine .Sound .CLICK );
292
- }
293
-
294
- int position = (Integer )v .getTag (R .string .position_tag );
295
- SuggestionItem item = getItem (position );
296
- mAdapter .remove (item );
297
- mAdapter .notifyDataSetChanged ();
298
-
299
- if (mURLBarDelegate != null ) {
300
- mURLBarDelegate .OnItemDeleted (item );
301
- }
302
- };
303
-
304
- OnClickListener mRowListener = v -> {
305
- if (mAudio != null ) {
306
- mAudio .playSound (AudioEngine .Sound .CLICK );
307
- }
308
-
309
- hide (KEEP_WIDGET );
310
-
311
- requestFocus ();
312
- requestFocusFromTouch ();
313
-
314
- if (mURLBarDelegate != null ) {
315
- int position = (Integer )v .getTag (R .string .position_tag );
316
- SuggestionItem item = getItem (position );
317
- mURLBarDelegate .OnItemClicked (item );
318
- }
319
- };
320
-
321
- private OnTouchListener mTouchListener = (view , event ) -> {
322
- int position = (int )view .getTag (R .string .position_tag );
323
- if (!isEnabled (position )) {
324
- return false ;
325
- }
326
-
327
- int ev = event .getActionMasked ();
328
- switch (ev ) {
329
- case MotionEvent .ACTION_UP :
330
- view .setPressed (false );
331
- view .performClick ();
332
- return true ;
333
-
334
- case MotionEvent .ACTION_DOWN :
335
- view .setPressed (true );
336
- return true ;
337
- }
338
-
339
- return false ;
340
- };
341
-
342
294
private OnHoverListener mHoverListener = (view , motionEvent ) -> {
343
295
int position = (int )view .getTag (R .string .position_tag );
344
296
if (!isEnabled (position )) {
@@ -348,7 +300,6 @@ public View getView(int position, View convertView, ViewGroup parent) {
348
300
View favicon = view .findViewById (R .id .favicon );
349
301
TextView title = view .findViewById (R .id .title );
350
302
View url = view .findViewById (R .id .url );
351
- View delete = view .findViewById (R .id .delete );
352
303
int ev = motionEvent .getActionMasked ();
353
304
switch (ev ) {
354
305
case MotionEvent .ACTION_HOVER_ENTER :
@@ -357,7 +308,6 @@ public View getView(int position, View convertView, ViewGroup parent) {
357
308
title .setHovered (true );
358
309
title .setShadowLayer (title .getShadowRadius (), title .getShadowDx (), title .getShadowDy (), getContext ().getColor (R .color .text_shadow_light ));
359
310
url .setHovered (true );
360
- delete .setHovered (true );
361
311
return true ;
362
312
363
313
case MotionEvent .ACTION_HOVER_EXIT :
@@ -366,14 +316,97 @@ public View getView(int position, View convertView, ViewGroup parent) {
366
316
title .setHovered (false );
367
317
title .setShadowLayer (title .getShadowRadius (), title .getShadowDx (), title .getShadowDy (), getContext ().getColor (R .color .text_shadow ));
368
318
url .setHovered (false );
369
- delete .setHovered (false );
370
319
return true ;
371
320
}
372
321
373
322
return false ;
374
323
};
375
324
}
376
325
326
+ private AdapterView .OnItemClickListener mClickListener = new AdapterView .OnItemClickListener () {
327
+ @ Override
328
+ public void onItemClick (AdapterView <?> parent , View view , int position , long id ) {
329
+ if (mAudio != null ) {
330
+ mAudio .playSound (AudioEngine .Sound .CLICK );
331
+ }
332
+
333
+ hide (KEEP_WIDGET );
334
+
335
+ requestFocus ();
336
+ requestFocusFromTouch ();
337
+
338
+ if (mURLBarDelegate != null ) {
339
+ SuggestionItem item = mAdapter .getItem (position );
340
+ mURLBarDelegate .OnItemClicked (item );
341
+ }
342
+ }
343
+ };
344
+
345
+ private AdapterView .OnItemLongClickListener mLongClickListener = new AdapterView .OnItemLongClickListener () {
346
+ @ Override
347
+ public boolean onItemLongClick (AdapterView <?> parent , View view , int position , long id ) {
348
+ SuggestionItem item = mAdapter .getItem (position );
349
+
350
+ view .setHovered (true );
351
+
352
+ hideMenu ();
353
+ if (item != null ) {
354
+ showMenu (view , item );
355
+
356
+ return true ;
357
+ }
358
+
359
+ return false ;
360
+ }
361
+ };
362
+
363
+ private void showMenu (@ NonNull View view , @ NonNull SuggestionItem item ) {
364
+ if (mSelectionMenu == null ) {
365
+ mSelectionMenu = new SelectionActionWidget (getContext ());
366
+ mSelectionMenu .mWidgetPlacement .parentHandle = getHandle ();
367
+ mSelectionMenu .setActions (Collections .singleton (GeckoSession .SelectionActionDelegate .ACTION_COPY ));
368
+ }
369
+
370
+ Rect offsetViewBounds = new Rect ();
371
+ view .getDrawingRect (offsetViewBounds );
372
+ float ratio = WidgetPlacement .viewToWidgetRatio (getContext (), this );
373
+ offsetDescendantRectToMyCoords (view , offsetViewBounds );
374
+ RectF rectF = new RectF (
375
+ offsetViewBounds .left * ratio ,
376
+ offsetViewBounds .top * ratio ,
377
+ offsetViewBounds .right * ratio ,
378
+ offsetViewBounds .bottom * ratio
379
+ );
380
+ mSelectionMenu .setSelectionRect (rectF );
381
+ mSelectionMenu .setDelegate (new SelectionActionWidget .Delegate () {
382
+ @ Override
383
+ public void onAction (String action ) {
384
+ hideMenu ();
385
+ ClipData clip = ClipData .newRawUri (item .title , Uri .parse (item .url ));
386
+ mClipboard .setPrimaryClip (clip );
387
+ }
388
+
389
+ @ Override
390
+ public void onDismiss () {
391
+ hideMenu ();
392
+ }
393
+ });
394
+ mSelectionMenu .show (KEEP_FOCUS );
395
+ }
396
+
397
+ private void hideMenu () {
398
+ if (mSelectionMenu != null ) {
399
+ mSelectionMenu .setDelegate ((SelectionActionWidget .Delegate )null );
400
+ if (!mSelectionMenu .isReleased ()) {
401
+ if (mSelectionMenu .isVisible ()) {
402
+ mSelectionMenu .hide (REMOVE_WIDGET );
403
+ }
404
+ mSelectionMenu .releaseWidget ();
405
+ }
406
+ mSelectionMenu = null ;
407
+ }
408
+ }
409
+
377
410
private SpannableStringBuilder createHighlightedText (@ NonNull String text ) {
378
411
final SpannableStringBuilder sb = new SpannableStringBuilder (text );
379
412
final StyleSpan bold = new StyleSpan (Typeface .BOLD );
0 commit comments