Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
eaa1179
Fix scrolling comments list
Sep 20, 2019
4806ac6
Correctly move focus from toolbar search bar to dropdown
Sep 20, 2019
8952e2b
Close DrawerLayout on back button press
Sep 20, 2019
2b39438
Fix scrolling in main screen grid
Sep 20, 2019
7dc4ccf
MainPlayer: make title and subtitle non-focusable
Sep 20, 2019
1bb96ef
When child of CoordinatorLayout wants focus, show it!
Sep 20, 2019
6791de5
Do not discriminate against non-Amazon TV boxes
Sep 20, 2019
644ad11
Make description focusable, so TV users can scroll it
Sep 20, 2019
d29e0aa
Improve usability of MainVideoActivity with directional navigation
Sep 20, 2019
d8bd8d8
Make player screen controls into buttons
Sep 20, 2019
7db1ba4
Do not allow focus to escape from open DrawerLayout
Sep 20, 2019
a8a2829
Support for seeking videos in directional navigation mode
Sep 20, 2019
eb6d26b
Focus drawer when it opens
Sep 23, 2019
d23227d
Implement global focus highlight
Sep 23, 2019
28fb864
Focus video view thumbnail after it is loaded
Sep 23, 2019
79c962f
More robust focus search in SuperScrollLayoutManager
Sep 30, 2019
6da2b39
Allow comment links (if any) to gain focus
Oct 9, 2019
6e76610
Eliminate bunch of ExoPlayer warnings
Sep 23, 2019
a7c31e6
RecyclerView scroll fixes
Alexander-- Nov 8, 2019
b5558a8
Remove FixedGridLayoutManager
Alexander-- Nov 8, 2019
9801cf5
Save/restore focused item
Alexander-- Nov 14, 2019
7bb5cac
Special MovementMethod for video description
Alexander-- Nov 14, 2019
5716cf8
Add hints for focus transition from description
Alexander-- Nov 14, 2019
5f051a9
More fixes to comment focus handling
Alexander-- Nov 14, 2019
7d75950
Disable srolling down comment list while comments are loading
Alexander-- Nov 14, 2019
436c75c
Make comment pic explicitly non-focusable
Alexander-- Nov 14, 2019
a1e02f7
Default to landscape orientation for Android TV
Alexander-- Nov 16, 2019
c0fb96a
Release seekbar on any confirmation key, not just DPAD_CENTER
Alexander-- Nov 16, 2019
dc7ae39
Leanback launcher support
Alexander-- Nov 17, 2019
106e538
Excpicitly disable touchscreen requirement
Alexander-- Nov 17, 2019
20bff13
Disable touchScreenBlocksFocus on AppBarLayout
Alexander-- Nov 17, 2019
29136d6
Intercept ActivityNotFoundException for ACTION_CAPTIONING_SETTINGS
Alexander-- Dec 1, 2019
3f51114
Improve usability of settings on TV devices
Alexander-- Dec 1, 2019
a0cb96a
Merge remote-tracking branch 'newpipe/dev' into rebase
Alexander-- Dec 10, 2019
8c9015b
Remove commented code
Alexander-- Dec 10, 2019
55d2637
Merge remote-tracking branch 'newpipe/dev' into rebase
Alexander-- Jan 1, 2020
fac13fb
Merge remote-tracking branch 'newpipe/dev' into rebase
Alexander-- Jan 28, 2020
caa1de8
Rename FireTvUtils to AndroidTvUtils and isFireTv() to isTV()
Alexander-- Jan 28, 2020
e6df041
Merge remote-tracking branch 'newpipe/dev' into rebase
Alexander-- Feb 5, 2020
50a2771
Merge remote-tracking branch 'newpipe/dev' into rebase
Alexander-- Feb 7, 2020
5bd0c70
Merge remote-tracking branch 'newpipe/dev' into rebase
Alexander-- Feb 11, 2020
5654480
Merge remote-tracking branch 'newpipe/dev' into rebase
Alexander-- Feb 25, 2020
6a3a72e
NewPipeRecyclerView should allow scrolling down by default
Alexander-- Feb 25, 2020
fa68235
Merge remote-tracking branch 'newpipe/dev' into rebase
Alexander-- Mar 11, 2020
1cc5a67
Fix focus getting stuck by cycling within the same list item
Alexander-- Mar 11, 2020
9cb3cf2
Intercept ActivityNotFoundException for ACTION_MANAGE_OVERLAY_PERMISSION
Alexander-- Mar 11, 2020
512046e
Fix navigating to action bar buttons on API 28
Alexander-- Mar 14, 2020
6aca344
Merge remote-tracking branch 'newpipe/dev' into rebase
Alexander-- Mar 15, 2020
381b491
Prevent foocus from escaping open navigation drawer
Alexander-- Mar 15, 2020
3a611ad
Prevent NavigationMenuView from gobbling up focus
Alexander-- Mar 15, 2020
6a84f43
Merge remote-tracking branch 'newpipe/dev' into rebase
Alexander-- Mar 30, 2020
3c193dc
Merge remote-tracking branch 'newpipe/dev' into rebase
Alexander-- Apr 2, 2020
c42f5ec
Merge remote-tracking branch 'newpipe/dev' into rebase
Alexander-- Apr 11, 2020
ac5571a
Merge remote-tracking branch 'newpipe/dev' into rebase
Alexander-- Apr 11, 2020
53b3bda
Comply with Checkstyle rules
Alexander-- Apr 11, 2020
d1d942f
Fix Checkstyle violations in MediaSourceManager
Alexander-- Apr 11, 2020
ef7a5bc
Make channel info button focusable
Alexander-- Apr 11, 2020
54ceb85
Don't break navigation if player Views other than controls are focused
Alexander-- Apr 11, 2020
850f51a
When drawer opens, always place focus at it's start
Alexander-- Apr 12, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

