Proper support RTL (#1562)

This commit is contained in:
LoveSy 2022-01-26 11:50:47 +08:00 committed by GitHub
parent 66b196d5a7
commit 303def1344
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 114 additions and 54 deletions

View File

@ -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])

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html dir="@dir@">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">

View File

@ -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.

View File

@ -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) {

View File

@ -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 "<html><body>@body@</body></html>";
return "<html dir\"@dir@\"><body>@body@</body></html>";
}
}

View File

@ -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.EmptyStateAdapter<Scope
preferences.edit().putBoolean("filter_denylist", item.isChecked()).apply();
} else if (itemId == R.id.backup) {
LocalDateTime now = LocalDateTime.now();
fragment.backupLauncher.launch(String.format(Locale.ROOT,
fragment.backupLauncher.launch(String.format(LocaleDelegate.getDefaultLocale(),
"%s_%s.lsp", module.getAppName(), now.toString()));
return true;
} else if (itemId == R.id.restore) {

View File

@ -38,6 +38,8 @@ import org.lsposed.manager.util.NavUtil;
import java.time.LocalDateTime;
import rikka.material.app.LocaleDelegate;
public class CrashReportActivity extends AppCompatActivity {
ActivityCrashReportBinding binding;
@ -67,7 +69,7 @@ public class CrashReportActivity extends AppCompatActivity {
}
public String getAllErrorDetailsFromIntent(@NonNull Intent intent) {
String versionName = String.format("%s (%s)", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE);
String versionName = String.format(LocaleDelegate.getDefaultLocale(), "%s (%d)", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE);
return "Build version: " + versionName + " \n" +
"Current date: " + LocalDateTime.now() + " \n" +

View File

@ -45,8 +45,7 @@ import org.lsposed.manager.databinding.FragmentAppListBinding;
import org.lsposed.manager.util.BackupUtils;
import org.lsposed.manager.util.ModuleUtil;
import java.util.Locale;
import rikka.material.app.LocaleDelegate;
import rikka.recyclerview.RecyclerViewKt;
public class AppListFragment extends BaseFragment {
@ -79,7 +78,7 @@ public class AppListFragment extends BaseFragment {
binding.appBar.setLiftable(true);
String title;
if (module.userId != 0) {
title = String.format(Locale.ROOT, "%s (%d)", module.getAppName(), module.userId);
title = String.format(LocaleDelegate.getDefaultLocale(), "%s (%d)", module.getAppName(), module.userId);
} else {
title = module.getAppName();
}
@ -207,6 +206,7 @@ public class AppListFragment extends BaseFragment {
binding.recyclerView.setNestedScrollingEnabled(true);
}
});
searchView.findViewById(androidx.appcompat.R.id.search_edit_frame).setLayoutDirection(View.LAYOUT_DIRECTION_INHERIT);
scopeAdapter.onPrepareOptionsMenu(menu);
}

View File

@ -46,9 +46,8 @@ import org.lsposed.manager.util.NavUtil;
import org.lsposed.manager.util.UpdateUtil;
import org.lsposed.manager.util.chrome.LinkTransformationMethod;
import java.util.Locale;
import rikka.core.util.ClipboardUtils;
import rikka.material.app.LocaleDelegate;
public class HomeFragment extends BaseFragment {
@ -125,7 +124,7 @@ public class HomeFragment extends BaseFragment {
binding.statusTitle.setText(R.string.activated);
binding.statusIcon.setImageResource(R.drawable.ic_round_check_circle_24);
}
binding.statusSummary.setText(String.format(Locale.ROOT, "%s (%d) - %s",
binding.statusSummary.setText(String.format(LocaleDelegate.getDefaultLocale(), "%s (%d) - %s",
ConfigManager.getXposedVersionName(), ConfigManager.getXposedVersionCode(), ConfigManager.getApi()));
} else {
boolean isMagiskInstalled = ConfigManager.isMagiskInstalled();
@ -152,18 +151,18 @@ public class HomeFragment extends BaseFragment {
if (ConfigManager.isBinderAlive()) {
binding.apiVersion.setText(String.valueOf(ConfigManager.getXposedApiVersion()));
binding.api.setText(ConfigManager.getApi());
binding.frameworkVersion.setText(String.format(Locale.ROOT, "%s (%s)", ConfigManager.getXposedVersionName(), ConfigManager.getXposedVersionCode()));
binding.frameworkVersion.setText(String.format(LocaleDelegate.getDefaultLocale(), "%1$s (%2$d)", ConfigManager.getXposedVersionName(), ConfigManager.getXposedVersionCode()));
} else {
binding.apiVersion.setText(R.string.not_installed);
binding.api.setText(R.string.not_installed);
binding.frameworkVersion.setText(R.string.not_installed);
}
binding.managerVersion.setText(String.format(Locale.ROOT, "%s (%s)", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
binding.managerVersion.setText(String.format(LocaleDelegate.getDefaultLocale(), "%1$s (%2$d)", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
if (Build.VERSION.PREVIEW_SDK_INT != 0) {
binding.systemVersion.setText(String.format(Locale.ROOT, "%1$s Preview (API %2$d)", Build.VERSION.CODENAME, Build.VERSION.SDK_INT));
binding.systemVersion.setText(String.format(LocaleDelegate.getDefaultLocale(), "%1$s Preview (API %2$d)", Build.VERSION.CODENAME, Build.VERSION.SDK_INT));
} else {
binding.systemVersion.setText(String.format(Locale.ROOT, "%1$s (API %2$d)", Build.VERSION.RELEASE, Build.VERSION.SDK_INT));
binding.systemVersion.setText(String.format(LocaleDelegate.getDefaultLocale(), "%1$s (API %2$d)", Build.VERSION.RELEASE, Build.VERSION.SDK_INT));
}
binding.device.setText(getDevice());
@ -222,7 +221,7 @@ public class HomeFragment extends BaseFragment {
R.string.about_view_source_code,
"<b><a href=\"https://github.com/LSPosed/LSPosed\">GitHub</a></b>",
"<b><a href=\"https://t.me/LSPosed\">Telegram</a></b>"), 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();
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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);

View File

@ -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;

View File

@ -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;
});

View File

@ -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);

View File

@ -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" />

View File

@ -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" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -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" />
</com.google.android.material.appbar.SubtitleCollapsingToolbarLayout>

View File

@ -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">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/item_root"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
tools:ignore="RtlSymmetry">
<com.google.android.material.textview.MaterialTextView
@ -46,10 +46,10 @@
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItem"
android:textSize="16sp"
app:layout_constraintBottom_toTopOf="@id/description"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintBottom_toTopOf="@id/description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/lorem" />
<org.lsposed.manager.ui.widget.ScrollWebView
@ -57,37 +57,39 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:scrollbars="none"
android:textAppearance="?android:attr/textAppearanceSmall"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="@+id/title"
app:layout_constraintTop_toBottomOf="@id/title"
android:scrollbars="none"
tools:text="@tools:sample/lorem" />
<com.google.android.material.button.MaterialButton
android:id="@+id/view_assets"
style="@style/Widget.MaterialComponents.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/module_release_view_assets"
android:layout_marginTop="4dp"
android:text="@string/module_release_view_assets"
android:tooltipText="@string/module_release_view_assets"
app:icon="@drawable/ic_attach_file"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@id/description"
app:layout_constraintTop_toBottomOf="@id/description"
style="@style/Widget.MaterialComponents.Button.Icon" />
app:layout_constraintTop_toBottomOf="@id/description" />
<com.google.android.material.button.MaterialButton
android:id="@+id/open_in_browser"
style="@style/Widget.App.Button.OutlinedButton.IconOnly"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginTop="4dp"
android:tooltipText="@string/menu_open_in_browser"
app:icon="@drawable/ic_open_in_browser"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/view_assets"
app:layout_constraintTop_toBottomOf="@id/description"
style="@style/Widget.App.Button.OutlinedButton.IconOnly" />
app:layout_constraintTop_toBottomOf="@id/description" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@ -31,4 +31,8 @@
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
<style name="language_menu_style" parent="ThemeOverlay.Preference.SimpleMenuPreference.PopupMenu">
<item name="android:textAlignment">textStart</item>
</style>
</resources>

View File

@ -32,6 +32,7 @@
<item name="android:colorAccent">?colorPrimary</item>
<!-- Preference switch -->
<item name="android:colorControlActivated">?colorPrimary</item>
<item name="android:textAlignment">viewStart</item>
</style>
<style name="Theme.Light" parent="Base.AppTheme.Light" />
@ -48,6 +49,7 @@
<item name="android:colorAccent">?colorPrimary</item>
<!-- Preference switch -->
<item name="android:colorControlActivated">?colorPrimary</item>
<item name="android:textAlignment">viewStart</item>
</style>
<style name="Theme" parent="Base.AppTheme" />

View File

@ -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" />

View File

@ -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);