Skip to content

Commit c29ae0e

Browse files
aryanchoudharyproTobiGr
authored andcommitted
Improve accessibility for video lists and player details
1 parent d859a5e commit c29ae0e

8 files changed

Lines changed: 245 additions & 14 deletions

File tree

app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java

Lines changed: 213 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,51 @@
11
package org.schabi.newpipe.info_list.holder;
22

3+
import android.content.Context;
4+
import android.net.Uri;
5+
import android.os.Bundle;
36
import android.view.View;
47
import android.view.ViewGroup;
58
import android.widget.ImageView;
69
import android.widget.TextView;
710

11+
import androidx.appcompat.app.AppCompatActivity;
812
import 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

1019
import org.schabi.newpipe.R;
20+
import org.schabi.newpipe.database.stream.model.StreamEntity;
1121
import 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;
1226
import org.schabi.newpipe.extractor.InfoItem;
1327
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
1428
import org.schabi.newpipe.info_list.InfoItemBuilder;
1529
import org.schabi.newpipe.ktx.ViewUtils;
30+
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
31+
import org.schabi.newpipe.local.dialog.PlaylistDialog;
1632
import org.schabi.newpipe.local.history.HistoryRecordManager;
33+
import org.schabi.newpipe.player.helper.PlayerHolder;
1734
import org.schabi.newpipe.util.DependentPreferenceHelper;
1835
import org.schabi.newpipe.util.Localization;
36+
import org.schabi.newpipe.util.NavigationHelper;
37+
import org.schabi.newpipe.util.SparseItemUtil;
1938
import org.schabi.newpipe.util.StreamTypeUtil;
39+
import org.schabi.newpipe.util.external_communication.KoreUtils;
40+
import org.schabi.newpipe.util.external_communication.ShareUtils;
2041
import org.schabi.newpipe.util.image.CoilHelper;
2142
import org.schabi.newpipe.views.AnimatedProgressBar;
2243

44+
import java.util.List;
2345
import java.util.concurrent.TimeUnit;
2446

47+
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
48+
2549
public 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+

app/src/main/res/layout/activity_player_queue_control.xml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@
176176
android:scaleType="fitXY"
177177
android:src="@drawable/ic_repeat"
178178
android:tint="?attr/colorAccent"
179-
tools:ignore="ContentDescription" />
179+
android:contentDescription="@string/notification_action_repeat" />
180180

181181
<androidx.appcompat.widget.AppCompatImageButton
182182
android:id="@+id/control_backward"
@@ -191,7 +191,7 @@
191191
android:scaleType="fitCenter"
192192
android:src="@drawable/ic_previous"
193193
android:tint="?attr/colorAccent"
194-
tools:ignore="ContentDescription" />
194+
android:contentDescription="@string/previous_stream" />
195195

196196
<ImageButton
197197
android:id="@+id/control_fast_rewind"
@@ -205,7 +205,8 @@
205205
android:focusable="true"
206206
android:scaleType="fitCenter"
207207
android:src="@drawable/exo_controls_rewind"
208-
android:tint="?attr/colorAccent" />
208+
android:tint="?attr/colorAccent"
209+
android:contentDescription="@string/rewind" />
209210

210211
<ImageButton
211212
android:id="@+id/control_play_pause"
@@ -221,7 +222,7 @@
221222
android:scaleType="fitCenter"
222223
android:src="@drawable/ic_pause"
223224
android:tint="?attr/colorAccent"
224-
tools:ignore="ContentDescription" />
225+
android:contentDescription="@string/play" />
225226

226227
<ProgressBar
227228
android:id="@+id/control_progress_bar"
@@ -255,7 +256,8 @@
255256
android:focusable="true"
256257
android:scaleType="fitCenter"
257258
android:src="@drawable/exo_controls_fastforward"
258-
android:tint="?attr/colorAccent" />
259+
android:tint="?attr/colorAccent"
260+
android:contentDescription="@string/forward" />
259261

260262
<androidx.appcompat.widget.AppCompatImageButton
261263
android:id="@+id/control_forward"
@@ -270,7 +272,7 @@
270272
android:scaleType="fitCenter"
271273
android:src="@drawable/ic_next"
272274
android:tint="?attr/colorAccent"
273-
tools:ignore="ContentDescription" />
275+
android:contentDescription="@string/next_stream" />
274276

275277
<ImageButton
276278
android:id="@+id/control_shuffle"
@@ -286,7 +288,7 @@
286288
android:scaleType="fitXY"
287289
android:src="@drawable/ic_shuffle"
288290
android:tint="?attr/colorAccent"
289-
tools:ignore="ContentDescription" />
291+
android:contentDescription="@string/notification_action_shuffle" />
290292

291293
</RelativeLayout>
292294
</RelativeLayout>

app/src/main/res/layout/dialog_feed_group_create.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
android:scaleType="centerInside"
2929
app:layout_constraintStart_toStartOf="parent"
3030
app:layout_constraintTop_toTopOf="parent"
31-
tools:ignore="ContentDescription"
31+
android:contentDescription="@string/select_icon"
3232
tools:src="@drawable/ic_asterisk" />
3333

3434
<com.google.android.material.textfield.TextInputLayout
@@ -195,7 +195,7 @@
195195
android:scaleType="centerInside"
196196
android:src="@drawable/ic_delete"
197197
android:visibility="gone"
198-
tools:ignore="ContentDescription"
198+
android:contentDescription="@string/delete"
199199
tools:visibility="visible" />
200200

201201
<Button

app/src/main/res/layout/fragment_video_detail.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@
5656
android:layout_gravity="center"
5757
android:background="@android:color/transparent"
5858
android:src="@drawable/ic_play_arrow_shadow"
59+
android:contentDescription="@string/play"
5960
android:visibility="invisible"
60-
tools:ignore="ContentDescription"
6161
tools:visibility="visible" />
6262

6363
<org.schabi.newpipe.views.NewPipeTextView
@@ -188,7 +188,7 @@
188188
android:layout_marginTop="11dp"
189189
android:layout_marginEnd="10dp"
190190
android:src="@drawable/ic_expand_more"
191-
tools:ignore="ContentDescription" />
191+
android:contentDescription="@string/show_more" />
192192

193193
</FrameLayout>
194194

@@ -614,7 +614,7 @@
614614
android:paddingLeft="@dimen/video_item_search_padding"
615615
android:paddingRight="@dimen/video_item_search_padding"
616616
android:scaleType="fitCenter"
617-
tools:ignore="ContentDescription" />
617+
android:contentDescription="@string/switch_to_main" />
618618

619619
<LinearLayout
620620
android:id="@+id/overlay_metadata_layout"

app/src/main/res/layout/list_stream_item.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
android:layout_width="@dimen/video_item_search_thumbnail_image_width"
1616
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
1717
android:scaleType="fitCenter"
18+
android:importantForAccessibility="no"
1819
android:src="@drawable/placeholder_thumbnail_video"
1920
app:layout_constraintBottom_toTopOf="@+id/itemProgressView"
2021
app:layout_constraintStart_toStartOf="parent"

app/src/main/res/layout/list_stream_mini_item.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
android:layout_alignParentTop="true"
1919
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
2020
android:scaleType="fitCenter"
21+
android:importantForAccessibility="no"
2122
android:src="@drawable/placeholder_thumbnail_video"
2223
tools:ignore="RtlHardcoded" />
2324

0 commit comments

Comments
 (0)