diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java index 10578d999..ed8aa47f9 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java @@ -10,7 +10,6 @@ import android.text.Html; import android.text.SpannableString; import android.text.Spanned; import android.text.TextUtils; -import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; import android.view.KeyEvent; import android.view.View; @@ -63,6 +62,7 @@ import com.fongmi.android.tv.player.danmu.Parser; import com.fongmi.android.tv.ui.adapter.QualityAdapter; import com.fongmi.android.tv.ui.base.BaseActivity; import com.fongmi.android.tv.ui.custom.CustomKeyDownVod; +import com.fongmi.android.tv.ui.custom.CustomMovement; import com.fongmi.android.tv.ui.custom.dialog.DescDialog; import com.fongmi.android.tv.ui.custom.dialog.TrackDialog; import com.fongmi.android.tv.ui.presenter.ArrayPresenter; @@ -524,11 +524,11 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List } private void setText(TextView view, int resId, String text) { - view.setTag(text); - view.setLinkTextColor(MDColor.YELLOW_500); - view.setVisibility(text.isEmpty() ? View.GONE : View.VISIBLE); view.setText(getSpan(resId, text), TextView.BufferType.SPANNABLE); - if (Sniffer.CLICKER.matcher(text).find()) view.setMovementMethod(LinkMovementMethod.getInstance()); + view.setVisibility(text.isEmpty() ? View.GONE : View.VISIBLE); + view.setLinkTextColor(MDColor.YELLOW_500); + CustomMovement.bind(view); + view.setTag(text); } private SpannableString getSpan(int resId, String text) { diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/custom/dialog/DescDialog.java b/app/src/leanback/java/com/fongmi/android/tv/ui/custom/dialog/DescDialog.java index 371076f36..260b7a3cb 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/custom/dialog/DescDialog.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/custom/dialog/DescDialog.java @@ -1,15 +1,13 @@ package com.fongmi.android.tv.ui.custom.dialog; import android.app.Activity; -import android.text.Editable; -import android.text.method.LinkMovementMethod; -import android.text.style.ClickableSpan; import android.view.LayoutInflater; import android.widget.TextView; import androidx.appcompat.app.AlertDialog; import com.fongmi.android.tv.databinding.DialogDescBinding; +import com.fongmi.android.tv.ui.custom.CustomMovement; import com.github.bassaer.library.MDColor; import com.google.android.material.dialog.MaterialAlertDialogBuilder; @@ -28,10 +26,8 @@ public class DescDialog { } private void initView(TextView view, CharSequence desc) { - view.setLinkTextColor(MDColor.BLUE_500); view.setText(desc, TextView.BufferType.SPANNABLE); - Editable e = new Editable.Factory().newEditable(view.getText()); - ClickableSpan[] spans = e.getSpans(0, e.length(), ClickableSpan.class); - if (spans.length > 0) view.setMovementMethod(LinkMovementMethod.getInstance()); + view.setLinkTextColor(MDColor.BLUE_500); + CustomMovement.bind(view); } } diff --git a/app/src/leanback/res/layout/activity_video.xml b/app/src/leanback/res/layout/activity_video.xml index 1d2a18992..b96c4248b 100644 --- a/app/src/leanback/res/layout/activity_video.xml +++ b/app/src/leanback/res/layout/activity_video.xml @@ -18,6 +18,7 @@ android:focusableInTouchMode="true" android:foreground="@drawable/selector_video" android:nextFocusLeft="@id/video" + android:nextFocusRight="@id/desc" android:nextFocusUp="@id/video" android:nextFocusDown="@id/flag"> @@ -146,7 +147,7 @@ 0 ? CustomMovement.getInstance() : null); + } + + @Override + public boolean canSelectArbitrarily() { + return true; + } + + @Override + protected boolean handleMovementKey(TextView widget, Spannable buffer, int keyCode, int movementMetaState, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_CENTER: + case KeyEvent.KEYCODE_ENTER: + if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) { + if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0 && action(CLICK, widget, buffer)) { + return true; + } + } + break; + } + return super.handleMovementKey(widget, buffer, keyCode, movementMetaState, event); + } + + private void findFocus(TextView widget, int direction) { + View view = widget.focusSearch(direction); + if (view != null) view.requestFocus(); + } + + @Override + protected boolean up(TextView widget, Spannable buffer) { + if (action(UP, widget, buffer)) return true; + findFocus(widget, View.FOCUS_UP); + return super.up(widget, buffer); + } + + @Override + protected boolean down(TextView widget, Spannable buffer) { + if (action(DOWN, widget, buffer)) return true; + findFocus(widget, View.FOCUS_DOWN); + return super.down(widget, buffer); + } + + @Override + protected boolean left(TextView widget, Spannable buffer) { + if (action(UP, widget, buffer)) return true; + findFocus(widget, View.FOCUS_LEFT); + return super.left(widget, buffer); + } + + @Override + protected boolean right(TextView widget, Spannable buffer) { + if (action(DOWN, widget, buffer)) return true; + findFocus(widget, View.FOCUS_RIGHT); + return super.right(widget, buffer); + } + + private boolean action(int what, TextView widget, Spannable buffer) { + Layout layout = widget.getLayout(); + int padding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom(); + int areaTop = widget.getScrollY(); + int areaBot = areaTop + widget.getHeight() - padding; + int lineTop = layout.getLineForVertical(areaTop); + int lineBot = layout.getLineForVertical(areaBot); + int first = layout.getLineStart(lineTop); + int last = layout.getLineEnd(lineBot); + ClickableSpan[] candidates = buffer.getSpans(first, last, ClickableSpan.class); + int a = Selection.getSelectionStart(buffer); + int b = Selection.getSelectionEnd(buffer); + int selStart = Math.min(a, b); + int selEnd = Math.max(a, b); + if (selStart < 0) { + if (buffer.getSpanStart(FROM_BELOW) >= 0) { + selStart = selEnd = buffer.length(); + } + } + if (selStart > last) selStart = selEnd = Integer.MAX_VALUE; + if (selEnd < first) selStart = selEnd = -1; + switch (what) { + case CLICK: + if (selStart == selEnd) return false; + ClickableSpan[] links = buffer.getSpans(selStart, selEnd, ClickableSpan.class); + if (links.length != 1) return false; + ClickableSpan link = links[0]; + link.onClick(widget); + break; + case UP: + int bestStart, bestEnd; + bestStart = -1; + bestEnd = -1; + for (ClickableSpan candidate : candidates) { + int end = buffer.getSpanEnd(candidate); + if (end < selEnd || selStart == selEnd) { + if (end > bestEnd) { + bestStart = buffer.getSpanStart(candidate); + bestEnd = end; + } + } + } + if (bestStart >= 0) { + Selection.setSelection(buffer, bestEnd, bestStart); + return true; + } + break; + case DOWN: + bestStart = Integer.MAX_VALUE; + bestEnd = Integer.MAX_VALUE; + for (ClickableSpan candidate : candidates) { + int start = buffer.getSpanStart(candidate); + if (start > selStart || selStart == selEnd) { + if (start < bestStart) { + bestStart = start; + bestEnd = buffer.getSpanEnd(candidate); + } + } + } + if (bestEnd < Integer.MAX_VALUE) { + Selection.setSelection(buffer, bestStart, bestEnd); + return true; + } + break; + } + return false; + } + + @Override + public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { + int action = event.getAction(); + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) { + int x = (int) event.getX(); + int y = (int) event.getY(); + x -= widget.getTotalPaddingLeft(); + y -= widget.getTotalPaddingTop(); + x += widget.getScrollX(); + y += widget.getScrollY(); + Layout layout = widget.getLayout(); + int line = layout.getLineForVertical(y); + int off = layout.getOffsetForHorizontal(line, x); + ClickableSpan[] links = buffer.getSpans(off, off, ClickableSpan.class); + if (links.length != 0) { + ClickableSpan link = links[0]; + if (action == MotionEvent.ACTION_UP) { + link.onClick(widget); + } else { + Selection.setSelection(buffer, buffer.getSpanStart(link), buffer.getSpanEnd(link)); + } + return true; + } else { + Selection.removeSelection(buffer); + } + } + return super.onTouchEvent(widget, buffer, event); + } + + @Override + public void initialize(TextView widget, Spannable text) { + Selection.removeSelection(text); + text.removeSpan(FROM_BELOW); + } + + @Override + public void onTakeFocus(TextView view, Spannable text, int dir) { + Selection.removeSelection(text); + if ((dir & View.FOCUS_BACKWARD) != 0) { + text.setSpan(FROM_BELOW, 0, 0, Spannable.SPAN_POINT_POINT); + } else { + text.removeSpan(FROM_BELOW); + } + } +}