Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RTL layout support #7652

Closed
wants to merge 9 commits into from
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