Start fixes for last PR.

pull/149/head
Fox2Code 2 years ago
parent 1ee0aa31ab
commit 5d2a08d818

@ -1,15 +0,0 @@
# LEGALS
## Fox's Magisk Module Manager
- Maintained: Yes
- Maintainers
- [Fox2Code](https://github.com/Fox2Code)
- [androidacybot](https://github.com/androidacybot)
- License: [<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-law mr-2"><path fill-rule="evenodd" d="M8.75.75a.75.75 0 00-1.5 0V2h-.984c-.305 0-.604.08-.869.23l-1.288.737A.25.25 0 013.984 3H1.75a.75.75 0 000 1.5h.428L.066 9.192a.75.75 0 00.154.838l.53-.53-.53.53v.001l.002.002.002.002.006.006.016.015.045.04a3.514 3.514 0 00.686.45A4.492 4.492 0 003 11c.88 0 1.556-.22 2.023-.454a3.515 3.515 0 00.686-.45l.045-.04.016-.015.006-.006.002-.002.001-.002L5.25 9.5l.53.53a.75.75 0 00.154-.838L3.822 4.5h.162c.305 0 .604-.08.869-.23l1.289-.737a.25.25 0 01.124-.033h.984V13h-2.5a.75.75 0 000 1.5h6.5a.75.75 0 000-1.5h-2.5V3.5h.984a.25.25 0 01.124.033l1.29.736c.264.152.563.231.868.231h.162l-2.112 4.692a.75.75 0 00.154.838l.53-.53-.53.53v.001l.002.002.002.002.006.006.016.015.045.04a3.517 3.517 0 00.686.45A4.492 4.492 0 0013 11c.88 0 1.556-.22 2.023-.454a3.512 3.512 0 00.686-.45l.045-.04.01-.01.006-.005.006-.006.002-.002.001-.002-.529-.531.53.53a.75.75 0 00.154-.838L13.823 4.5h.427a.75.75 0 000-1.5h-2.234a.25.25 0 01-.124-.033l-1.29-.736A1.75 1.75 0 009.735 2H8.75V.75zM1.695 9.227c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L3 6.327l-1.305 2.9zm10 0c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L13 6.327l-1.305 2.9z"></path></svg> LGPL-3.0 license](https://github.com/Fox2Code/FoxMagiskModuleManager/blob/master/LICENCE)
## Rosetta ([Fork](https://github.com/iamjazzar/rosetta))
- Maintained: No
- Maintainers
- [iamjazzar](https://github.com/iamjazzar)
- Others unknown
- License: [<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-law mr-2"><path fill-rule="evenodd" d="M8.75.75a.75.75 0 00-1.5 0V2h-.984c-.305 0-.604.08-.869.23l-1.288.737A.25.25 0 013.984 3H1.75a.75.75 0 000 1.5h.428L.066 9.192a.75.75 0 00.154.838l.53-.53-.53.53v.001l.002.002.002.002.006.006.016.015.045.04a3.514 3.514 0 00.686.45A4.492 4.492 0 003 11c.88 0 1.556-.22 2.023-.454a3.515 3.515 0 00.686-.45l.045-.04.016-.015.006-.006.002-.002.001-.002L5.25 9.5l.53.53a.75.75 0 00.154-.838L3.822 4.5h.162c.305 0 .604-.08.869-.23l1.289-.737a.25.25 0 01.124-.033h.984V13h-2.5a.75.75 0 000 1.5h6.5a.75.75 0 000-1.5h-2.5V3.5h.984a.25.25 0 01.124.033l1.29.736c.264.152.563.231.868.231h.162l-2.112 4.692a.75.75 0 00.154.838l.53-.53-.53.53v.001l.002.002.002.002.006.006.016.015.045.04a3.517 3.517 0 00.686.45A4.492 4.492 0 0013 11c.88 0 1.556-.22 2.023-.454a3.512 3.512 0 00.686-.45l.045-.04.01-.01.006-.005.006-.006.002-.002.001-.002-.529-.531.53.53a.75.75 0 00.154-.838L13.823 4.5h.427a.75.75 0 000-1.5h-2.234a.25.25 0 01-.124-.033l-1.29-.736A1.75 1.75 0 009.735 2H8.75V.75zM1.695 9.227c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L3 6.327l-1.305 2.9zm10 0c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L13 6.327l-1.305 2.9z"></path></svg> MIT license](https://github.com/iamjazzar/rosetta/blob/master/LICENSE)

@ -71,10 +71,7 @@ android {
}
aboutLibraries {
additionalLicenses = [
"LGPL_3_0_only",
"rosetta"
]
additionalLicenses = ["LGPL_3_0_only"]
}
configurations {
@ -101,7 +98,7 @@ dependencies {
implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.3'
implementation 'com.squareup.okhttp3:okhttp-brotli:4.9.3'
implementation 'com.github.topjohnwu.libsu:io:5.0.1'
implementation project(":rosetta")
implementation 'com.github.Fox2Code:RosettaX:46ec630055'
// Markdown
implementation "io.noties.markwon:core:4.6.2"

@ -23,6 +23,7 @@ import com.fox2code.mmm.compat.CompatThemeWrapper;
import com.fox2code.mmm.installer.InstallerInitializer;
import com.fox2code.mmm.utils.GMSProviderInstaller;
import com.fox2code.mmm.utils.Http;
import com.fox2code.rosettax.LanguageSwitcher;
import com.google.android.material.color.DynamicColors;
import com.topjohnwu.superuser.Shell;
@ -307,6 +308,8 @@ public class MainApplication extends CompatApplication {
} else {
MainApplication.firstBoot = bootPrefs.getBoolean("first_boot", false);
}
// Force initialize language early.
new LanguageSwitcher(this);
this.updateTheme();
// Update SSL Ciphers if update is possible
GMSProviderInstaller.installIfNeeded(this);

@ -31,7 +31,7 @@ public enum ActionButtonType {
INFO() {
@Override
public void update(Chip button, ModuleHolder moduleHolder) {
button.setChipIcon(button.getContext().getResources().getDrawable(R.drawable.ic_baseline_info_24));
button.setChipIcon(button.getContext().getDrawable(R.drawable.ic_baseline_info_24));
button.setText(R.string.description);
}
@ -70,7 +70,7 @@ public enum ActionButtonType {
int icon = moduleHolder.hasUpdate() ?
R.drawable.ic_baseline_update_24 :
R.drawable.ic_baseline_system_update_24;
button.setChipIcon(button.getContext().getResources().getDrawable(icon));
button.setChipIcon(button.getContext().getDrawable(icon));
if (moduleHolder.hasUpdate()) {
button.setText(R.string.update);
} else {
@ -150,7 +150,7 @@ public enum ActionButtonType {
moduleHolder.hasFlag(ModuleInfo.FLAGS_MODULE_ACTIVE)) ?
R.drawable.ic_baseline_delete_24 :
R.drawable.ic_baseline_delete_forever_24;
button.setChipIcon(button.getContext().getResources().getDrawable(icon));
button.setChipIcon(button.getContext().getDrawable(icon));
button.setText(R.string.uninstall);
}
@ -190,7 +190,7 @@ public enum ActionButtonType {
CONFIG() {
@Override
public void update(Chip button, ModuleHolder moduleHolder) {
button.setChipIcon(button.getContext().getResources().getDrawable(R.drawable.ic_baseline_app_settings_alt_24));
button.setChipIcon(button.getContext().getDrawable(R.drawable.ic_baseline_app_settings_alt_24));
button.setText(R.string.config);
}
@ -209,7 +209,7 @@ public enum ActionButtonType {
@Override
public void update(Chip button, ModuleHolder moduleHolder) {
ModuleInfo moduleInfo = moduleHolder.getMainModuleInfo();
button.setChipIcon(button.getContext().getResources().getDrawable(supportIconForUrl(moduleInfo.support)));
button.setChipIcon(button.getContext().getDrawable(supportIconForUrl(moduleInfo.support)));
button.setText(R.string.support);
}
@ -228,7 +228,7 @@ public enum ActionButtonType {
} else if (moduleInfo.donate.startsWith("https://www.patreon.com/")) {
icon = R.drawable.ic_patreon;
}
button.setChipIcon(button.getContext().getResources().getDrawable(icon));
button.setChipIcon(button.getContext().getDrawable(icon));
button.setText(R.string.donate);
}
@ -266,7 +266,7 @@ public enum ActionButtonType {
}
public void update(Chip button, ModuleHolder moduleHolder) {
button.setChipIcon(button.getContext().getResources().getDrawable(this.iconId));
button.setChipIcon(button.getContext().getDrawable(this.iconId));
}
public abstract void doAction(Chip button, ModuleHolder moduleHolder);

@ -26,7 +26,7 @@ import com.fox2code.mmm.repo.RepoManager;
import com.fox2code.mmm.utils.Http;
import com.fox2code.mmm.utils.IntentHelper;
import com.ahmedjazzar.rosetta.LanguageSwitcher;
import com.fox2code.rosettax.LanguageSwitcher;
import com.mikepenz.aboutlibraries.LibsBuilder;
import com.topjohnwu.superuser.internal.UiThreadHandler;
@ -98,9 +98,6 @@ public class SettingsActivity extends CompatActivity {
return true;
});
// This is the locale that you wanna your app to launch with.
String firstLaunchLocale = "en";
// Warning! Locales that are't exist will crash the app
HashSet<String> supportedLocales = new HashSet<>();
supportedLocales.add("cs");
@ -120,13 +117,13 @@ public class SettingsActivity extends CompatActivity {
supportedLocales.add("vi");
supportedLocales.add("zh-rCH");
supportedLocales.add("zh-rTW");
supportedLocales.add(firstLaunchLocale);
supportedLocales.add("en");
Preference languageSelector = findPreference("pref_language_selector");
languageSelector.setOnPreferenceClickListener(preference -> {
LanguageSwitcher ls = new LanguageSwitcher(getActivity(), new Locale(firstLaunchLocale));
ls.showChangeLanguageDialog(getActivity());
LanguageSwitcher ls = new LanguageSwitcher(getActivity());
ls.setSupportedStringLocales(supportedLocales);
ls.showChangeLanguageDialog(getActivity());
return true;
});

33
rosetta/.gitignore vendored

@ -1,33 +0,0 @@
# files for the dex VM
*.dex
# Java class files
*.class
# generated files
bin/
gen/
# Local configuration file (sdk path, etc)
local.properties
# Windows thumbnail db
Thumbs.db
# OSX files
.DS_Store
# Eclipse project files
.classpath
.project
# Android Studio
*.iml
.idea
#.idea/workspace.xml - remove # and delete .idea if it better suit your needs.
.gradle
build/
#NDK
obj/

@ -1,66 +0,0 @@
apply plugin: 'com.android.library'
ext {
bintrayRepo = 'maven'
bintrayName = 'Rosetta'
publishedGroupId = 'com.ahmedjazzar.rosetta'
libraryName = 'Rosetta'
artifact = 'rosetta'
libraryDescription = 'Android library that lets your app supporting multiple languages ' +
'without any concern from you as a developer.'
siteUrl = 'https://github.com/ahmedaljazzar/rosetta'
gitUrl = 'https://github.com/ahmedaljazzar/rosetta.git'
libraryVersion = '1.0.1'
developerId = 'ahmedaljazzar'
developerName = 'Ahmed Jazzar'
developerEmail = 'me@ahmedjazzar.com'
licenseName = 'MIT'
licenseUrl = 'https://opensource.org/licenses/MIT'
allLicenses = ["MIT"]
}
buildscript {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.0'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
}
}
android {
compileSdkVersion 32
buildToolsVersion "23.0.3"
defaultConfig {
minSdkVersion 21
targetSdkVersion 32
versionCode 3
versionName "1.0.1"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:1.10.19'
implementation 'com.google.android.material:material:1.6.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
}

@ -1,10 +0,0 @@
/**
* Automatically generated file. DO NOT MODIFY
*/
package com.ahmedjazzar.rosetta;
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String LIBRARY_PACKAGE_NAME = "com.ahmedjazzar.rosetta";
public static final String BUILD_TYPE = "debug";
}

@ -1,23 +0,0 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Fox builds props mods
org.gradle.parallel=true
android.enableR8.fullMode=true

@ -1,17 +0,0 @@
# TODO: Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/ahmedjazzar/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

@ -1,5 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ahmedjazzar.rosetta">
<application android:supportsRtl="true" />
</manifest>

@ -1,179 +0,0 @@
package com.ahmedjazzar.rosetta;
import android.app.Activity;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
import java.util.HashSet;
import java.util.Locale;
/**
* This class is the application door to this Library. It handles the ongoing and outgoing requests,
* initializations, preferences, ..
* I think that there's no need for logging here because other classes already handle logs for these
* actions based on their returned results.
*
* Created by ahmedjazzar on 1/16/16.
*/
public class LanguageSwitcher {
private Context mContext;
private LocalesPreferenceManager mLocalesPreferences;
private final String TAG = LanguageSwitcher.class.getName();
/**
* A constructor that accepts context and sets the base and first launch locales to en_US
* @param context the context of the dealer
*/
public LanguageSwitcher(@NonNull Context context) {
this(context, Locale.US);
}
/**
* A constructor that accepts context and sets the base and first launch locales to
* firstLaunchLocale.
*
* NOTE: Please do not use unless:
* 1. You wanna set your locales by calling {@link LanguageSwitcher#setSupportedLocales}
* 2. You know for sure that the preferred locale is as same as your base locale
*
* @param context the context of the dealer
* @param firstLaunchLocale the locale that owner wanna use at its first launch
*/
public LanguageSwitcher(@NonNull Context context, Locale firstLaunchLocale) {
this(context, firstLaunchLocale, firstLaunchLocale);
}
/**
* This is supposed to be more specific; It has three parameters cover all owner needs
* @param context the context of the dealer
* @param firstLaunchLocale the locale that owner wanna use at its first launch
* @param baseLocale the locale that used in the main xml strings file (most likely 'en')
*/
public LanguageSwitcher(@NonNull Context context, Locale firstLaunchLocale, Locale baseLocale) {
this.mContext = context.getApplicationContext();
this.mLocalesPreferences =
new LocalesPreferenceManager(context, firstLaunchLocale, baseLocale);
// initializing Locales utils needed objects (detector, preferences)
LocalesUtils.setDetector(new LocalesDetector(this.mContext));
LocalesUtils.setLocalesPreferenceManager(mLocalesPreferences);
// Setting app locale to match the user preferred one
LocalesUtils.setAppLocale(mContext,
mLocalesPreferences
.getPreferredLocale(LocalesPreferenceManager.USER_PREFERRED_LOCALE));
}
/**
* Responsible for displaying Change dialog fragment
*/
public void showChangeLanguageDialog(FragmentActivity activity) {
new LanguagesListDialogFragment()
.show(activity.getSupportFragmentManager(), TAG);
}
/**
*
* @return the application supported locales
*/
public HashSet<Locale> getLocales() {
return LocalesUtils.getLocales();
}
/**
* Sets the app locales from a string Set
* @param sLocales supported locales in a String form
*/
public void setSupportedStringLocales(HashSet<String> sLocales) {
HashSet<Locale> locales = new HashSet<>();
for (String sLocale: sLocales) {
locales.add(new Locale(sLocale));
}
this.setSupportedLocales(locales);
}
/**
* set supported locales from the given Set
* @param locales supported locales
*/
public void setSupportedLocales(HashSet<Locale> locales) {
LocalesUtils.setSupportedLocales(locales);
}
/**
* Sets the supported locales after fetching there availability using fetchAvailableLocales
* method
* @param stringId the string that this library gonna use to detect current app available
* locales
*/
public void setSupportedLocales(int stringId) {
this.setSupportedLocales(this.fetchAvailableLocales(stringId));
}
/**
* Fetching the application available locales inside the resources folder dynamically
* @param stringId the string that this library gonna use to detect current app available
* locales
* @return a set of detected application locales
*/
public HashSet<Locale> fetchAvailableLocales(int stringId) {
return LocalesUtils.fetchAvailableLocales(stringId);
}
/**
* Setting the application locale manually
* @param newLocale the locale in a string format
* @param activity the current activity in order to refresh the app
*
* @return true if the operation succeed, false otherwise
*/
public boolean setLocale(String newLocale, Activity activity) {
return setLocale(new Locale(newLocale), activity);
}
/**
* Setting the application locale manually
* @param newLocale the desired locale
* @param activity the current activity in order to refresh the app
*
* @return true if the operation succeed, false otherwise
*/
public boolean setLocale(Locale newLocale, Activity activity) {
return LocalesUtils.setLocale(newLocale, activity);
}
/**
*
* @return the first launch locale
*/
public Locale getLaunchLocale() {
return LocalesUtils.getLaunchLocale();
}
/**
*
* @return the current locale
*/
public Locale getCurrentLocale() {
return LocalesUtils.getCurrentLocale(this.mContext);
}
/**
* Return to the first launch locale
* @param activity the current activity in order to refresh the app
*
* @return true if the operation succeed, false otherwise
*/
public boolean switchToLaunch(Activity activity) {
return setLocale(getLaunchLocale(), activity);
}
}

@ -1,180 +0,0 @@
package com.ahmedjazzar.rosetta;
import android.app.Dialog;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList;
import java.util.Locale;
/**
* This fragment is responsible for displaying the supported locales and performing any necessary
* action that allows user to select, cancel, and commit changes.
*
* Created by ahmedjazzar on 1/19/16.
*/
public class LanguagesListDialogFragment extends DialogFragment {
private final int DIALOG_TITLE_ID = R.string.language;
private final int DIALOG_POSITIVE_ID = R.string.ok;
private final int DIALOG_NEGATIVE_ID = R.string.cancel;
private int mSelectedLanguage = -1;
private final Logger mLogger;
public LanguagesListDialogFragment() {
String TAG = LanguagesListDialogFragment.class.getName();
this.mLogger = new Logger(TAG);
}
/**
* @return a Dialog fragment
*/
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity());
mLogger.debug("Building DialogFragment.");
builder.setTitle(getString(DIALOG_TITLE_ID))
.setSingleChoiceItems(
getLanguages(),
getCurrentLocaleIndex(),
(dialogInterface, which) -> onLanguageSelectedLocalized(which))
.setPositiveButton(
getString(DIALOG_POSITIVE_ID).toUpperCase(),
(dialogInterface, which) -> onPositiveClick())
.setNegativeButton(
getString(DIALOG_NEGATIVE_ID).toUpperCase(),
(dialogInterface, which) -> onNegativeClick());
mLogger.verbose("DialogFragment built.");
return builder.create();
}
/**
* @param which the position of the selected locale
*/
protected void onLanguageSelected(int which) {
// just update the selected locale
mSelectedLanguage = which;
}
/**
* Localizing the dialog buttons and title
* @param which the position of the selected locale
*/
protected void onLanguageSelectedLocalized(int which) {
// update the selected locale
mSelectedLanguage = which;
AlertDialog dialog = (AlertDialog) getDialog();
mLogger.debug("Displaying dialog main strings in the selected " +
"locale");
onLanguageSelectedLocalized(
which,
null,
dialog.getButton(AlertDialog.BUTTON_POSITIVE),
dialog.getButton(AlertDialog.BUTTON_NEGATIVE));
}
/**
* the position of the selected locale given the ids
* @param which the position of the selected locale
* @param titleView dialog's title text view
* @param positiveButton positive button
* @param negativeButton negative button
*/
protected void onLanguageSelectedLocalized(int which, TextView titleView, Button positiveButton,
Button negativeButton) {
// update the selected locale
mSelectedLanguage = which;
Locale locale = LocalesUtils.getLocaleFromIndex(mSelectedLanguage);
AlertDialog dialog = (AlertDialog) getDialog();
FragmentActivity activity = getActivity();
mLogger.debug("Displaying dialog main strings in the selected " +
"locale");
assert activity != null;
String LocalizedTitle = LocalesUtils.getInSpecificLocale(activity, locale, DIALOG_TITLE_ID);
if(titleView == null) {
// Display dialog title in the selected locale
assert dialog != null;
dialog.setTitle(LocalizedTitle);
} else {
titleView.setText(LocalizedTitle);
}
// Display positive button text in the selected locale
positiveButton.setText(LocalesUtils.getInSpecificLocale(
activity, locale, DIALOG_POSITIVE_ID));
// Display negative button text in the selected locale
negativeButton.setText(LocalesUtils.getInSpecificLocale(
activity, locale, DIALOG_NEGATIVE_ID));
}
/**
* called when the user approved changing locale
*/
protected void onPositiveClick() {
// if the user did not select the same locale go ahead, else ignore
if (mSelectedLanguage != -1 &&
mSelectedLanguage != LocalesUtils.getCurrentLocaleIndex()) {
// Try changing the locale
if (LocalesUtils.setAppLocale(
getActivity(), mSelectedLanguage)) {
mLogger.info("App locale changed successfully.");
LocalesUtils.refreshApplication(requireActivity());
} else {
mLogger.error("Unsuccessful trial to change the App locale.");
// TODO: notify the user that his request not placed
}
} else {
dismiss();
}
}
/**
* called when the user discarded changing locale
*/
protected void onNegativeClick() {
mLogger.verbose("User discarded changing language.");
mLogger.debug("Return to the original locale.");
this.onLanguageSelectedLocalized(this.getCurrentLocaleIndex());
}
/**
*
* @return available languages
*/
protected String[] getLanguages() {
ArrayList<String> languages = LocalesUtils.getLocalesWithDisplayName();
return languages.toArray(new String[languages.size()]);
}
/**
*
* @return the index of the locale that app is using now
*/
protected int getCurrentLocaleIndex() {
return LocalesUtils.getCurrentLocaleIndex();
}
}

@ -1,153 +0,0 @@
package com.ahmedjazzar.rosetta;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.util.DisplayMetrics;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
/**
* This class detects the application available locales inside the resources based on a string id,
* it's not so accurate and expects another methodologies. Next release may hold a better algorithms
* for detecting strings' languages and availability inside apps.
*
* Created by ahmedjazzar on 1/16/16.
*/
class LocalesDetector {
private final Context mContext;
private Logger mLogger;
private final String TAG = LocalesDetector.class.getName();
LocalesDetector(Context context) {
this.mContext = context;
this.mLogger = new Logger(TAG);
}
/**
* this method takes an experimental string id to see if it's exists in other available
* locales inside the app than default locale.
* NOTE: Even if you have a folder named values-ar it doesn't mean you have any resources
* there
*
* @param stringId experimental string id to discover locales
* @return the discovered locales
*/
HashSet<Locale> fetchAvailableLocales(int stringId) {
DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
Configuration conf = mContext.getResources().getConfiguration();
Locale originalLocale = conf.locale;
Locale baseLocale = LocalesUtils.getBaseLocale();
conf.locale = baseLocale;
ArrayList<String> references = new ArrayList<>();
references.add(new Resources(mContext.getAssets(), dm, conf).getString(stringId));
HashSet<Locale> result = new HashSet<>();
result.add(baseLocale);
for(String loc : mContext.getAssets().getLocales()) {
if(loc.isEmpty()){
continue;
}
Locale l;
boolean referencesUpdateLock = false;
l = Locale.forLanguageTag(loc);
conf.locale = l;
//TODO: put it in a method
String tmpString = new Resources(mContext.getAssets(), dm, conf).getString(stringId);
for (String reference: references) {
if(reference.equals(tmpString)){
// TODO: check its original locale
referencesUpdateLock = true;
break;
}
}
if(!referencesUpdateLock) {
result.add(l);
references.add(tmpString);
}
}
conf.locale = originalLocale; // to restore our guy initial state
return result;
}
/**
* TODO: return the selected one instead
* @return application current locale
*/
Locale getCurrentLocale() {
return mContext.getResources().getConfiguration().locale;
}
/**
* TODO: what if a user didn't provide a closer email at all?
* TODO: check the closest locale not the first identified
*
* This method should provide a locale that is close to the given one in the parameter, it's
* currently checking the language only if in case the detector detects the string in other
* language.
*
* @param locale mostly the locale that's not detected or provided
* @return the index of the most close locale to the given locale. -1 if not detected
*/
int detectMostClosestLocale(Locale locale) {
mLogger.debug("Start detecting a close locale to: ");
int index = 0;
for (Locale loc: LocalesUtils.getLocales()) {
if(loc.getDisplayLanguage().equals(locale.getDisplayLanguage())) {
mLogger.info("The locale: '" + loc + "' has been detected as a closer locale to: '"
+ locale + "'");
return index;
}
index++;
}
mLogger.debug("No closer locales founded.");
return -1;
}
/**
* This method validate locales by checking if they are available of they contain wrong letter
* case and adding the valid ones in a clean set.
* @param locales to be checked
* @return valid locales
*/
HashSet<Locale> validateLocales(HashSet<Locale> locales) {
mLogger.debug("Validating given locales..");
for (Locale l:LocalesUtils.getPseudoLocales()) {
if(locales.remove(l)) {
mLogger.info("Pseudo locale '" + l + "' has been removed.");
}
}
HashSet<Locale> cleanLocales = new HashSet<>();
Locale[] androidLocales = Locale.getAvailableLocales();
for (Locale locale: locales) {
if (Arrays.asList(androidLocales).contains(locale)) {
cleanLocales.add(locale);
} else {
mLogger.error("Invalid passed locale: " + locale);
mLogger.warn("Invalid specified locale: '" + locale + "', has been discarded");
}
}
mLogger.debug("passing validated locales.");
return cleanLocales;
}
}

@ -1,161 +0,0 @@
package com.ahmedjazzar.rosetta;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import java.util.Locale;
/**
* This class is responsible for setting and getting the preferred locale and manage any related
* actions. I think that there's no need for logging here because the utils class already handles
* logs for these actions based on their returned results.
*
* Created by ahmedjazzar on 1/22/16.
*/
class LocalesPreferenceManager {
private SharedPreferences mSharedPreferences;
private SharedPreferences.Editor mEditor;
static final int BASE_LOCALE = 1;
private final String BASE_LANGUAGE_KEY = "base_language";
private final String BASE_COUNTRY_KEY = "base_country";
static final int LAUNCH_LOCALE = 2;
private final String LAUNCH_LANGUAGE_KEY = "launch_language";
private final String LAUNCH_COUNTRY_KEY = "launch_country";
static final int USER_PREFERRED_LOCALE = 3;
private final String USER_PREFERRED_LANGUAGE_KEY = "user_preferred_language";
private final String USER_PREFERRED_COUNTRY_KEY = "user_preferred_country";
LocalesPreferenceManager(Context context, Locale firstLaunchLocale, Locale baseLocale) {
this.mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
this.mEditor = this.mSharedPreferences.edit();
if (!isLocaleExists(BASE_LOCALE)) {
this.setPreferredLocale(BASE_LOCALE, baseLocale);
}
if (!isLocaleExists(LAUNCH_LOCALE)) {
this.setPreferredLocale(LAUNCH_LOCALE, firstLaunchLocale);
}
if (!isLocaleExists(USER_PREFERRED_LOCALE)) {
this.setPreferredLocale(USER_PREFERRED_LOCALE, firstLaunchLocale);
}
}
boolean isLocaleExists(int key) {
switch (key) {
case BASE_LOCALE:
return mSharedPreferences.contains(this.BASE_LANGUAGE_KEY);
case LAUNCH_LOCALE:
return mSharedPreferences.contains(this.LAUNCH_LANGUAGE_KEY);
case USER_PREFERRED_LOCALE:
return mSharedPreferences.contains(this.USER_PREFERRED_LANGUAGE_KEY);
default:
return false;
}
}
/**
* Sets user preferred locale
*
* @param locale user desired locale
* @return true if the preference updated
*/
boolean setPreferredLocale(int key, Locale locale) {
return this.setPreferredLocale(key, locale.getLanguage(), locale.getCountry());
}
/**
*
* @return preferred locale after concatenating language and country
*/
Locale getPreferredLocale(int key) {
String languageKey;
String countryKey;
switch (key) {
case BASE_LOCALE:
languageKey = this.BASE_LANGUAGE_KEY;
countryKey = this.BASE_COUNTRY_KEY;
break;
case LAUNCH_LOCALE:
languageKey = this.LAUNCH_LANGUAGE_KEY;
countryKey = this.LAUNCH_COUNTRY_KEY;
break;
case USER_PREFERRED_LOCALE:
languageKey = this.USER_PREFERRED_LANGUAGE_KEY;
countryKey = this.USER_PREFERRED_COUNTRY_KEY;
break;
default:
return null;
}
String language = getPreferredLanguage(languageKey);
String country = getPreferredCountry(countryKey);
if (language == null) {
return null;
}
return new Locale(language, country);
}
/**
* Sets user preferred locale by setting a language preference and a country preference since
* there's no supported preferences for locales
* @param language of the locale; ex. en
* @param country of the locale; ex. US
* @return true if the preferences updated
*/
private boolean setPreferredLocale(int key, String language, String country) {
String languageKey;
String countryKey;
switch (key) {
case BASE_LOCALE:
languageKey = this.BASE_LANGUAGE_KEY;
countryKey = this.BASE_COUNTRY_KEY;
break;
case LAUNCH_LOCALE:
languageKey = this.LAUNCH_LANGUAGE_KEY;
countryKey = this.LAUNCH_COUNTRY_KEY;
break;
case USER_PREFERRED_LOCALE:
languageKey = this.USER_PREFERRED_LANGUAGE_KEY;
countryKey = this.USER_PREFERRED_COUNTRY_KEY;
break;
default:
return false;
}
mEditor.putString(languageKey, language);
mEditor.putString(countryKey, country);
return mEditor.commit();
}
/**
*
* @return preferred language
*/
private String getPreferredLanguage(String key) {
return mSharedPreferences.getString(key, null);
}
/**
*
* @return preferred country
*/
private String getPreferredCountry(String key) {
return mSharedPreferences.getString(key, null);
}
}

@ -1,302 +0,0 @@
package com.ahmedjazzar.rosetta;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.util.DisplayMetrics;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
/**
* This class is a helper class that connects all library classes activities together and make it
* easier for every class in the library to use and look at the shared info without a need to
* initialize a new object from the desired class
*
* Created by ahmedjazzar on 1/19/16.
*/
final class LocalesUtils {
@SuppressLint("StaticFieldLeak")
private static LocalesDetector sDetector;
private static LocalesPreferenceManager sLocalesPreferenceManager;
private static HashSet<Locale> sLocales;
private static final Locale[] PSEUDO_LOCALES = {
new Locale("en", "XA"),
new Locale("ar", "XB")
};
private static final String TAG = LocalesDetector.class.getName();
private static Logger sLogger = new Logger(TAG);
/**
*
* @param detector just a setter because I don't want to declare any constructors in this class
*/
static void setDetector(@NonNull LocalesDetector detector) {
LocalesUtils.sDetector = detector;
}
/**
*
* @param localesPreferenceManager just a setter because I don't want to declare any
* constructors in this class
*/
static void setLocalesPreferenceManager(
@NonNull LocalesPreferenceManager localesPreferenceManager) {
LocalesUtils.sLocalesPreferenceManager = localesPreferenceManager;
}
/**
*
* @param stringId a string to start discovering sLocales in
* @return a HashSet of discovered sLocales
*/
static HashSet<Locale> fetchAvailableLocales(int stringId) {
return sDetector.fetchAvailableLocales(stringId);
}
/**
*
* @param localesSet sLocales user wanna use
*/
static void setSupportedLocales(HashSet<Locale> localesSet) {
LocalesUtils.sLocales = sDetector.validateLocales(localesSet);
sLogger.debug("Locales have been changed");
}
/**
*
* @return a HashSet of the available sLocales discovered in the application
*/
static HashSet<Locale> getLocales() {
return LocalesUtils.sLocales;
}
/**
*
* @return a list of locales for displaying on the layout purposes
*/
static ArrayList<String> getLocalesWithDisplayName() {
ArrayList<String> stringLocales = new ArrayList<>();
for (Locale loc: LocalesUtils.getLocales()) {
String langDisplay = loc.getDisplayName(loc);
stringLocales.add(langDisplay.substring(0, 1).toUpperCase() + langDisplay.substring(1).toLowerCase());
}
return stringLocales;
}
/**
*
* @return the index of the current app locale
*/
static int getCurrentLocaleIndex() {
Locale locale = LocalesUtils.getCurrentLocale();
int index = -1;
int itr = 0;
for (Locale l : sLocales) {
if(locale.equals(l)) {
index = itr;
break;
}
itr++;
}
if (index == -1) {
//TODO: change the index to the most closer available locale
sLogger.warn("Current device locale '" + locale.toString() +
"' does not appear in your given supported locales");
index = sDetector.detectMostClosestLocale(locale);
if(index == -1) {
index = 0;
sLogger.warn("Current locale index changed to 0 as the current locale '" +
locale +
"' not supported."
);
}
}
return index;
}
/**
*
* @see <a href="http://en.wikipedia.org/wiki/Pseudolocalization">Pseudolocalization</a> for
* more information about pseudo localization
* @return pseudo locales list
*/
static List<Locale> getPseudoLocales() {
return Arrays.asList(LocalesUtils.PSEUDO_LOCALES);
}
/**
*
* @return the locale at the given index
*/
static Locale getLocaleFromIndex(int index) {
return LocalesUtils.sLocales.toArray(new Locale[LocalesUtils.sLocales.size()])[index];
}
/**
*
* @param context
* @param index the selected locale position
* @return true if the application locale changed
*/
static boolean setAppLocale(Context context, int index) {
return setAppLocale(context, getLocaleFromIndex(index));
}
/**
*
* @return true if the application locale changed
*/
static boolean setAppLocale(Context context, Locale newLocale) {
Resources resources = context.getResources();
DisplayMetrics displayMetrics = resources.getDisplayMetrics();
Configuration configuration = resources.getConfiguration();
Locale oldLocale = new Locale(configuration.locale.getLanguage(), configuration.locale.getCountry());
configuration.locale = newLocale;
// Sets the layout direction from the Locale
sLogger.debug("Setting the layout direction");
configuration.setLayoutDirection(newLocale);
resources.updateConfiguration(configuration, displayMetrics);
if(oldLocale.equals(newLocale)) {
return false;
}
if (LocalesUtils.updatePreferredLocale(newLocale)) {
sLogger.info("Locale preferences updated to: " + newLocale);
Locale.setDefault(newLocale);
} else {
sLogger.error("Failed to update locale preferences.");
}
return true;
}
/**
*
* @return application's base locale
*/
static Locale getBaseLocale() {
return LocalesUtils.sLocalesPreferenceManager.getPreferredLocale(LocalesPreferenceManager.BASE_LOCALE);
}
/**
*
* @param stringId the target string
* @return a localized string
*/
static String getInSpecificLocale(FragmentActivity activity, Locale locale, int stringId) {
Configuration conf = activity.getResources().getConfiguration();
Locale old = conf.locale;
conf.locale = locale;
DisplayMetrics metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
Resources resources = new Resources(activity.getAssets(), metrics, conf);
conf.locale = old;
return resources.getString(stringId);
}
/**
* Refreshing the application so no weired results occurred after changing the locale.
*/
static void refreshApplication(Activity activity) {
Intent app = activity.getBaseContext().getPackageManager()
.getLaunchIntentForPackage(activity.getBaseContext().getPackageName());
app.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK);
Intent current = new Intent(activity, activity.getClass());
sLogger.debug("Refreshing the application: " +
activity.getBaseContext().getPackageName());
sLogger.debug("Finishing current activity.");
activity.finish();
sLogger.debug("Start the application");
activity.startActivity(app);
activity.startActivity(current);
sLogger.debug("Application refreshed");
}
/**
*
* @return the first launch locale
*/
static Locale getLaunchLocale() {
return sLocalesPreferenceManager.getPreferredLocale(LocalesPreferenceManager.LAUNCH_LOCALE);
}
/**
* Setting the application locale manually
* @param newLocale the desired locale
* @param activity the current activity in order to refresh the app
*
* @return true if the operation succeed, false otherwise
*/
static boolean setLocale(Locale newLocale, Activity activity) {
if (newLocale == null || !getLocales().contains(newLocale)) {
return false;
}
if (LocalesUtils.setAppLocale(activity.getApplicationContext(), newLocale)) {
LocalesUtils.refreshApplication(activity);
return true;
}
return false;
}
/**
* @param context application base context
* @return the current locale
*/
public static Locale getCurrentLocale(Context context) {
Resources resources = context.getResources();
Configuration configuration = resources.getConfiguration();
return new Locale(configuration.locale.getLanguage(), configuration.locale.getCountry());
}
/**
*
* @param locale the new preferred locale
* @return true if the preferred locale updated
*/
private static boolean updatePreferredLocale(Locale locale) {
return LocalesUtils.sLocalesPreferenceManager
.setPreferredLocale(LocalesPreferenceManager.USER_PREFERRED_LOCALE, locale);
}
/**
*
* @return current application locale
*/
private static Locale getCurrentLocale() {
return sDetector.getCurrentLocale();
}
}

@ -1,38 +0,0 @@
package com.ahmedjazzar.rosetta;
import android.util.Log;
/**
* This class helps logging app events without a need to rewrite the tag name in every time
* Created by ahmedjazzar on 1/16/16.
*/
class Logger {
private final String mTag;
Logger(String tag) {
this.mTag = tag;
this.verbose("Object from " + this.mTag + " has been created.");
}
void error(String log) {
Log.e(this.mTag, log);
}
void warn(String log) {
Log.w(this.mTag, log);
}
void debug(String log) {
Log.d(this.mTag, log);
}
void info(String log) {
Log.i(this.mTag, log);
}
void verbose(String log) {
Log.v(this.mTag, log);
}
}

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="language">Language</string>
<string name="ok">Ok</string>
<string name="cancel">Cancel</string>
</resources>

@ -10,4 +10,3 @@ dependencyResolutionManagement {
}
rootProject.name = "MagiskModuleManager"
include ':app'
include ':rosetta'

Loading…
Cancel
Save