New Language Feature: Support for unsuported languages.

This commit is contained in:
Rashiq Ahmad 2013-12-16 03:00:46 +01:00
parent 1b84f3c5c5
commit 870d51b9b1
13 changed files with 497 additions and 45 deletions

View File

@ -1,23 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="10"
android:versionName="1.7" package="org.kiwix.kiwixmobile">
android:versionName="1.7"
package="org.kiwix.kiwixmobile">
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:resizeable="true"
android:anyDensity="true"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-sdk android:minSdkVersion="11" android:targetSdkVersion="17"/>
<application android:icon="@drawable/kiwix_icon" android:label="@string/app_name"
android:allowBackup="true"
android:debuggable="true"
>
<activity android:name=".KiwixMobileActivity"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden|screenSize">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="17"/>
<application
android:icon="@drawable/kiwix_icon"
android:label="@string/app_name"
android:allowBackup="true">
<activity
android:name=".KiwixMobileActivity"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden|screenSize|locale">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@ -25,6 +32,7 @@
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.zim.*"/>
@ -36,6 +44,7 @@
</activity>
<activity android:name=".KiwixSettings">
</activity>
<provider
android:name=".ZimContentProvider"
android:authorities="org.kiwix.zim"

Binary file not shown.

85
assets/locales.txt Normal file
View File

@ -0,0 +1,85 @@
af,
ar,
as,
ba,
be,
bg,
bm,
bn,
br,
ca,
cs,
cv,
cy,
da,
de,
el,
eo,
es,
et,
fa,
fi,
fo,
fr,
gl,
gu,
he,
hi,
hr,
hu,
ia,
id,
ie,
it,
ja,
jv,
ka,
km,
kn,
ko,
ky,
lb,
li,
lt,
lv,
mk,
ml,
mn,
ms,
mt,
my,
nb,
ne,
nl,
nn,
oc,
or,
pl,
ps,
pt,
qu,
rm,
ro,
ru,
sa,
sh,
si,
sk,
sl,
sq,
su,
sv,
sw,
ta,
te,
th,
tl,
tr,
uk,
ur,
uz,
vi,
yi,
yo,
zh,
en

View File

@ -10,6 +10,7 @@ import os
import sys
import copy
import shutil
import create_locales
from subprocess import call, check_output
# target platform to compile for
@ -515,6 +516,8 @@ for arch in ARCHS:
change_env(ORIGINAL_ENVIRON)
if COMPILE_APK:
#create locales.txt
create_locales.write_locales(create_locales.get_all_language_codes())
syscall('rm -f bin/*.apk', shell=True)
syscall('ant debug')
syscall('ls -lh bin/*.apk', shell=True)

26
create-locales.py Normal file
View File

@ -0,0 +1,26 @@
import os
def get_all_language_codes():
# Get the path of the res folder
res_path = os.path.join(os.getcwd(),'res')
# Get all the ISO 639-1 language codes from the suffix of the value folders
files = [f.split('values-')[1] for f in os.listdir(res_path) if f.startswith('values-')]
# Append the English Locale to the list, since the default values folder, (the english) values folder
# does not have a suffix and gets ignored when creating the above list
files.append('en')
return files
def write_locales(locales):
# Create a CSV file with all the langauge codes in the assets folder
with open(os.path.join(os.getcwd(), 'assets', 'locales.txt'), 'w') as f:
f.write(',\n'.join(locales))
if __name__ == '__main__':
write_locales(get_all_language_codes())

View File

@ -52,4 +52,6 @@
<string name="remove">Remove</string>
<string name="open_in_new_tab">Open link in new Tab?</string>
<string name="share_via">Share via</string>
<string name="pref_language_title">Language</string>
<string name="pref_language_chooser">Choose a language</string>
</resources>

View File

