commit
1ee0aa31ab
@ -0,0 +1,15 @@
|
|||||||
|
# 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)
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.fox2code.mmm.compat;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.ContextWrapper;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.LocaleList;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class CompatWrapper extends android.content.ContextWrapper {
|
||||||
|
|
||||||
|
public CompatWrapper(Context base) {
|
||||||
|
super(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ContextWrapper setLocale(Context context, Locale newLocale) {
|
||||||
|
|
||||||
|
Resources res = context.getResources();
|
||||||
|
Configuration configuration = res.getConfiguration();
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
configuration.setLocale(newLocale);
|
||||||
|
|
||||||
|
LocaleList localeList = new LocaleList(newLocale);
|
||||||
|
LocaleList.setDefault(localeList);
|
||||||
|
configuration.setLocales(localeList);
|
||||||
|
|
||||||
|
context = context.createConfigurationContext(configuration);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
configuration.setLocale(newLocale);
|
||||||
|
context = context.createConfigurationContext(configuration);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ContextWrapper(context);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M5,14.5h14v-6L5,8.5v6zM11,0.55L11,3.5h2L13,0.55h-2zM19.04,3.05l-1.79,1.79 1.41,1.41 1.8,-1.79 -1.42,-1.41zM13,22.45L13,19.5h-2v2.95h2zM20.45,18.54l-1.8,-1.79 -1.41,1.41 1.79,1.8 1.42,-1.42zM3.55,4.46l1.79,1.79 1.41,-1.41 -1.79,-1.79 -1.41,1.41zM4.96,19.95l1.79,-1.8 -1.41,-1.41 -1.79,1.79 1.41,1.42z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,6 @@
|
|||||||
|
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M16.24,11.51l1.57,-1.57l-3.75,-3.75l-1.57,1.57L8.35,3.63c-0.78,-0.78 -2.05,-0.78 -2.83,0l-1.9,1.9c-0.78,0.78 -0.78,2.05 0,2.83l4.13,4.13L3,17.25V21h3.75l4.76,-4.76l4.13,4.13c0.95,0.95 2.23,0.6 2.83,0l1.9,-1.9c0.78,-0.78 0.78,-2.05 0,-2.83L16.24,11.51zM9.18,11.07L5.04,6.94l1.89,-1.9c0,0 0,0 0,0l1.27,1.27L7.02,7.5l1.41,1.41l1.19,-1.19l1.45,1.45L9.18,11.07zM17.06,18.96l-4.13,-4.13l1.9,-1.9l1.45,1.45l-1.19,1.19l1.41,1.41l1.19,-1.19l1.27,1.27L17.06,18.96z"/>
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.47,-0.47 -1.12,-0.29 -1.41,0l-1.83,1.83l3.75,3.75L20.71,7.04z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,15 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<group android:scaleX="1.8531"
|
||||||
|
android:scaleY="1.8531"
|
||||||
|
android:translateX="31.7628"
|
||||||
|
android:translateY="31.7628">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/launcher_icon"
|
||||||
|
android:pathData="M20.5,11H19V7c0,-1.1 -0.9,-2 -2,-2h-4V3.5C13,2.12 11.88,1 10.5,1S8,2.12 8,3.5V5H4c-1.1,0 -1.99,0.9 -1.99,2v3.8H3.5c1.49,0 2.7,1.21 2.7,2.7s-1.21,2.7 -2.7,2.7H2V20c0,1.1 0.9,2 2,2h3.8v-1.5c0,-1.49 1.21,-2.7 2.7,-2.7 1.49,0 2.7,1.21 2.7,2.7V22H17c1.1,0 2,-0.9 2,-2v-4h1.5c1.38,0 2.5,-1.12 2.5,-2.5S21.88,11 20.5,11z"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
@ -1,9 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<!-- Thanks https://romannurik.github.io/AndroidAssetStudio/ for icons -->
|
<background android:drawable="@color/launcher_icon_background"/>
|
||||||
<!--
|
<foreground android:drawable="@drawable/ic_foreground"/>
|
||||||
https://romannurik.github.io/AndroidAssetStudio/icons-launcher.html#foreground.type=clipart&foreground.clipart=extension&foreground.space.trim=0&foreground.space.pad=0.25&foreColor=rgb(255%2C%20255%2C%20255)&backColor=rgb(255%2C%20152%2C%200)&crop=0&backgroundShape=circle&effects=elevate&name=ic_launcher
|
|
||||||
-->
|
|
||||||
<background android:drawable="@mipmap/ic_launcher_adaptive_back"/>
|
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_adaptive_fore"/>
|
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/launcher_icon_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_foreground"/>
|
||||||
|
</adaptive-icon>
|
@ -0,0 +1,78 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="purple_200">#FFBB86FC</color>
|
||||||
|
<color name="purple_500">#FF6200EE</color>
|
||||||
|
<color name="purple_700">#FF3700B3</color>
|
||||||
|
<color name="teal_200">#FF03DAC5</color>
|
||||||
|
<color name="teal_700">#FF018786</color>
|
||||||
|
<color name="black">#FF000000</color>
|
||||||
|
<color name="white">#FFFFFFFF</color>
|
||||||
|
<color name="system_neutral1_0">@android:color/system_neutral1_0</color>
|
||||||
|
<color name="system_neutral1_10">@android:color/system_neutral1_10</color>
|
||||||
|
<color name="system_neutral1_50">@android:color/system_neutral1_50</color>
|
||||||
|
<color name="system_neutral1_100">@android:color/system_neutral1_100</color>
|
||||||
|
<color name="system_neutral1_200">@android:color/system_neutral1_200</color>
|
||||||
|
<color name="system_neutral1_300">@android:color/system_neutral1_300</color>
|
||||||
|
<color name="system_neutral1_400">@android:color/system_neutral1_400</color>
|
||||||
|
<color name="system_neutral1_500">@android:color/system_neutral1_500</color>
|
||||||
|
<color name="system_neutral1_600">@android:color/system_neutral1_600</color>
|
||||||
|
<color name="system_neutral1_700">@android:color/system_neutral1_700</color>
|
||||||
|
<color name="system_neutral1_800">@android:color/system_neutral1_800</color>
|
||||||
|
<color name="system_neutral1_900">@android:color/system_neutral1_900</color>
|
||||||
|
<color name="system_neutral1_1000">@android:color/system_neutral1_1000</color>
|
||||||
|
<color name="system_neutral2_0">@android:color/system_neutral2_0</color>
|
||||||
|
<color name="system_neutral2_10">@android:color/system_neutral2_10</color>
|
||||||
|
<color name="system_neutral2_50">@android:color/system_neutral2_50</color>
|
||||||
|
<color name="system_neutral2_100">@android:color/system_neutral2_100</color>
|
||||||
|
<color name="system_neutral2_200">@android:color/system_neutral2_200</color>
|
||||||
|
<color name="system_neutral2_300">@android:color/system_neutral2_300</color>
|
||||||
|
<color name="system_neutral2_400">@android:color/system_neutral2_400</color>
|
||||||
|
<color name="system_neutral2_500">@android:color/system_neutral2_500</color>
|
||||||
|
<color name="system_neutral2_600">@android:color/system_neutral2_600</color>
|
||||||
|
<color name="system_neutral2_700">@android:color/system_neutral2_700</color>
|
||||||
|
<color name="system_neutral2_800">@android:color/system_neutral2_800</color>
|
||||||
|
<color name="system_neutral2_900">@android:color/system_neutral2_900</color>
|
||||||
|
<color name="system_neutral2_1000">@android:color/system_neutral2_1000</color>
|
||||||
|
<color name="system_accent1_0">@android:color/system_accent1_0</color>
|
||||||
|
<color name="system_accent1_10">@android:color/system_accent1_10</color>
|
||||||
|
<color name="system_accent1_50">@android:color/system_accent1_50</color>
|
||||||
|
<color name="system_accent1_100">@android:color/system_accent1_100</color>
|
||||||
|
<color name="system_accent1_200">@android:color/system_accent1_200</color>
|
||||||
|
<color name="system_accent1_300">@android:color/system_accent1_300</color>
|
||||||
|
<color name="system_accent1_400">@android:color/system_accent1_400</color>
|
||||||
|
<color name="system_accent1_500">@android:color/system_accent1_500</color>
|
||||||
|
<color name="system_accent1_600">@android:color/system_accent1_600</color>
|
||||||
|
<color name="system_accent1_700">@android:color/system_accent1_700</color>
|
||||||
|
<color name="system_accent1_800">@android:color/system_accent1_800</color>
|
||||||
|
<color name="system_accent1_900">@android:color/system_accent1_900</color>
|
||||||
|
<color name="system_accent1_1000">@android:color/system_accent1_1000</color>
|
||||||
|
<color name="system_accent2_0">@android:color/system_accent2_0</color>
|
||||||
|
<color name="system_accent2_10">@android:color/system_accent2_10</color>
|
||||||
|
<color name="system_accent2_50">@android:color/system_accent2_50</color>
|
||||||
|
<color name="system_accent2_100">@android:color/system_accent2_100</color>
|
||||||
|
<color name="system_accent2_200">@android:color/system_accent2_200</color>
|
||||||
|
<color name="system_accent2_300">@android:color/system_accent2_300</color>
|
||||||
|
<color name="system_accent2_400">@android:color/system_accent2_400</color>
|
||||||
|
<color name="system_accent2_500">@android:color/system_accent2_500</color>
|
||||||
|
<color name="system_accent2_600">@android:color/system_accent2_600</color>
|
||||||
|
<color name="system_accent2_700">@android:color/system_accent2_700</color>
|
||||||
|
<color name="system_accent2_800">@android:color/system_accent2_800</color>
|
||||||
|
<color name="system_accent2_900">@android:color/system_accent2_900</color>
|
||||||
|
<color name="system_accent2_1000">@android:color/system_accent2_1000</color>
|
||||||
|
<color name="system_accent3_0">@android:color/system_accent3_0</color>
|
||||||
|
<color name="system_accent3_10">@android:color/system_accent3_10</color>
|
||||||
|
<color name="system_accent3_50">@android:color/system_accent3_50</color>
|
||||||
|
<color name="system_accent3_100">@android:color/system_accent3_100</color>
|
||||||
|
<color name="system_accent3_200">@android:color/system_accent3_200</color>
|
||||||
|
<color name="system_accent3_300">@android:color/system_accent3_300</color>
|
||||||
|
<color name="system_accent3_400">@android:color/system_accent3_400</color>
|
||||||
|
<color name="system_accent3_500">@android:color/system_accent3_500</color>
|
||||||
|
<color name="system_accent3_600">@android:color/system_accent3_600</color>
|
||||||
|
<color name="system_accent3_700">@android:color/system_accent3_700</color>
|
||||||
|
<color name="system_accent3_800">@android:color/system_accent3_800</color>
|
||||||
|
<color name="system_accent3_900">@android:color/system_accent3_900</color>
|
||||||
|
<color name="system_accent3_1000">@android:color/system_accent3_1000</color>
|
||||||
|
<!-- Icon -->
|
||||||
|
<color name="launcher_icon_background">@android:color/system_accent1_100</color>
|
||||||
|
<color name="launcher_icon">@android:color/system_neutral2_700</color>
|
||||||
|
</resources>
|
@ -0,0 +1,37 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<style name="Theme.MagiskModuleManager.Light" parent="Theme.Material3.DynamicColors.Light">
|
||||||
|
<item name="android:statusBarColor">@color/status_bar_color</item>
|
||||||
|
<item name="colorBackgroundFloating">@color/system_accent2_200</item>
|
||||||
|
<item name="android:windowBackground">@color/system_accent2_100</item>
|
||||||
|
<item name="chipStyle">@style/Widget.Material3.Chip.Choice.Light</item>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="Widget.Material3.Chip.Choice.Light" parent="Widget.Material3.Chip.Assist">
|
||||||
|
<item name="chipBackgroundColor">@color/system_accent2_300</item>
|
||||||
|
<item name="chipStrokeWidth">0dp</item>
|
||||||
|
<item name="chipIconTint">?attr/colorControlNormal</item>
|
||||||
|
</style>
|
||||||
|
<style name="Theme.MagiskModuleManager.Transparent.Light" parent="Theme.MagiskModuleManager.Light" />
|
||||||
|
|
||||||
|
<style name="Theme.MagiskModuleManager.Dark" parent="Theme.Material3.DynamicColors.Dark">
|
||||||
|
<item name="android:statusBarColor">@color/status_bar_color</item>
|
||||||
|
<item name="colorBackgroundFloating">@color/system_accent2_800</item>
|
||||||
|
<item name="android:windowBackground">@color/system_accent2_900</item>
|
||||||
|
<item name="chipStyle">@style/Widget.Material3.Chip.Choice.Dark</item>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="Widget.Material3.Chip.Choice.Dark" parent="Widget.Material3.Chip.Assist">
|
||||||
|
<item name="chipBackgroundColor">@color/system_accent2_700</item>
|
||||||
|
<item name="chipStrokeWidth">0dp</item>
|
||||||
|
<item name="chipIconTint">?attr/colorControlNormal</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="Theme.MagiskModuleManager.Transparent.Dark" parent="Theme.MagiskModuleManager.Dark" />
|
||||||
|
|
||||||
|
<style name="Theme.MagiskModuleManager" parent="Theme.MagiskModuleManager.Light" />
|
||||||
|
</resources>
|
@ -1,6 +1,7 @@
|
|||||||
|
#Sun May 15 18:07:03 CEST 2022
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionSha256Sum=9afb3ca688fc12c761a0e9e4321e4d24e977a4a8916c8a768b1fe05ddb4d6b66
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
distributionSha256Sum=b586e04868a22fd817c8971330fec37e298f3242eb85c374181b12d637f80302
|
@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
# 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/
|
@ -0,0 +1,66 @@
|
|||||||
|
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'
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* 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";
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
# 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
|
@ -0,0 +1,17 @@
|
|||||||
|
# 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 *;
|
||||||
|
#}
|
@ -0,0 +1,5 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.ahmedjazzar.rosetta">
|
||||||
|
|
||||||
|
<application android:supportsRtl="true" />
|
||||||
|
</manifest>
|
@ -0,0 +1,179 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,180 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,153 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,161 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,302 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="language">Language</string>
|
||||||
|
<string name="ok">Ok</string>
|
||||||
|
<string name="cancel">Cancel</string>
|
||||||
|
</resources>
|
Loading…
Reference in New Issue