11package org .mozilla .vrbrowser .ui .widgets ;
22
3+ import android .content .ClipData ;
4+ import android .content .ClipboardManager ;
35import android .content .Context ;
6+ import android .graphics .Rect ;
7+ import android .graphics .RectF ;
48import android .graphics .Typeface ;
9+ import android .net .Uri ;
510import android .text .Spannable ;
611import android .text .SpannableStringBuilder ;
712import android .text .style .StyleSpan ;
1217import android .view .ViewGroup ;
1318import android .view .animation .Animation ;
1419import android .view .animation .AnimationUtils ;
20+ import android .widget .AdapterView ;
1521import android .widget .ArrayAdapter ;
16- import android .widget .ImageButton ;
1722import android .widget .ImageView ;
18- import android .widget .ListView ;
1923import android .widget .TextView ;
2024
2125import androidx .annotation .NonNull ;
2226
23- import org .mozilla .gecko . util . ThreadUtils ;
27+ import org .mozilla .geckoview . GeckoSession ;
2428import org .mozilla .vrbrowser .R ;
2529import org .mozilla .vrbrowser .audio .AudioEngine ;
2630import org .mozilla .vrbrowser .ui .views .CustomListView ;
31+ import org .mozilla .vrbrowser .ui .widgets .dialogs .SelectionActionWidget ;
2732import org .mozilla .vrbrowser .utils .ViewUtils ;
2833
2934import java .util .ArrayList ;
35+ import java .util .Collections ;
3036import java .util .List ;
3137
3238public class SuggestionsWidget extends UIWidget implements WidgetManagerDelegate .FocusChangeListener {
@@ -38,10 +44,12 @@ public class SuggestionsWidget extends UIWidget implements WidgetManagerDelegate
3844 private URLBarPopupDelegate mURLBarDelegate ;
3945 private String mHighlightedText ;
4046 private AudioEngine mAudio ;
47+ private ClipboardManager mClipboard ;
48+ private SelectionActionWidget mSelectionMenu ;
4149
4250 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 ) {}
4553 }
4654
4755 public SuggestionsWidget (Context aContext ) {
@@ -87,8 +95,12 @@ public void onAnimationRepeat(Animation animation) {
8795
8896 mAdapter = new SuggestionsAdapter (getContext (), R .layout .list_popup_window_item , new ArrayList <>());
8997 mList .setAdapter (mAdapter );
98+ mList .setOnItemClickListener (mClickListener );
99+ mList .setOnItemLongClickListener (mLongClickListener );
100+ mList .setOnScrollChangeListener ((v , scrollX , scrollY , oldScrollX , oldScrollY ) -> hideMenu ());
90101
91102 mAudio = AudioEngine .fromContext (aContext );
103+ mClipboard = (ClipboardManager ) getContext ().getSystemService (Context .CLIPBOARD_SERVICE );
92104
93105 mHighlightedText = "" ;
94106 }
@@ -135,6 +147,7 @@ public void hideNoAnim(@HideFlags int aHideFlags) {
135147 @ Override
136148 public void onGlobalFocusChanged (View oldFocus , View newFocus ) {
137149 if (!ViewUtils .isEqualOrChildrenOf (this , newFocus )) {
150+ hideMenu ();
138151 onDismiss ();
139152 }
140153 }
@@ -200,7 +213,6 @@ private class ItemViewHolder {
200213 ImageView favicon ;
201214 TextView title ;
202215 TextView url ;
203- ImageButton delete ;
204216 View divider ;
205217 }
206218
@@ -220,24 +232,18 @@ public View getView(int position, View convertView, ViewGroup parent) {
220232
221233 itemViewHolder .layout = listItem .findViewById (R .id .layout );
222234 itemViewHolder .layout .setTag (R .string .position_tag , position );
223- itemViewHolder .layout .setOnClickListener (mRowListener );
224235 itemViewHolder .favicon = listItem .findViewById (R .id .favicon );
225236 itemViewHolder .title = listItem .findViewById (R .id .title );
226237 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 );
230238 itemViewHolder .divider = listItem .findViewById (R .id .divider );
231239
232240 listItem .setTag (R .string .list_item_view_tag , itemViewHolder );
233241
234242 listItem .setOnHoverListener (mHoverListener );
235- listItem .setOnTouchListener (mTouchListener );
236243
237244 } else {
238245 itemViewHolder = (ItemViewHolder ) listItem .getTag (R .string .list_item_view_tag );
239246 itemViewHolder .layout .setTag (R .string .position_tag , position );
240- itemViewHolder .delete .setTag (R .string .position_tag , position );
241247 }
242248
243249 SuggestionItem selectedItem = getItem (position );
@@ -274,7 +280,6 @@ public View getView(int position, View convertView, ViewGroup parent) {
274280 itemViewHolder .favicon .setImageResource (R .drawable .ic_icon_bookmark );
275281 }
276282
277- itemViewHolder .delete .setVisibility (GONE );
278283 itemViewHolder .favicon .setVisibility (VISIBLE );
279284
280285 if (position == 0 ) {
@@ -286,59 +291,6 @@ public View getView(int position, View convertView, ViewGroup parent) {
286291 return listItem ;
287292 }
288293
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-
342294 private OnHoverListener mHoverListener = (view , motionEvent ) -> {
343295 int position = (int )view .getTag (R .string .position_tag );
344296 if (!isEnabled (position )) {
@@ -348,7 +300,6 @@ public View getView(int position, View convertView, ViewGroup parent) {
348300 View favicon = view .findViewById (R .id .favicon );
349301 TextView title = view .findViewById (R .id .title );
350302 View url = view .findViewById (R .id .url );
351- View delete = view .findViewById (R .id .delete );
352303 int ev = motionEvent .getActionMasked ();
353304 switch (ev ) {
354305 case MotionEvent .ACTION_HOVER_ENTER :
@@ -357,7 +308,6 @@ public View getView(int position, View convertView, ViewGroup parent) {
357308 title .setHovered (true );
358309 title .setShadowLayer (title .getShadowRadius (), title .getShadowDx (), title .getShadowDy (), getContext ().getColor (R .color .text_shadow_light ));
359310 url .setHovered (true );
360- delete .setHovered (true );
361311 return true ;
362312
363313 case MotionEvent .ACTION_HOVER_EXIT :
@@ -366,14 +316,97 @@ public View getView(int position, View convertView, ViewGroup parent) {
366316 title .setHovered (false );
367317 title .setShadowLayer (title .getShadowRadius (), title .getShadowDx (), title .getShadowDy (), getContext ().getColor (R .color .text_shadow ));
368318 url .setHovered (false );
369- delete .setHovered (false );
370319 return true ;
371320 }
372321
373322 return false ;
374323 };
375324 }
376325
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+
377410 private SpannableStringBuilder createHighlightedText (@ NonNull String text ) {
378411 final SpannableStringBuilder sb = new SpannableStringBuilder (text );
379412 final StyleSpan bold = new StyleSpan (Typeface .BOLD );
0 commit comments