@ -29,8 +29,20 @@
</PreferenceCategory>
<PreferenceCategory
android:title="@string/pref_info_title"
android:key="pref_info">
android:key="pref_language"
android:title="@string/pref_language_title">
<ListPreference
android:key="pref_language_chooser"
android:title="@string/pref_language_title"
android:summary="@string/pref_language_chooser"
android:dialogTitle="@string/pref_language_chooser"/>
</PreferenceCategory>
<PreferenceCategory
android:key="pref_info"
android:title="@string/pref_info_title">
<EditTextPreference
android:key="pref_version"

View File

@ -5,6 +5,8 @@ import android.content.SharedPreferences;
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
@ -45,6 +47,28 @@ public class FileWriter {
saveCsvToPrefrences(sb.toString());
}
// Read the locales.txt file in the assets folder, that has been created at compile time by the
// build script
public ArrayList<String> readFileFromAssets() {
String content = "";
try {
InputStream stream = mContext.getAssets().open("locales.txt");
int size = stream.available();
byte[] buffer = new byte[size];
stream.read(buffer);
stream.close();
content = new String(buffer);
} catch (IOException e) {
}
return readCsv(content);
}
// Add items to the MediaStore list, that are not in the MediaStore database.
// These will be loaded from a previously saved CSV file.
// We are checking, if these file still exist as well.
@ -65,6 +89,11 @@ public class FileWriter {
String csv = getCsvFromPrefrences();
return readCsv(csv);
}
private ArrayList<String> readCsv(String csv) {
String[] csvArray = csv.split(",");
return new ArrayList<String>(Arrays.asList(csvArray));

View File

@ -5,10 +5,13 @@ import android.app.FragmentTransaction;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
@ -31,14 +34,13 @@ import android.widget.LinearLayout;
import android.widget.Spinner;
import java.util.ArrayList;
import java.util.Locale;
public class KiwixMobileActivity extends FragmentActivity implements ActionBar.TabListener,
View.OnLongClickListener, View.OnDragListener, KiwixMobileFragment.FragmentCommunicator {
public static ArrayList<State> mPrefState;
private int NUM_ITEMS = 0;
private ViewPagerAdapter mViewPagerAdapter;
private ViewPager mViewPager;
@ -47,6 +49,8 @@ public class KiwixMobileActivity extends FragmentActivity implements ActionBar.T
private KiwixMobileFragment mCurrentFragment;
private int mNumberOfTabs = 0;
private int mCurrentDraggedTab;
private int mTabsWidth;
@ -60,6 +64,8 @@ public class KiwixMobileActivity extends FragmentActivity implements ActionBar.T
requestWindowFeature(Window.FEATURE_PROGRESS);
setProgressBarVisibility(true);
handleLocaleCheck();
setContentView(R.layout.viewpager);
// Set an OnDragListener on the entire View. Now we can track, if the user drags the
@ -68,15 +74,24 @@ public class KiwixMobileActivity extends FragmentActivity implements ActionBar.T
mViewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
mPrefState = new ArrayList<State>();
mViewPager = (ViewPager) findViewById(R.id.viewPager);
mActionBar = getActionBar();
mPrefState = new ArrayList<State>();
setUpViewPagerAndActionBar();
// Set the initial tab. It's hidden.
addNewTab();
}
private void setUpViewPagerAndActionBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mActionBar.setHomeButtonEnabled(false);
}
mViewPager = (ViewPager) findViewById(R.id.viewPager);
mViewPager.setAdapter(mViewPagerAdapter);
// set the amount of pages, that the ViewPager adapter should keep in cache
mViewPager.setOffscreenPageLimit(3);
@ -114,9 +129,24 @@ public class KiwixMobileActivity extends FragmentActivity implements ActionBar.T
setTabsOnLongClickListener(root);
}
});
}
// Set the initial tab. It's hidden.
addNewTab();
// Reset the Locale and change the font of all TextViews and its subclasses, if necessary
private void handleLocaleCheck() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String language = prefs.getString("pref_language_chooser", "");
if (language.isEmpty()) {
return;
}
Locale locale = new Locale(language);
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
getResources().updateConfiguration(config, getResources().getDisplayMetrics());
new LanguageUtils(this).changeFont(getLayoutInflater());
}
@Override
@ -436,7 +466,7 @@ public class KiwixMobileActivity extends FragmentActivity implements ActionBar.T
// If it's the first (visible) tab, then switch the navigation mode from NAVIGATION_MODE_NORMAL to
// NAVIGATION_MODE_TABS and show tabs
if (NUM_ITEMS == 1) {
if (mNumberOfTabs == 1) {
mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
mCurrentFragment = getCurrentVisibleFragment();
@ -454,10 +484,10 @@ public class KiwixMobileActivity extends FragmentActivity implements ActionBar.T
mActionBar.addTab(mActionBar.newTab().setTabListener(this));
NUM_ITEMS = NUM_ITEMS + 1;
mNumberOfTabs = mNumberOfTabs + 1;
mViewPagerAdapter.notifyDataSetChanged();
mPrefState.add(NUM_ITEMS - 1, new State(false));
mPrefState.add(mNumberOfTabs - 1, new State(false));
if (mActionBar.getTabCount() > 1) {
mActionBar.setTitle(ZimContentProvider.getZimFileTitle());
@ -601,12 +631,12 @@ public class KiwixMobileActivity extends FragmentActivity implements ActionBar.T
@Override
public int getCount() {
return NUM_ITEMS;
return mNumberOfTabs;
}
public void removeFragment(int position) {
tabs.remove(position);
NUM_ITEMS = NUM_ITEMS - 1;
mNumberOfTabs = mNumberOfTabs - 1;
mViewPagerAdapter.notifyDataSetChanged();
}

View File

@ -375,7 +375,6 @@ public class KiwixMobileFragment extends Fragment {
mBackToTopButton.startAnimation(
AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in));
}
}
} else {
if (mBackToTopButton.getVisibility() == View.VISIBLE) {
@ -600,6 +599,11 @@ public class KiwixMobileFragment extends Fragment {
}
break;
case PREFERENCES_REQUEST_CODE:
if (resultCode == KiwixSettings.RESULT_RESTART) {
getActivity().finish();
startActivity(new Intent(getActivity(), KiwixMobileActivity.class));
}
loadPrefs();
for (KiwixMobileActivity.State state : KiwixMobileActivity.mPrefState) {
state.setHasToBeRefreshed(true);
@ -615,9 +619,11 @@ public class KiwixMobileFragment extends Fragment {
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.main, menu);
this.menu = menu;
if (requestInitAllMenuItems) {
initAllMenuItems();
}
super.onCreateOptionsMenu(menu, inflater);
}
@ -646,7 +652,7 @@ public class KiwixMobileFragment extends Fragment {
// Pinch to zoom
// This seems to suffer from a bug in Android. If you set to "false" this only apply after a restart of the app.
Log.d(TAG_KIWIX, "pref_zoom_enabled value (" + pref_zoom_enabled + ")");
webView.getSettings().setBuiltInZoomControls(pref_zoom_enabled);
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setDisplayZoomControls(pref_zoom_enabled);
if (!isButtonEnabled) {
@ -937,6 +943,7 @@ public class KiwixMobileFragment extends Fragment {
data.add(suggestion);
}
} catch (Exception e) {
}
// Now assign the values and count to the FilterResults object
filterResults.values = data;
@ -1053,7 +1060,7 @@ public class KiwixMobileFragment extends Fragment {
getActivity().getActionBar().getSelectedTab().setText(title);
}
//Workaround for #643
// Workaround for #643
if (requestWebReloadOnFinished > 0) {
requestWebReloadOnFinished = requestWebReloadOnFinished - 1;
Log.d(TAG_KIWIX, "Workaround for #643: onPageFinished. Trigger reloading. ("

View File

@ -9,49 +9,50 @@ import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceFragment;
import java.util.Locale;
public class KiwixSettings extends Activity {
public static final int RESULT_RESTART = 1236;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, new PrefsFragment()).commit();
new LanguageUtils(this).changeFont(getLayoutInflater());
}
public class PrefsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences);
prepareListPreferenceForAutoSummary("pref_zoom");
// Set version
String version;
try {
version = getPackageManager().getPackageInfo("org.kiwix.kiwixmobile", 0).versionName;
} catch (NameNotFoundException e) {
return;
}
EditTextPreference versionPref = (EditTextPreference) findPreference("pref_version");
versionPref.setSummary(version);
prepareListPreferenceForAutoSummary("pref_zoom");
setUpLanguageChooser("pref_language_chooser");
setAppVersionNumber();
}
private void prepareListPreferenceForAutoSummary(String preferenceID) {
ListPreference prefList = (ListPreference) findPreference(preferenceID);
private void prepareListPreferenceForAutoSummary(String preferenceId) {
ListPreference prefList = (ListPreference) findPreference(preferenceId);
prefList.setDefaultValue(prefList.getEntryValues()[0]);
String ss = prefList.getValue();
if (ss == null) {
String summary = prefList.getValue();
if (summary == null) {
prefList.setValue((String) prefList.getEntryValues()[0]);
ss = prefList.getValue();
summary = prefList.getValue();
}
prefList.setSummary(prefList.getEntries()[prefList.findIndexOfValue(ss)]);
prefList.setSummary(prefList.getEntries()[prefList.findIndexOfValue(summary)]);
prefList.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference,
Object newValue) {
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference instanceof ListPreference) {
preference.setSummary(((ListPreference) preference)
.getEntries()[((ListPreference) preference)
@ -61,5 +62,42 @@ public class KiwixSettings extends Activity {
}
});
}
private void setUpLanguageChooser(String preferenceId) {
ListPreference languageList = (ListPreference) findPreference(preferenceId);
LanguageUtils languageUtils = new LanguageUtils(getActivity());
languageList.setEntries(languageUtils.getValues().toArray(new String[0]));
languageList.setEntryValues(languageUtils.getKeys().toArray(new String[0]));
languageList.setDefaultValue(Locale.getDefault().toString());
languageList.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (!newValue.equals(Locale.getDefault().toString())) {
// Request a restart when the user returns to the Activity, that called this Activity
setResult(RESULT_RESTART);
}
return true;
}
});
}
private void setAppVersionNumber() {
String version;
try {
version = getPackageManager().getPackageInfo("org.kiwix.kiwixmobile", 0).versionName;
} catch (NameNotFoundException e) {
return;
}
EditTextPreference versionPref = (EditTextPreference) findPreference("pref_version");
versionPref.setSummary(version);
}
}
}

