Skip to content
3 changes: 2 additions & 1 deletion app/src/debug/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@

<application
android:name=".DebugApp"
tools:replace="android:name" />
tools:replace="android:name"
android:supportsRtl="true" />
</manifest>
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
android:label="@string/app_name"
android:logo="@mipmap/ic_launcher"
android:theme="@style/OpeningTheme"
android:supportsRtl="true"
android:resizeableActivity="true"
tools:ignore="AllowBackup">
<activity
Expand Down
36 changes: 29 additions & 7 deletions app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.settings.tabs.Tab;
import org.schabi.newpipe.settings.tabs.TabsManager;
import org.schabi.newpipe.util.LocalizeLayoutUtils;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ServiceHelper;

Expand Down Expand Up @@ -160,26 +161,38 @@ private void setupTabs() {
tabsList.clear();
tabsList.addAll(tabsManager.getTabs());

final boolean isRTL = LocalizeLayoutUtils.isRTL(getContext());

if (pagerAdapter == null || !pagerAdapter.sameTabs(tabsList)) {
pagerAdapter = new SelectedTabsPagerAdapter(requireContext(),
getChildFragmentManager(), tabsList);
getChildFragmentManager(), tabsList, isRTL);
}

binding.pager.setAdapter(null);
binding.pager.setOffscreenPageLimit(tabsList.size());
binding.pager.setAdapter(pagerAdapter);

updateTabsIconAndDescription();
if (isRTL) {
binding.mainTabLayout.getTabAt(pagerAdapter.getCount() - 1).select();
}

updateTabsIconAndDescription(isRTL);
updateTitleForTab(binding.pager.getCurrentItem());

hasTabsChanged = false;
}