<uses-feature android:name="android.hardware.touchscreen" android:required="false"/>

<application
android:banner="@mipmap/newpipe_tv_banner"
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
Expand All @@ -26,6 +29,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,58 @@
package com.google.android.material.appbar;

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.OverScroller;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout;

import java.lang.reflect.Field;

// See https://stackoverflow.com/questions/56849221#57997489
public final class FlingBehavior extends AppBarLayout.Behavior {
private final Rect focusScrollRect = new Rect();

public FlingBehavior(final Context context, final AttributeSet attrs) {
super(context, attrs);
}

@Override
public boolean onRequestChildRectangleOnScreen(
@NonNull final CoordinatorLayout coordinatorLayout, @NonNull final AppBarLayout child,
@NonNull final Rect rectangle, final boolean immediate) {

focusScrollRect.set(rectangle);

coordinatorLayout.offsetDescendantRectToMyCoords(child, focusScrollRect);

int height = coordinatorLayout.getHeight();

if (focusScrollRect.top <= 0 && focusScrollRect.bottom >= height) {
// the child is too big to fit inside ourselves completely, ignore request
return false;
}

int dy;

if (focusScrollRect.bottom > height) {
dy = focusScrollRect.top;
} else if (focusScrollRect.top < 0) {
// scrolling up
dy = -(height - focusScrollRect.bottom);
} else {
// nothing to do
return false;
}

int consumed = scroll(coordinatorLayout, child, dy, getMaxDragOffset(child), 0);

return consumed == dy;
}

public boolean onInterceptTouchEvent(final CoordinatorLayout parent, final AppBarLayout child,
final MotionEvent ev) {
switch (ev.getActionMasked()) {
Expand Down
14 changes: 14 additions & 0 deletions app/src/main/java/org/schabi/newpipe/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import org.schabi.newpipe.fragments.list.search.SearchFragment;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.AndroidTvUtils;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
Expand All @@ -74,6 +75,7 @@
import org.schabi.newpipe.util.StateSaver;
import org.schabi.newpipe.util.TLSSocketFactoryCompat;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.views.FocusOverlayView;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -142,6 +144,10 @@ && getSupportFragmentManager().getBackStackEntryCount() == 0) {
} catch (Exception e) {
ErrorActivity.reportUiError(this, e);
}

if (AndroidTvUtils.isTv()) {
FocusOverlayView.setupFocusObserver(this);
}
}

private void setupDrawer() throws Exception {
Expand Down Expand Up @@ -526,6 +532,14 @@ public void onBackPressed() {
Log.d(TAG, "onBackPressed() called");
}

if (AndroidTvUtils.isTv()) {
View drawerPanel = findViewById(R.id.navigation);
if (drawer.isDrawerOpen(drawerPanel)) {
drawer.closeDrawers();
return;
}
}

Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
// If current fragment implements BackPressable (i.e. can/wanna handle back press)
// delegate the back press to it
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/java/org/schabi/newpipe/RouterActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.AndroidTvUtils;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.views.FocusOverlayView;
import org.schabi.newpipe.util.urlfinder.UrlFinder;

import java.io.Serializable;
Expand Down Expand Up @@ -341,6 +343,10 @@ private void showDialog(final List<AdapterChoiceItem> choices) {
selectedPreviously = selectedRadioPosition;

alertDialog.show();

if (AndroidTvUtils.isTv()) {
FocusOverlayView.setupFocusObserver(alertDialog);
}
}

private List<AdapterChoiceItem> getChoicesForService(final StreamingService service,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
import androidx.appcompat.widget.Toolbar;

import org.schabi.newpipe.R;
import org.schabi.newpipe.util.AndroidTvUtils;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.views.FocusOverlayView;

import us.shandian.giga.service.DownloadManagerService;
import us.shandian.giga.ui.fragment.MissionsFragment;
Expand Down Expand Up @@ -54,6 +56,10 @@ public void onGlobalLayout() {
getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});

if (AndroidTvUtils.isTv()) {
FocusOverlayView.setupFocusObserver(this);
}
}

private void updateFragments() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import android.text.Html;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.util.DisplayMetrics;
import android.util.Log;
Expand Down Expand Up @@ -86,6 +85,7 @@
import org.schabi.newpipe.util.StreamItemAdapter;
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
import org.schabi.newpipe.views.AnimatedProgressBar;
import org.schabi.newpipe.views.LargeTextMovementMethod;

import java.io.Serializable;
import java.util.Collection;
Expand Down Expand Up @@ -470,10 +470,13 @@ private void toggleTitleAndDescription() {
if (videoDescriptionRootLayout.getVisibility() == View.VISIBLE) {
videoTitleTextView.setMaxLines(1);
videoDescriptionRootLayout.setVisibility(View.GONE);
videoDescriptionView.setFocusable(false);
videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
} else {
videoTitleTextView.setMaxLines(10);
videoDescriptionRootLayout.setVisibility(View.VISIBLE);
videoDescriptionView.setFocusable(true);
videoDescriptionView.setMovementMethod(new LargeTextMovementMethod());
videoTitleToggleArrow.setImageResource(R.drawable.arrow_up);
}
}
Expand Down Expand Up @@ -510,7 +513,6 @@ protected void initViews(final View rootView, final Bundle savedInstanceState) {
videoDescriptionRootLayout = rootView.findViewById(R.id.detail_description_root_layout);
videoUploadDateView = rootView.findViewById(R.id.detail_upload_date_view);
videoDescriptionView = rootView.findViewById(R.id.detail_description_view);
videoDescriptionView.setMovementMethod(LinkMovementMethod.getInstance());

thumbsUpTextView = rootView.findViewById(R.id.detail_thumbs_up_count_view);
thumbsUpImageView = rootView.findViewById(R.id.detail_thumbs_up_img_view);
Expand All @@ -532,6 +534,8 @@ protected void initViews(final View rootView, final Bundle savedInstanceState) {
relatedStreamsLayout = rootView.findViewById(R.id.relatedStreamsLayout);

setHeightThumbnail();

thumbnailBackgroundButton.requestFocus();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import org.schabi.newpipe.R;
Expand All @@ -35,6 +34,7 @@
import org.schabi.newpipe.util.OnClickGesture;
import org.schabi.newpipe.util.StateSaver;
import org.schabi.newpipe.util.StreamDialogEntry;
import org.schabi.newpipe.views.SuperScrollLayoutManager;

import java.util.List;
import java.util.Queue;
Expand All @@ -56,6 +56,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>

protected InfoListAdapter infoListAdapter;
protected RecyclerView itemsList;
private int focusedPosition = -1;

/*//////////////////////////////////////////////////////////////////////////
// LifeCycle
Expand Down Expand Up @@ -129,20 +130,53 @@ public String generateSuffix() {
return "." + infoListAdapter.getItemsList().size() + ".list";
}

private int getFocusedPosition() {
View focusedItem = itemsList.getFocusedChild();
if (focusedItem != null) {
RecyclerView.ViewHolder itemHolder = itemsList.findContainingViewHolder(focusedItem);
if (itemHolder != null) {
return itemHolder.getAdapterPosition();
}
}

return -1;
}

@Override
public void writeTo(final Queue<Object> objectsToSave) {
if (useDefaultStateSaving) {
objectsToSave.add(infoListAdapter.getItemsList());
if (!useDefaultStateSaving) {
return;
}

objectsToSave.add(infoListAdapter.getItemsList());
objectsToSave.add(getFocusedPosition());
}

@Override
@SuppressWarnings("unchecked")
public void readFrom(@NonNull final Queue<Object> savedObjects) throws Exception {
if (useDefaultStateSaving) {
infoListAdapter.getItemsList().clear();
infoListAdapter.getItemsList().addAll((List<InfoItem>) savedObjects.poll());
if (!useDefaultStateSaving) {
return;
}

infoListAdapter.getItemsList().clear();
infoListAdapter.getItemsList().addAll((List<InfoItem>) savedObjects.poll());
restoreFocus((Integer) savedObjects.poll());
}

private void restoreFocus(final Integer position) {
if (position == null || position < 0) {
return;
}

itemsList.post(() -> {
RecyclerView.ViewHolder focusedHolder =
itemsList.findViewHolderForAdapterPosition(position);

if (focusedHolder != null) {
focusedHolder.itemView.requestFocus();
}
});
}

@Override
Expand All @@ -162,6 +196,18 @@ protected void onRestoreInstanceState(@NonNull final Bundle bundle) {
}
}

@Override
public void onStop() {
focusedPosition = getFocusedPosition();
super.onStop();
}

@Override
public void onStart() {
super.onStart();
restoreFocus(focusedPosition);
}

/*//////////////////////////////////////////////////////////////////////////
// Init
//////////////////////////////////////////////////////////////////////////*/
Expand All @@ -175,7 +221,7 @@ protected View getListFooter() {
}

protected RecyclerView.LayoutManager getListLayoutManager() {
return new LinearLayoutManager(activity);
return new SuperScrollLayoutManager(activity);
}

protected RecyclerView.LayoutManager getGridLayoutManager() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.ListInfo;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.views.NewPipeRecyclerView;

import java.util.Queue;

Expand Down Expand Up @@ -149,9 +150,13 @@ protected void loadMoreItems() {
if (currentWorker != null) {
currentWorker.dispose();
}

forbidDownwardFocusScroll();

currentWorker = loadMoreItemsLogic()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doFinally(this::allowDownwardFocusScroll)
.subscribe((@io.reactivex.annotations.NonNull
ListExtractor.InfoItemsPage InfoItemsPage) -> {
isLoading.set(false);
Expand All @@ -162,6 +167,18 @@ protected void loadMoreItems() {
});
}

private void forbidDownwardFocusScroll() {
if (itemsList instanceof NewPipeRecyclerView) {
((NewPipeRecyclerView) itemsList).setFocusScrollAllowed(false);
}
}

private void allowDownwardFocusScroll() {
if (itemsList instanceof NewPipeRecyclerView) {
((NewPipeRecyclerView) itemsList).setFocusScrollAllowed(true);
}
}

@Override
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
super.handleNextItems(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.search.SearchInfo;
import org.schabi.newpipe.util.AndroidTvUtils;
import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.list.BaseListFragment;
import org.schabi.newpipe.local.history.HistoryRecordManager;
Expand All @@ -49,7 +50,6 @@
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.FireTvUtils;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ServiceHelper;

Expand Down Expand Up @@ -511,7 +511,7 @@ private void initSearchListeners() {
if (isSuggestionsEnabled && errorPanelRoot.getVisibility() != View.VISIBLE) {
showSuggestionsPanel();
}
if (FireTvUtils.isFireTv()) {
if (AndroidTvUtils.isTv()) {
showKeyboardSearch();
}
});
Expand Down
Loading