View File

@ -0,0 +1,208 @@
package org.kiwix.kiwixmobile;
import android.content.Context;
import android.graphics.Typeface;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
public class LanguageUtils {
private List<LanguageContainer> mLanguageList;
private List<String> mLocaleLanguageCodes;
private Context mContext;
public LanguageUtils(Context context) {
mContext = context;
mLanguageList = new ArrayList<LanguageContainer>();
mLocaleLanguageCodes = new ArrayList<String>();
getLanguageCodesFromAssets();
setupLanguageList();
sortLanguageList();
}
// Read the language codes, that are supported in this app from the locales.txt file
private void getLanguageCodesFromAssets() {
List<String> locales = new ArrayList<String>(new FileWriter(mContext).readFileFromAssets());
for (String locale : locales) {
if (!locale.isEmpty()) {
mLocaleLanguageCodes.add(locale.trim());
}
}
}
// Create a list containing the language code and the corresponding (english) langauge name
private void setupLanguageList() {
for (String languageCode : mLocaleLanguageCodes) {
mLanguageList.add(new LanguageContainer(languageCode));
}
}
// Sort the language list by the language name
private void sortLanguageList() {
Collections.sort(mLanguageList, new Comparator<LanguageContainer>() {
@Override
public int compare(LanguageContainer a, LanguageContainer b) {
return a.getLanguageName().compareToIgnoreCase(b.getLanguageName());
}
});
}
// Check, if the selected Locale is supported and weather we actually need to change our font.
// We do this by checking, if our Locale is available in the List, that Locale.getAvailableLocales() returns.
private boolean haveToChangeFont() {
for (Locale s : Locale.getAvailableLocales()) {
if (s.getLanguage().equals(Locale.getDefault().toString())) {
return false;
}
}
return true;
}
// Change the font of all the TextViews and its subclasses in our whole app by attaching a custom
// Factory to the LayoutInflater of the Activity.
// The Factory is called on each element name as the xml is parsed and we can therefore modify the parsed
// Elements while the content is being parsed.
// A Factory can only be set once on a LayoutInflator. And since we are using the support Library,
// which also sets a Factory on the LayoutInflator, we have to access the private field of the
// LayoutInflater, that handles this restriction via the Java's reflection API
// and make it accessible set it to false again.
public void changeFont(LayoutInflater layoutInflater) {
if (!haveToChangeFont()) {
return;
}
try {
Field field = LayoutInflater.class.getDeclaredField("mFactorySet");
field.setAccessible(true);
field.setBoolean(layoutInflater, false);
layoutInflater.setFactory(new LayoutInflaterFactory(mContext, layoutInflater));
} catch (NoSuchFieldException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
}
// Get a list of all the language names
public List<String> getValues() {
List<String> values = new ArrayList<String>();
for (LanguageContainer value : mLanguageList) {
values.add(value.getLanguageName());
}
return values;
}
// Get a list of all the language codes
public List<String> getKeys() {
List<String> keys = new ArrayList<String>();
for (LanguageContainer key : mLanguageList) {
keys.add(key.getLanguageCode());
}
return keys;
}
// That's the Factory, that will handle the manipulation of all our TextView's and its subcalsses
// while the content is being parsed
public static class LayoutInflaterFactory implements LayoutInflater.Factory {
private Context mContext;
private LayoutInflater mLayoutInflater;
public LayoutInflaterFactory(Context context, LayoutInflater layoutInflater) {
mContext = context;
mLayoutInflater = layoutInflater;
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
Log.e("kiwix", name);
// Apply the custom font, if the xml tag equals "TextView", "EditText" or "AutoCompleteTextView"
if (name.equalsIgnoreCase("TextView")
|| name.equalsIgnoreCase("EditText")
|| name.equalsIgnoreCase("AutoCompleteTextView")) {
try {
LayoutInflater inflater = mLayoutInflater;
final View view = inflater.createView(name, null, attrs);
new Handler().post(new Runnable() {
public void run() {
((TextView) view).setTypeface(Typeface.createFromAsset(
mContext.getAssets(), "DejaVuSansCondensed.ttf"));
}
});
return view;
} catch (InflateException e) {
} catch (ClassNotFoundException e) {
}
}
return null;
}
}
private class LanguageContainer {
private String mLanguageCode;
private String mLanguageName;
// This constructor will take care of creating a language name for the given ISO 639-1 language code.
// The language name will always be in english to ensure user friendliness and to prevent
// possible incompatibilities, since not all language names are available in all languages.
private LanguageContainer(String languageCode) {
mLanguageCode = languageCode;
mLanguageName = new Locale(languageCode).getDisplayLanguage(new Locale("en"));
}
public String getLanguageCode() {
return mLanguageCode;
}
public String getLanguageName() {
return mLanguageName;
}
}
}

View File

@ -52,6 +52,9 @@ public class ZimFileSelectActivity extends FragmentActivity
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new LanguageUtils(this).changeFont(getLayoutInflater());
setContentView(R.layout.zimfilelist);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);