11package org .schabi .newpipe .info_list .holder ;
22
3+ import android .content .Context ;
4+ import android .net .Uri ;
5+ import android .os .Bundle ;
36import android .view .View ;
47import android .view .ViewGroup ;
58import android .widget .ImageView ;
69import android .widget .TextView ;
710
11+ import androidx .appcompat .app .AppCompatActivity ;
812import androidx .core .content .ContextCompat ;
13+ import androidx .core .view .AccessibilityDelegateCompat ;
14+ import androidx .core .view .ViewCompat ;
15+ import androidx .core .view .accessibility .AccessibilityNodeInfoCompat ;
16+ import androidx .fragment .app .FragmentActivity ;
17+ import androidx .preference .PreferenceManager ;
918
1019import org .schabi .newpipe .R ;
20+ import org .schabi .newpipe .database .stream .model .StreamEntity ;
1121import org .schabi .newpipe .database .stream .model .StreamStateEntity ;
22+ import org .schabi .newpipe .download .DownloadDialog ;
23+ import org .schabi .newpipe .error .ErrorInfo ;
24+ import org .schabi .newpipe .error .ErrorUtil ;
25+ import org .schabi .newpipe .error .UserAction ;
1226import org .schabi .newpipe .extractor .InfoItem ;
1327import org .schabi .newpipe .extractor .stream .StreamInfoItem ;
1428import org .schabi .newpipe .info_list .InfoItemBuilder ;
1529import org .schabi .newpipe .ktx .ViewUtils ;
30+ import org .schabi .newpipe .local .dialog .PlaylistAppendDialog ;
31+ import org .schabi .newpipe .local .dialog .PlaylistDialog ;
1632import org .schabi .newpipe .local .history .HistoryRecordManager ;
33+ import org .schabi .newpipe .player .helper .PlayerHolder ;
1734import org .schabi .newpipe .util .DependentPreferenceHelper ;
1835import org .schabi .newpipe .util .Localization ;
36+ import org .schabi .newpipe .util .NavigationHelper ;
37+ import org .schabi .newpipe .util .SparseItemUtil ;
1938import org .schabi .newpipe .util .StreamTypeUtil ;
39+ import org .schabi .newpipe .util .external_communication .KoreUtils ;
40+ import org .schabi .newpipe .util .external_communication .ShareUtils ;
2041import org .schabi .newpipe .util .image .CoilHelper ;
2142import org .schabi .newpipe .views .AnimatedProgressBar ;
2243
44+ import java .util .List ;
2345import java .util .concurrent .TimeUnit ;
2446
47+ import io .reactivex .rxjava3 .android .schedulers .AndroidSchedulers ;
48+
2549public class StreamMiniInfoItemHolder extends InfoItemHolder {
2650 public final ImageView itemThumbnailView ;
2751 public final TextView itemVideoTitleView ;
@@ -57,7 +81,8 @@ public void updateFromItem(final InfoItem infoItem,
5781
5882 if (item .getDuration () > 0 ) {
5983 itemDurationView .setText (Localization .getDurationString (item .getDuration ()));
60- itemDurationView .setBackgroundColor (ContextCompat .getColor (itemBuilder .getContext (),
84+ itemDurationView .setBackgroundColor (ContextCompat .getColor (
85+ itemBuilder .getContext (),
6186 R .color .duration_background_color ));
6287 itemDurationView .setVisibility (View .VISIBLE );
6388
@@ -76,7 +101,8 @@ public void updateFromItem(final InfoItem infoItem,
76101 }
77102 } else if (StreamTypeUtil .isLiveStream (item .getStreamType ())) {
78103 itemDurationView .setText (R .string .duration_live );
79- itemDurationView .setBackgroundColor (ContextCompat .getColor (itemBuilder .getContext (),
104+ itemDurationView .setBackgroundColor (ContextCompat .getColor (
105+ itemBuilder .getContext (),
80106 R .color .live_duration_background_color ));
81107 itemDurationView .setVisibility (View .VISIBLE );
82108 itemProgressView .setVisibility (View .GONE );
@@ -145,10 +171,195 @@ private void enableLongClick(final StreamInfoItem item) {
145171 }
146172 return true ;
147173 });
174+
175+ updateAccessibilityActions (item );
176+ }
177+
178+ private void updateAccessibilityActions (final StreamInfoItem item ) {
179+ ViewCompat .setAccessibilityDelegate (itemView , new AccessibilityDelegateCompat () {
180+ @ Override
181+ public void onInitializeAccessibilityNodeInfo (final View host ,
182+ final AccessibilityNodeInfoCompat info ) {
183+ super .onInitializeAccessibilityNodeInfo (host , info );
184+
185+ final Context context = itemBuilder .getContext ();
186+ if (context == null ) {
187+ return ;
188+ }
189+
190+ final PlayerHolder holder = PlayerHolder .INSTANCE ;
191+ if (holder .isPlayQueueReady ()) {
192+ info .addAction (new AccessibilityNodeInfoCompat .AccessibilityActionCompat (
193+ R .id .accessibility_action_enqueue ,
194+ context .getString (R .string .enqueue_stream )));
195+
196+ if (holder .getQueuePosition () < holder .getQueueSize () - 1 ) {
197+ info .addAction (new AccessibilityNodeInfoCompat .AccessibilityActionCompat (
198+ R .id .accessibility_action_enqueue_next ,
199+ context .getString (R .string .enqueue_next_stream )));
200+ }
201+ }
202+
203+ info .addAction (new AccessibilityNodeInfoCompat .AccessibilityActionCompat (
204+ R .id .accessibility_action_background ,
205+ context .getString (R .string .start_here_on_background )));
206+
207+ if (!StreamTypeUtil .isAudio (item .getStreamType ())) {
208+ info .addAction (new AccessibilityNodeInfoCompat .AccessibilityActionCompat (
209+ R .id .accessibility_action_popup ,
210+ context .getString (R .string .start_here_on_popup )));
211+ }
212+
213+ if (context instanceof FragmentActivity ) {
214+ info .addAction (new AccessibilityNodeInfoCompat .AccessibilityActionCompat (
215+ R .id .accessibility_action_download ,
216+ context .getString (R .string .download )));
217+
218+ info .addAction (new AccessibilityNodeInfoCompat .AccessibilityActionCompat (
219+ R .id .accessibility_action_playlist ,
220+ context .getString (R .string .add_to_playlist )));
221+ }
222+
223+ info .addAction (new AccessibilityNodeInfoCompat .AccessibilityActionCompat (
224+ R .id .accessibility_action_share ,
225+ context .getString (R .string .share )));
226+
227+ info .addAction (new AccessibilityNodeInfoCompat .AccessibilityActionCompat (
228+ R .id .accessibility_action_browser ,
229+ context .getString (R .string .open_in_browser )));
230+
231+ if (KoreUtils .shouldShowPlayWithKodi (context , item .getServiceId ())) {
232+ info .addAction (new AccessibilityNodeInfoCompat .AccessibilityActionCompat (
233+ R .id .accessibility_action_kodi ,
234+ context .getString (R .string .play_with_kodi_title )));
235+ }
236+
237+ final boolean isWatchHistoryEnabled = PreferenceManager
238+ .getDefaultSharedPreferences (context )
239+ .getBoolean (context .getString (R .string .enable_watch_history_key ), false );
240+ if (isWatchHistoryEnabled && !StreamTypeUtil .isLiveStream (item .getStreamType ())) {
241+ info .addAction (new AccessibilityNodeInfoCompat .AccessibilityActionCompat (
242+ R .id .accessibility_action_mark_watched ,
243+ context .getString (R .string .mark_as_watched )));
244+ }
245+
246+ if (context instanceof AppCompatActivity ) {
247+ info .addAction (new AccessibilityNodeInfoCompat .AccessibilityActionCompat (
248+ R .id .accessibility_action_channel_details ,
249+ context .getString (R .string .show_channel_details )));
250+ }
251+
252+ info .addAction (new AccessibilityNodeInfoCompat .AccessibilityActionCompat (
253+ R .id .accessibility_action_show_options ,
254+ context .getString (R .string .more_options )));
255+ }
256+
257+ @ Override
258+ public boolean performAccessibilityAction (final View host , final int action ,
259+ final Bundle args ) {
260+ final Context context = itemBuilder .getContext ();
261+ if (context == null ) {
262+ return super .performAccessibilityAction (host , action , args );
263+ }
264+
265+ if (action == R .id .accessibility_action_show_options ) {
266+ if (itemBuilder .getOnStreamSelectedListener () != null ) {
267+ itemBuilder .getOnStreamSelectedListener ().held (item );
268+ }
269+ return true ;
270+ } else if (action == R .id .accessibility_action_enqueue ) {
271+ SparseItemUtil .fetchItemInfoIfSparse (context , item ,
272+ singlePlayQueue -> NavigationHelper .enqueueOnPlayer (
273+ context , singlePlayQueue ));
274+ return true ;
275+ } else if (action == R .id .accessibility_action_enqueue_next ) {
276+ SparseItemUtil .fetchItemInfoIfSparse (context , item ,
277+ singlePlayQueue -> NavigationHelper .enqueueNextOnPlayer (
278+ context , singlePlayQueue ));
279+ return true ;
280+ } else if (action == R .id .accessibility_action_background ) {
281+ SparseItemUtil .fetchItemInfoIfSparse (context , item , singlePlayQueue ->
282+ NavigationHelper .playOnBackgroundPlayer (
283+ context , singlePlayQueue , true ));
284+ return true ;
285+ } else if (action == R .id .accessibility_action_popup ) {
286+ SparseItemUtil .fetchItemInfoIfSparse (context , item , singlePlayQueue ->
287+ NavigationHelper .playOnPopupPlayer (
288+ context , singlePlayQueue , true ));
289+ return true ;
290+ } else if (action == R .id .accessibility_action_download ) {
291+ SparseItemUtil .fetchStreamInfoAndSaveToDatabase (context ,
292+ item .getServiceId (),
293+ item .getUrl (), info -> {
294+ final FragmentActivity activity = (FragmentActivity ) context ;
295+ if (!activity .isFinishing () && !activity .isDestroyed ()) {
296+ final DownloadDialog downloadDialog =
297+ new DownloadDialog (context , info );
298+ downloadDialog .show (activity .getSupportFragmentManager (),
299+ "downloadDialog" );
300+ }
301+ });
302+ return true ;
303+ } else if (action == R .id .accessibility_action_playlist ) {
304+ final FragmentActivity activity = (FragmentActivity ) context ;
305+ PlaylistDialog .createCorrespondingDialog (
306+ context ,
307+ List .of (new StreamEntity (item )),
308+ dialog -> dialog .show (
309+ activity .getSupportFragmentManager (),
310+ "StreamDialogEntry@"
311+ + (dialog instanceof PlaylistAppendDialog
312+ ? "append" : "create" )
313+ + "_playlist"
314+ )
315+ );
316+ return true ;
317+ } else if (action == R .id .accessibility_action_share ) {
318+ ShareUtils .shareText (context , item .getName (),
319+ item .getUrl (), item .getThumbnails ());
320+ return true ;
321+ } else if (action == R .id .accessibility_action_browser ) {
322+ ShareUtils .openUrlInBrowser (context , item .getUrl ());
323+ return true ;
324+ } else if (action == R .id .accessibility_action_kodi ) {
325+ KoreUtils .playWithKore (context , Uri .parse (item .getUrl ()));
326+ return true ;
327+ } else if (action == R .id .accessibility_action_mark_watched ) {
328+ new HistoryRecordManager (context )
329+ .markAsWatched (item )
330+ .doOnError (error -> {
331+ ErrorUtil .showSnackbar (
332+ context ,
333+ new ErrorInfo (
334+ error ,
335+ UserAction .OPEN_INFO_ITEM_DIALOG ,
336+ "Got an error when trying to mark as watched"
337+ )
338+ );
339+ })
340+ .onErrorComplete ()
341+ .observeOn (AndroidSchedulers .mainThread ())
342+ .subscribe ();
343+ return true ;
344+ } else if (action == R .id .accessibility_action_channel_details ) {
345+ SparseItemUtil .fetchUploaderUrlIfSparse ((AppCompatActivity ) context ,
346+ item .getServiceId (), item .getUrl (),
347+ item .getUploaderUrl (),
348+ url -> NavigationHelper .openChannelFragment (
349+ ((AppCompatActivity ) context ).getSupportFragmentManager (),
350+ item .getServiceId (), url , item .getUploaderName ()));
351+ return true ;
352+ }
353+
354+ return super .performAccessibilityAction (host , action , args );
355+ }
356+ });
148357 }
149358
150359 private void disableLongClick () {
151360 itemView .setLongClickable (false );
152361 itemView .setOnLongClickListener (null );
362+ ViewCompat .setAccessibilityDelegate (itemView , null );
153363 }
154364}
365+
0 commit comments