diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index fe022628..c352758d 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -174,7 +174,7 @@ afterEvaluate {
doLast {
val langList = File(projectDir, "src/main/res").listFiles { dir ->
dir.name.startsWith("values-") && File(dir, "strings.xml").exists()
- }.orEmpty().map {
+ }.orEmpty().sorted().map {
it.name.substring(7).split("-", limit = 2)
}.map {
if (it.size == 1) Locale(it[0])
diff --git a/app/src/main/assets/webview/template.html b/app/src/main/assets/webview/template.html
index ea35654c..c6c9e00f 100644
--- a/app/src/main/assets/webview/template.html
+++ b/app/src/main/assets/webview/template.html
@@ -1,5 +1,5 @@
-
+
diff --git a/app/src/main/java/com/google/android/material/appbar/SubtitleCollapsingToolbarLayout.java b/app/src/main/java/com/google/android/material/appbar/SubtitleCollapsingToolbarLayout.java
index d26f4e7b..f2207f1a 100644
--- a/app/src/main/java/com/google/android/material/appbar/SubtitleCollapsingToolbarLayout.java
+++ b/app/src/main/java/com/google/android/material/appbar/SubtitleCollapsingToolbarLayout.java
@@ -95,6 +95,7 @@ public class SubtitleCollapsingToolbarLayout extends FrameLayout {
collapsingTextHelper = new SubtitleCollapsingTextHelper(this);
collapsingTextHelper.setTextSizeInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
+ collapsingTextHelper.setRtlTextDirectionHeuristicsEnabled(false);
TypedArray a = ThemeEnforcement.obtainStyledAttributes(
context,
@@ -1073,6 +1074,22 @@ public class SubtitleCollapsingToolbarLayout extends FrameLayout {
requestLayout();
}
+ /**
+ * Sets whether {@code TextDirectionHeuristics} should be used to determine whether the title text
+ * is RTL. Experimental Feature.
+ */
+ public void setRtlTextDirectionHeuristicsEnabled(boolean rtlTextDirectionHeuristicsEnabled) {
+ collapsingTextHelper.setRtlTextDirectionHeuristicsEnabled(rtlTextDirectionHeuristicsEnabled);
+ }
+
+ /**
+ * Gets whether {@code TextDirectionHeuristics} should be used to determine whether the title text
+ * is RTL. Experimental Feature.
+ */
+ public boolean isRtlTextDirectionHeuristicsEnabled() {
+ return collapsingTextHelper.isRtlTextDirectionHeuristicsEnabled();
+ }
+
/**
* Set the amount of visible height in pixels used to define when to trigger a scrim visibility
* change.
diff --git a/app/src/main/java/com/google/android/material/internal/SubtitleCollapsingTextHelper.java b/app/src/main/java/com/google/android/material/internal/SubtitleCollapsingTextHelper.java
index d9a156e7..95848058 100644
--- a/app/src/main/java/com/google/android/material/internal/SubtitleCollapsingTextHelper.java
+++ b/app/src/main/java/com/google/android/material/internal/SubtitleCollapsingTextHelper.java
@@ -85,6 +85,7 @@ public final class SubtitleCollapsingTextHelper {
@Nullable
private CharSequence titleToDraw, subtitleToDraw;
private boolean isRtl;
+ private boolean isRtlTextDirectionHeuristicsEnabled = true;
private boolean useTexture;
@Nullable
@@ -615,6 +616,14 @@ public final class SubtitleCollapsingTextHelper {
return expandedSubtitleTextSize;
}
+ public void setRtlTextDirectionHeuristicsEnabled(boolean rtlTextDirectionHeuristicsEnabled) {
+ isRtlTextDirectionHeuristicsEnabled = rtlTextDirectionHeuristicsEnabled;
+ }
+
+ public boolean isRtlTextDirectionHeuristicsEnabled() {
+ return isRtlTextDirectionHeuristicsEnabled;
+ }
+
private void calculateCurrentOffsets() {
calculateOffsets(expandedFraction);
}
@@ -889,10 +898,21 @@ public final class SubtitleCollapsingTextHelper {
}
private boolean calculateIsRtl(@NonNull CharSequence text) {
- final boolean defaultIsRtl = ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL;
+ final boolean defaultIsRtl = isDefaultIsRtl();
+ return isRtlTextDirectionHeuristicsEnabled
+ ? isTextDirectionHeuristicsIsRtl(text, defaultIsRtl)
+ : defaultIsRtl;
+ }
+
+ private boolean isDefaultIsRtl() {
+ return ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL;
+ }
+
+ private boolean isTextDirectionHeuristicsIsRtl(@NonNull CharSequence text, boolean defaultIsRtl) {
return (defaultIsRtl
? TextDirectionHeuristicsCompat.FIRSTSTRONG_RTL
- : TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR).isRtl(text, 0, text.length());
+ : TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR)
+ .isRtl(text, 0, text.length());
}
private void setInterpolatedTitleTextSize(float textSize) {
diff --git a/app/src/main/java/org/lsposed/manager/App.java b/app/src/main/java/org/lsposed/manager/App.java
index 85f5cccc..1a41b376 100644
--- a/app/src/main/java/org/lsposed/manager/App.java
+++ b/app/src/main/java/org/lsposed/manager/App.java
@@ -78,7 +78,7 @@ public class App extends Application {
return result.toString(StandardCharsets.UTF_8.name());
} catch (IOException e) {
Log.e(App.TAG, "read webview HTML", e);
- return "@body@";
+ return "@body@";
}
}
diff --git a/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java b/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java
index c0239b90..a515dcf9 100644
--- a/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java
+++ b/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java
@@ -78,12 +78,12 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
-import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import rikka.core.util.ResourceUtils;
+import rikka.material.app.LocaleDelegate;
import rikka.widget.switchbar.SwitchBar;
@SuppressLint("NotifyDataSetChanged")
@@ -253,7 +253,7 @@ public class ScopeAdapter extends EmptyStateRecyclerView.EmptyStateAdapterGitHub",
"Telegram"), HtmlCompat.FROM_HTML_MODE_LEGACY));
- binding.designAboutVersion.setText(String.format(Locale.ROOT, "%s (%s)", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
+ binding.designAboutVersion.setText(String.format(LocaleDelegate.getDefaultLocale(), "%s (%d)", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
return new BlurBehindDialogBuilder(requireContext())
.setView(binding.getRoot()).create();
}
diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/LogsFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/LogsFragment.java
index 7d98b3a6..fbbbb611 100644
--- a/app/src/main/java/org/lsposed/manager/ui/fragment/LogsFragment.java
+++ b/app/src/main/java/org/lsposed/manager/ui/fragment/LogsFragment.java
@@ -60,7 +60,6 @@ import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.Locale;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.zip.Deflater;
@@ -68,6 +67,7 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import rikka.core.os.FileUtils;
+import rikka.material.app.LocaleDelegate;
import rikka.recyclerview.RecyclerViewKt;
public class LogsFragment extends BaseFragment {
@@ -166,7 +166,7 @@ public class LogsFragment extends BaseFragment {
private void save() {
LocalDateTime now = LocalDateTime.now();
- String filename = String.format(Locale.ROOT, "LSPosed_%s.zip", now.toString());
+ String filename = String.format(LocaleDelegate.getDefaultLocale(), "LSPosed_%s.zip", now.toString());
saveLogsLauncher.launch(filename);
}
@@ -275,6 +275,8 @@ public class LogsFragment extends BaseFragment {
binding.recyclerView.setAdapter(adaptor);
layoutManager = new LinearLayoutManager(requireActivity());
binding.recyclerView.setLayoutManager(layoutManager);
+ // ltr even for rtl languages because of log format
+ binding.recyclerView.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
binding.swipeRefreshLayout.setProgressViewEndTarget(true, binding.swipeRefreshLayout.getProgressViewEndOffset());
RecyclerViewKt.fixEdgeEffect(binding.recyclerView, false, true);
binding.swipeRefreshLayout.setOnRefreshListener(adaptor::fullRefresh);
@@ -378,6 +380,7 @@ public class LogsFragment extends BaseFragment {
HorizontalScrollView horizontalScrollView = new HorizontalScrollView(getContext());
horizontalScrollView.setFillViewport(true);
horizontalScrollView.setHorizontalScrollBarEnabled(false);
+ horizontalScrollView.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
binding.swipeRefreshLayout.addView(horizontalScrollView);
horizontalScrollView.addView(binding.recyclerView);
binding.recyclerView.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/ModulesFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/ModulesFragment.java
index 0d0c03d5..e9a13048 100644
--- a/app/src/main/java/org/lsposed/manager/ui/fragment/ModulesFragment.java
+++ b/app/src/main/java/org/lsposed/manager/ui/fragment/ModulesFragment.java
@@ -90,6 +90,7 @@ import java.util.function.Consumer;
import java.util.stream.IntStream;
import rikka.core.util.ResourceUtils;
+import rikka.material.app.LocaleDelegate;
import rikka.recyclerview.RecyclerViewKt;
public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleListener, RepoLoader.RepoListener {
@@ -203,6 +204,7 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi
public void onViewDetachedFromWindow(View v) {
}
});
+ searchView.findViewById(androidx.appcompat.R.id.search_edit_frame).setLayoutDirection(View.LAYOUT_DIRECTION_INHERIT);
}
@Override
@@ -515,7 +517,7 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi
ModuleUtil.InstalledModule item = showList.get(position);
String appName;
if (item.userId != 0) {
- appName = String.format("%s (%s)", item.getAppName(), item.userId);
+ appName = String.format(LocaleDelegate.getDefaultLocale(), "%s (%d)", item.getAppName(), item.userId);
} else {
appName = item.getAppName();
}
diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/RepoFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/RepoFragment.java
index bef09ff3..8f71cb74 100644
--- a/app/src/main/java/org/lsposed/manager/ui/fragment/RepoFragment.java
+++ b/app/src/main/java/org/lsposed/manager/ui/fragment/RepoFragment.java
@@ -183,6 +183,7 @@ public class RepoFragment extends BaseFragment implements RepoLoader.RepoListene
binding.recyclerView.setNestedScrollingEnabled(true);
}
});
+ searchView.findViewById(androidx.appcompat.R.id.search_edit_frame).setLayoutDirection(View.LAYOUT_DIRECTION_INHERIT);
int sort = App.getPreferences().getInt("repo_sort", 0);
if (sort == 0) {
menu.findItem(R.id.item_sort_by_name).setChecked(true);
diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/RepoItemFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/RepoItemFragment.java
index 5137d399..0d9e9abc 100644
--- a/app/src/main/java/org/lsposed/manager/ui/fragment/RepoItemFragment.java
+++ b/app/src/main/java/org/lsposed/manager/ui/fragment/RepoItemFragment.java
@@ -79,7 +79,6 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
-import java.util.Locale;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -87,6 +86,7 @@ import okhttp3.Headers;
import okhttp3.Request;
import okhttp3.Response;
import rikka.core.util.ResourceUtils;
+import rikka.material.app.LocaleDelegate;
import rikka.recyclerview.RecyclerViewKt;
import rikka.widget.borderview.BorderView;
@@ -152,10 +152,16 @@ public class RepoItemFragment extends BaseFragment implements RepoLoader.RepoLis
setting.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
setting.setTextZoom(80);
String body;
- if (ResourceUtils.isNightMode(getResources().getConfiguration())) {
- body = App.HTML_TEMPLATE_DARK.get().replace("@body@", text);
+ String direction;
+ if (getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ direction = "rtl";
} else {
- body = App.HTML_TEMPLATE.get().replace("@body@", text);
+ direction = "ltr";
+ }
+ if (ResourceUtils.isNightMode(getResources().getConfiguration())) {
+ body = App.HTML_TEMPLATE_DARK.get().replace("@dir@", direction).replace("@body@", text);
+ } else {
+ body = App.HTML_TEMPLATE.get().replace("@dir@", direction).replace("@body@", text);
}
view.setWebViewClient(new WebViewClient() {
@Override
@@ -353,12 +359,12 @@ public class RepoItemFragment extends BaseFragment implements RepoLoader.RepoLis
if (channel.equals(channels[0])) {
this.items = releases.parallelStream().filter(t -> {
if (t.getIsPrerelease()) return false;
- var name = t.getName().toLowerCase(Locale.ROOT);
+ var name = t.getName().toLowerCase(LocaleDelegate.getDefaultLocale());
return !name.startsWith("snapshot") && !name.startsWith("nightly");
}).collect(Collectors.toList());
} else if (channel.equals(channels[1])) {
this.items = releases.parallelStream().filter(t -> {
- var name = t.getName().toLowerCase(Locale.ROOT);
+ var name = t.getName().toLowerCase(LocaleDelegate.getDefaultLocale());
return !name.startsWith("snapshot") && !name.startsWith("nightly");
}).collect(Collectors.toList());
} else this.items = releases;
diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java
index e4ca7705..6461bf39 100644
--- a/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java
+++ b/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java
@@ -77,10 +77,10 @@ public class SettingsFragment extends BaseFragment {
.add(R.id.setting_container, new PreferenceFragment()).commitNow();
}
if (ConfigManager.isBinderAlive()) {
- binding.toolbar.setSubtitle(String.format(Locale.ROOT, "%s (%d) - %s",
+ binding.toolbar.setSubtitle(String.format(LocaleDelegate.getDefaultLocale(), "%s (%d) - %s",
ConfigManager.getXposedVersionName(), ConfigManager.getXposedVersionCode(), ConfigManager.getApi()));
} else {
- binding.toolbar.setSubtitle(String.format(Locale.ROOT, "%s (%d) - %s",
+ binding.toolbar.setSubtitle(String.format(LocaleDelegate.getDefaultLocale(), "%s (%d) - %s",
BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, getString(R.string.not_installed)));
}
return binding.getRoot();
@@ -163,7 +163,7 @@ public class SettingsFragment extends BaseFragment {
backup.setEnabled(installed);
backup.setOnPreferenceClickListener(preference -> {
LocalDateTime now = LocalDateTime.now();
- backupLauncher.launch(String.format(Locale.ROOT,
+ backupLauncher.launch(String.format(LocaleDelegate.getDefaultLocale(),
"LSPosed_%s.lsp", now.toString()));
return true;
});
diff --git a/app/src/main/java/org/lsposed/manager/util/UpdateUtil.java b/app/src/main/java/org/lsposed/manager/util/UpdateUtil.java
index 1240039e..15f15e57 100644
--- a/app/src/main/java/org/lsposed/manager/util/UpdateUtil.java
+++ b/app/src/main/java/org/lsposed/manager/util/UpdateUtil.java
@@ -16,13 +16,13 @@ import java.io.File;
import java.io.IOException;
import java.time.Instant;
import java.time.ZoneOffset;
-import java.util.Locale;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Request;
import okhttp3.Response;
import okio.Okio;
+import rikka.material.app.LocaleDelegate;
public class UpdateUtil {
public static void loadRemoteVersion() {
@@ -42,7 +42,7 @@ public class UpdateUtil {
var notes = info.get("body").getAsString();
var assetsArray = info.getAsJsonArray("assets");
for (var assets : assetsArray) {
- checkAssets(assets.getAsJsonObject(), notes, api.toLowerCase(Locale.ROOT));
+ checkAssets(assets.getAsJsonObject(), notes, api.toLowerCase(LocaleDelegate.getDefaultLocale()));
}
} catch (Throwable t) {
Log.e(App.TAG, t.getMessage(), t);
diff --git a/app/src/main/res/layout-sw600dp/activity_main.xml b/app/src/main/res/layout-sw600dp/activity_main.xml
index ca466e8b..4a622f17 100644
--- a/app/src/main/res/layout-sw600dp/activity_main.xml
+++ b/app/src/main/res/layout-sw600dp/activity_main.xml
@@ -35,8 +35,8 @@
android:background="?android:colorBackground"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintLeft_toRightOf="@id/nav"
- app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintStart_toEndOf="@id/nav"
+ app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/main_nav" />
@@ -45,7 +45,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:menu="@menu/navigation_menu"
app:menuGravity="center" />
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 4c3942ed..7e956da9 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -36,8 +36,8 @@
android:background="?android:colorBackground"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@id/nav"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/main_nav" />
@@ -47,8 +47,8 @@
android:layout_height="wrap_content"
app:fitsSystemWindowsInsets="bottom"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
app:menu="@menu/navigation_menu" />
diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml
index 8afd2f40..cab64c86 100644
--- a/app/src/main/res/layout/fragment_settings.xml
+++ b/app/src/main/res/layout/fragment_settings.xml
@@ -52,6 +52,7 @@
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:elevation="0dp"
+ android:theme="@style/ThemeOverlay.MaterialComponents.ActionBar"
app:layout_collapseMode="pin" />
diff --git a/app/src/main/res/layout/item_repo_release.xml b/app/src/main/res/layout/item_repo_release.xml
index ecf41923..b1cff881 100644
--- a/app/src/main/res/layout/item_repo_release.xml
+++ b/app/src/main/res/layout/item_repo_release.xml
@@ -25,19 +25,19 @@
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:clickable="true"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:focusable="true"
android:minHeight="?attr/listPreferredItemHeight"
android:paddingVertical="16dp"
android:paddingStart="16dp"
- android:paddingEnd="24dp"
- android:clipToPadding="false"
- android:clipChildren="false">
+ android:paddingEnd="24dp">
+ app:layout_constraintTop_toBottomOf="@id/description" />
+ app:layout_constraintTop_toBottomOf="@id/description" />
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 541743ad..b4a15b8f 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -31,4 +31,8 @@
- ?android:attr/textColorSecondary
+
+
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 5e972267..ab02f42b 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -32,6 +32,7 @@
- ?colorPrimary
- ?colorPrimary
+ - viewStart
@@ -48,6 +49,7 @@
- ?colorPrimary
- ?colorPrimary
+ - viewStart
diff --git a/app/src/main/res/xml/prefs.xml b/app/src/main/res/xml/prefs.xml
index 6d24c3b3..a660f218 100644
--- a/app/src/main/res/xml/prefs.xml
+++ b/app/src/main/res/xml/prefs.xml
@@ -36,6 +36,7 @@
android:defaultValue="SYSTEM"
android:icon="@drawable/ic_outline_language_24"
android:key="language"
+ android:popupTheme="@style/language_menu_style"
android:summary="%s"
android:title="@string/settings_language" />
diff --git a/daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java b/daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java
index 59c92c6e..1df9eb83 100644
--- a/daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java
+++ b/daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java
@@ -81,7 +81,7 @@ public class ServiceManager {
}
}
Log.i(TAG, "starting server...");
- Log.i(TAG, String.format("version %s (%s)", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
+ Log.i(TAG, String.format("version %s (%d)", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
Log.e(TAG, "Uncaught exception", e);