private void updateTabsIconAndDescription() {
private void updateTabsIconAndDescription(final boolean isRTL) {
for (int i = 0; i < tabsList.size(); i++) {
final TabLayout.Tab tabToSet = binding.mainTabLayout.getTabAt(i);
if (tabToSet != null) {
final Tab tab = tabsList.get(i);
final Tab tab = tabsList.get(
LocalizeLayoutUtils.getLayoutPosition(
isRTL,
tabsList.size(),
i)
);

tabToSet.setIcon(tab.getTabIconRes(requireContext()));
tabToSet.setContentDescription(tab.getTabName(requireContext()));
}
Expand All @@ -199,7 +212,8 @@ public void onTabSelected(final TabLayout.Tab selectedTab) {
}

@Override
public void onTabUnselected(final TabLayout.Tab tab) { }
public void onTabUnselected(final TabLayout.Tab tab) {
}

@Override
public void onTabReselected(final TabLayout.Tab tab) {
Expand All @@ -213,19 +227,27 @@ private static final class SelectedTabsPagerAdapter
extends FragmentStatePagerAdapterMenuWorkaround {
private final Context context;
private final List<Tab> internalTabsList;
private boolean isRTL;

private SelectedTabsPagerAdapter(final Context context,
final FragmentManager fragmentManager,
final List<Tab> tabsList) {
final List<Tab> tabsList,
final boolean isRTL) {
super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
this.context = context;
this.internalTabsList = new ArrayList<>(tabsList);
this.isRTL = isRTL;
}

@NonNull
@Override
public Fragment getItem(final int position) {
final Tab tab = internalTabsList.get(position);
final Tab tab = internalTabsList.get(
LocalizeLayoutUtils.getLayoutPosition(
isRTL,
internalTabsList.size(),
position)
);

final Fragment fragment;
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,30 @@
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;

import org.schabi.newpipe.util.LocalizeLayoutUtils;

import java.util.ArrayList;
import java.util.List;

public class TabAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
private final FragmentManager fragmentManager;
private boolean isRTL;

public TabAdapter(final FragmentManager fm) {
public TabAdapter(final FragmentManager fm, final boolean isRTL) {
// if changed to BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT => crash if enqueueing stream in
// the background and then clicking on it to open VideoDetailFragment:
// "Cannot setMaxLifecycle for Fragment not attached to FragmentManager"
super(fm, BEHAVIOR_SET_USER_VISIBLE_HINT);
this.fragmentManager = fm;
this.isRTL = isRTL;
}

@NonNull
@Override
public Fragment getItem(final int position) {
return mFragmentList.get(position);
return mFragmentList.get(getLayoutPosition(position));
}

@Override
Expand All @@ -46,16 +50,18 @@ public void clearAllItems() {
}

public void removeItem(final int position) {
mFragmentList.remove(position == 0 ? 0 : position - 1);
mFragmentTitleList.remove(position == 0 ? 0 : position - 1);
final int newPosition = getLayoutPosition(position);

mFragmentList.remove(newPosition == 0 ? 0 : newPosition - 1);
mFragmentTitleList.remove(newPosition == 0 ? 0 : newPosition - 1);
}

public void updateItem(final int position, final Fragment fragment) {
mFragmentList.set(position, fragment);
mFragmentList.set(getLayoutPosition(position), fragment);
}

public void updateItem(final String title, final Fragment fragment) {
final int index = mFragmentTitleList.indexOf(title);
final int index = getLayoutPosition(mFragmentTitleList.indexOf(title));
if (index != -1) {
updateItem(index, fragment);
}
Expand All @@ -64,22 +70,23 @@ public void updateItem(final String title, final Fragment fragment) {
@Override
public int getItemPosition(@NonNull final Object object) {
if (mFragmentList.contains(object)) {
return mFragmentList.indexOf(object);
return getLayoutPosition(mFragmentList.indexOf(object));
} else {
return POSITION_NONE;
}
}

public int getItemPositionByTitle(final String title) {
return mFragmentTitleList.indexOf(title);
return getLayoutPosition(mFragmentTitleList.indexOf(title));
}

@Nullable
public String getItemTitle(final int position) {
if (position < 0 || position >= mFragmentTitleList.size()) {
return null;
}
return mFragmentTitleList.get(position);

return mFragmentTitleList.get(getLayoutPosition(position));
}

public void notifyDataSetUpdate() {
Expand All @@ -93,4 +100,7 @@ public void destroyItem(@NonNull final ViewGroup container,
fragmentManager.beginTransaction().remove((Fragment) object).commitNowAllowingStateLoss();
}

private int getLayoutPosition(final int position) {
return LocalizeLayoutUtils.getLayoutPosition(isRTL, mFragmentList.size(), position);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.LocalizeLayoutUtils;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.PicassoHelper;
Expand Down Expand Up @@ -603,10 +604,6 @@ public void onViewCreated(@NonNull final View rootView, final Bundle savedInstan
protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);

pageAdapter = new TabAdapter(getChildFragmentManager());
binding.viewPager.setAdapter(pageAdapter);
binding.tabLayout.setupWithViewPager(binding.viewPager);

binding.detailThumbnailRootLayout.requestFocus();

binding.detailControlsPlayWithKodi.setVisibility(
Expand All @@ -621,6 +618,13 @@ protected void initViews(final View rootView, final Bundle savedInstanceState) {
: View.GONE
);

pageAdapter = new TabAdapter(
getChildFragmentManager(),
LocalizeLayoutUtils.isRTL(getContext())
);
binding.viewPager.setAdapter(pageAdapter);
binding.tabLayout.setupWithViewPager(binding.viewPager);

if (DeviceUtils.isTv(getContext())) {
// remove ripple effects from detail controls
final int transparent = ContextCompat.getColor(requireContext(),
Expand Down Expand Up @@ -985,8 +989,12 @@ private void initTabs() {
* {@link #tabContentDescriptions}, which are all set in {@link #initTabs()}.
*/
private void updateTabIconsAndContentDescriptions() {
final boolean isRTL = LocalizeLayoutUtils.isRTL(getContext());

for (int i = 0; i < tabIcons.size(); ++i) {
final TabLayout.Tab tab = binding.tabLayout.getTabAt(i);
final TabLayout.Tab tab = binding.tabLayout.getTabAt(
LocalizeLayoutUtils.getLayoutPosition(
isRTL, tabIcons.size(), i));
if (tab != null) {
tab.setIcon(tabIcons.get(i));
tab.setContentDescription(tabContentDescriptions.get(i));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1089,7 +1089,7 @@ public int getSuggestionMovementFlags(@NonNull final RecyclerView.ViewHolder vie

final SuggestionItem item = suggestionListAdapter.getItem(position);
return item.fromHistory ? makeMovementFlags(0,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) : 0;
ItemTouchHelper.START | ItemTouchHelper.END) : 0;
}

public void onSuggestionItemSwiped(@NonNull final RecyclerView.ViewHolder viewHolder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ private void saveImmediate() {
private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
int directions = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
if (shouldUseGridLayout(requireContext())) {
directions |= ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
directions |= ItemTouchHelper.START | ItemTouchHelper.END;
}
return new ItemTouchHelper.SimpleCallback(directions,
ItemTouchHelper.ACTION_STATE_IDLE) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,47 @@ import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import org.schabi.newpipe.R
import org.schabi.newpipe.util.LocalizeLayoutUtils

class FeedGroupCarouselDecoration(context: Context) : RecyclerView.ItemDecoration() {

private val marginStartEnd: Int
private val marginTopBottom: Int
private val marginBetweenItems: Int
private val isRTL: Boolean

init {
with(context.resources) {
marginStartEnd = getDimensionPixelOffset(R.dimen.feed_group_carousel_start_end_margin)
marginTopBottom = getDimensionPixelOffset(R.dimen.feed_group_carousel_top_bottom_margin)
marginBetweenItems = getDimensionPixelOffset(R.dimen.feed_group_carousel_between_items_margin)
}

isRTL = LocalizeLayoutUtils.isRTL(context)
}

override fun getItemOffsets(outRect: Rect, child: View, parent: RecyclerView, state: RecyclerView.State) {
val childAdapterPosition = parent.getChildAdapterPosition(child)
val childAdapterCount = parent.adapter?.itemCount ?: 0

outRect.set(marginBetweenItems, marginTopBottom, 0, marginTopBottom)
outRect.set(0, marginTopBottom, 0, marginTopBottom)

if (isRTL) {
outRect.right = marginBetweenItems

if (childAdapterPosition == 0) {
outRect.right = marginStartEnd
} else if (childAdapterPosition == childAdapterCount - 1) {
outRect.left = marginStartEnd
}
} else {
outRect.left = marginBetweenItems

if (childAdapterPosition == 0) {
outRect.left = marginStartEnd
} else if (childAdapterPosition == childAdapterCount - 1) {
outRect.right = marginStartEnd
if (childAdapterPosition == 0) {
outRect.left = marginStartEnd
} else if (childAdapterPosition == childAdapterCount - 1) {
outRect.right = marginStartEnd
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ public static WindowManager.LayoutParams retrievePopupLayoutParamsFromPrefs(
popupLayoutParamType(),
IDLE_WINDOW_FLAGS,
PixelFormat.TRANSLUCENT);
popupLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
popupLayoutParams.gravity = Gravity.START | Gravity.TOP;
popupLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;

final int centerX = (int) (player.getScreenWidth() / 2f - popupWidth / 2f);
Expand Down Expand Up @@ -558,7 +558,7 @@ public static WindowManager.LayoutParams buildCloseOverlayLayoutParams() {
flags,
PixelFormat.TRANSLUCENT);

closeOverlayLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
closeOverlayLayoutParams.gravity = Gravity.START | Gravity.TOP;
closeOverlayLayoutParams.softInputMode =
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
return closeOverlayLayoutParams;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public abstract class PlayQueueItemTouchCallback extends ItemTouchHelper.SimpleC
private static final int MAXIMUM_INITIAL_DRAG_VELOCITY = 25;

public PlayQueueItemTouchCallback() {
super(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT);
super(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.END);
}

public abstract void onMove(int sourceIndex, int targetIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public boolean onTouch(final View v, final MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();

x -= widget.getTotalPaddingLeft();
x -= widget.getTotalPaddingStart();
y -= widget.getTotalPaddingTop();

x += widget.getScrollX();
Expand Down
29 changes: 29 additions & 0 deletions app/src/main/java/org/schabi/newpipe/util/LocalizeLayoutUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.schabi.newpipe.util;

import android.content.Context;
import android.content.res.Configuration;
import android.view.View;

public final class LocalizeLayoutUtils {
private LocalizeLayoutUtils() { }

private static Boolean isRTL = null;

public static int getLayoutPosition(final boolean isRtl, final int count, final int position) {
return isRtl
? count - 1 - position
: position;
}

public static boolean isRTL(final Context context) {
if (isRTL != null) {
return isRTL;
}

final Configuration config = context.getResources().getConfiguration();

isRTL = config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;

return isRTL;
}
}
1 change: 0 additions & 1 deletion app/src/main/res/drawable/ic_play_arrow_shadow.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:viewportWidth="80"
android:viewportHeight="80">
<path
Expand Down
Loading