jackpal.androidterm.TermPreferences.java Source code

Java tutorial

Introduction

Here is the source code for jackpal.androidterm.TermPreferences.java

Source

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jackpal.androidterm;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import jackpal.androidterm.compat.AndroidCompat;
import jackpal.androidterm.util.TermSettings;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.preference.PreferenceManager;
import android.support.v4.content.ContextCompat;

import com.droidvim.XmlUtils;

import static jackpal.androidterm.Term.getPath;

public class TermPreferences extends PreferenceActivity {
    private static final String STATUSBAR_KEY = "statusbar";
    private static final String ACTIONBAR_KEY = "actionbar";
    private static final String CATEGORY_SCREEN_KEY = "screen";
    static final String FONTPATH = Environment.getExternalStorageDirectory().getPath() + "/fonts";
    private static final String CATEGORY_TEXT_KEY = "text";

    private final static boolean FLAVOR_VIM = TermVimInstaller.FLAVOR_VIM;
    private boolean mFirst = true;

    static final String EXTERNAL_APP_BUTTON = "external_app_button";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        final SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        final TermSettings settings = new TermSettings(getResources(), mPrefs);
        if (mFirst && settings.getColorTheme() == 0)
            setTheme(R.style.Theme_AppCompat);
        mFirst = false;
        super.onCreate(savedInstanceState);

        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preferences);
        if (FLAVOR_VIM) {
            addPreferencesFromResource(R.xml.preferences_apps);

            Resources res = getResources();
            String[] array = res.getStringArray(R.array.entries_app_filepicker_preference);

            OnPreferenceChangeListener listener = new OnPreferenceChangeListener() {
                @Override
                public boolean onPreferenceChange(Preference preference, Object newValue) {
                    Resources res = getResources();
                    String[] array = res.getStringArray(R.array.entries_app_filepicker_preference);
                    int value = Integer.valueOf((String) newValue);
                    String summary = array[value];
                    preference.setSummary(summary);
                    return true;
                }
            };

            String id = "cloud_dropbox_filepicker";
            Preference filePicker = findPreference(id);
            String summary = array[settings.getDropboxFilePicker()];
            filePicker.setSummary(summary);
            filePicker.setOnPreferenceChangeListener(listener);

            id = "cloud_googledrive_filepicker";
            filePicker = findPreference(id);
            summary = array[settings.getGoogleDriveFilePicker()];
            filePicker.setSummary(summary);
            filePicker.setOnPreferenceChangeListener(listener);

            id = "cloud_onedrive_filepicker";
            filePicker = findPreference(id);
            summary = array[settings.getOneDriveFilePicker()];
            filePicker.setSummary(summary);
            filePicker.setOnPreferenceChangeListener(listener);

            id = "external_app_button";
            filePicker = findPreference(id);
            final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
            OnPreferenceChangeListener AppButtonListener = new OnPreferenceChangeListener() {
                @Override
                public boolean onPreferenceChange(Preference preference, Object newValue) {
                    sp.edit().putBoolean(EXTERNAL_APP_BUTTON, (Boolean) newValue).apply();
                    return true;
                }
            };
            filePicker.setOnPreferenceChangeListener(AppButtonListener);

            id = "external_app_package_name";
            Preference pref = getPreferenceScreen().findPreference(id);
            String appId = mPrefs.getString(id, "");
            setAppPickerLabel(pref, appId);
            new Thread() {
                @Override
                public void run() {
                    setAppPickerList();
                }
            }.start();
            pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
                @Override
                public boolean onPreferenceChange(Preference preference, Object newValue) {
                    setAppPickerLabel(preference, (String) newValue);
                    return true;
                }
            });

        }

        if (AndroidCompat.SDK >= 19) {
            addPreferencesFromResource(R.xml.preferences_prefs);
            final String PREFS_KEY = "prefs_rw";
            Preference prefsPicker = getPreferenceScreen().findPreference(PREFS_KEY);
            prefsPicker.setOnPreferenceClickListener(new OnPreferenceClickListener() {
                @Override
                public boolean onPreferenceClick(Preference preference) {
                    prefsPicker();
                    return true;
                }
            });

            final String LICENSE_KEY = "license";
            Preference licensePrefs = getPreferenceScreen().findPreference(LICENSE_KEY);
            licensePrefs.setOnPreferenceClickListener(new OnPreferenceClickListener() {
                @Override
                public boolean onPreferenceClick(Preference preference) {
                    licensePrefs();
                    return true;
                }
            });
        }

        // Remove the action bar pref on older platforms without an action bar
        Preference actionBarPref = findPreference(ACTIONBAR_KEY);
        PreferenceCategory screenCategory = (PreferenceCategory) findPreference(CATEGORY_SCREEN_KEY);
        if ((actionBarPref != null) && (screenCategory != null)) {
            screenCategory.removePreference(actionBarPref);
        }

        Preference statusBarPref = findPreference(STATUSBAR_KEY);
        if ((statusBarPref != null) && (screenCategory != null)) {
            screenCategory.removePreference(statusBarPref);
        }

        PreferenceCategory keyboardCategory = (PreferenceCategory) findPreference("categoryKeyboard");
        Preference controlKeyPref = findPreference("controlkey");
        if ((controlKeyPref != null) && (keyboardCategory != null)) {
            keyboardCategory.removePreference(controlKeyPref);
        }
        Preference fnKeyPref = findPreference("fnkey");
        if ((fnKeyPref != null) && (keyboardCategory != null)) {
            keyboardCategory.removePreference(fnKeyPref);
        }

        if (FLAVOR_VIM) {
            findPreference("functionbar_vim_paste").setDefaultValue(true);
        }

        // FIXME:
        if (AndroidCompat.SDK < 19 && !new File(FONTPATH).exists())
            new File(FONTPATH).mkdirs();
        Preference fontPicker = getPreferenceScreen().findPreference("fontfile_picker");
        fontPicker.setOnPreferenceClickListener(new OnPreferenceClickListener() {
            @Override
            public boolean onPreferenceClick(Preference preference) {
                filePicker();
                return true;
            }
        });

        ListPreference fontFileList = (ListPreference) getPreferenceScreen().findPreference("fontfile");
        setFontList(fontFileList);

        Preference fontSelect = findPreference("fontfile");
        Resources res = getResources();
        fontSelect
                .setSummary(res.getString(R.string.summary_fontfile_preference) + String.format(" (%s)", FONTPATH));
        fontSelect.setOnPreferenceClickListener(new OnPreferenceClickListener() {
            @Override
            public boolean onPreferenceClick(Preference preference) {
                ListPreference fontFileList = (ListPreference) preference;
                setFontList(fontFileList);
                return true;
            }
        });

        fontSelect.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                ListPreference fontFileList = (ListPreference) preference;
                setFontList(fontFileList);
                fontFileList.setDefaultValue(newValue);
                return true;
            }
        });

        PreferenceCategory textCategory = (PreferenceCategory) findPreference(CATEGORY_TEXT_KEY);
        Preference fontPref;
        if (AndroidCompat.SDK >= 19) {
            fontPref = findPreference("fontfile");
        } else {
            fontPref = findPreference("fontfile_picker");
        }
        if ((fontPref != null) && (textCategory != null)) {
            textCategory.removePreference(fontPref);
        }

    }

    void licensePrefs() {
        AlertDialog.Builder bld = new AlertDialog.Builder(this);
        bld.setIcon(android.R.drawable.ic_dialog_info);
        bld.setMessage(this.getString(R.string.license_text));
        bld.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                dialog.dismiss();
            }
        });
        bld.setNeutralButton(this.getString(R.string.github), new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                dialog.dismiss();
                Intent openUrl = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.github_url)));
                startActivity(openUrl);
            }
        });
        bld.create().show();
    }

    public static final int REQUEST_FONT_PICKER = 16;
    public static final int REQUEST_PREFS_READ_PICKER = REQUEST_FONT_PICKER + 1;
    public static final int REQUEST_STORAGE_FONT_PICKER = REQUEST_FONT_PICKER + 2;
    public static final int REQUEST_STORAGE_PREFS_PICKER = REQUEST_FONT_PICKER + 3;

    @SuppressLint("NewApi")
    void prefsPicker() {
        if (AndroidCompat.SDK >= 23 && ContextCompat.checkSelfPermission(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
                    REQUEST_STORAGE_PREFS_PICKER);
        } else {
            doPrefsPicker();
        }
    }

    @SuppressLint("NewApi")
    private void doPrefsPicker() {
        AlertDialog.Builder bld = new AlertDialog.Builder(this);
        bld.setIcon(android.R.drawable.ic_dialog_info);
        bld.setMessage(this.getString(R.string.prefs_dialog_rw));
        bld.setNeutralButton(this.getString(R.string.prefs_write), new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                dialog.dismiss();
                confirmWritePrefs();
            }
        });
        bld.setPositiveButton(this.getString(R.string.prefs_read), new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                dialog.dismiss();
                Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                intent.setType("text/xml");
                startActivityForResult(intent, REQUEST_PREFS_READ_PICKER);
            }
        });
        bld.setNegativeButton(this.getString(android.R.string.no), null);
        bld.create().show();
    }

    private void confirmWritePrefs() {
        File pathExternalPublicDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
        String downloadDir = pathExternalPublicDir.getPath();
        final String filename = downloadDir + "/" + BuildConfig.APPLICATION_ID + ".xml";
        if (new File(filename).exists()) {
            AlertDialog.Builder bld = new AlertDialog.Builder(this);
            bld.setIcon(android.R.drawable.ic_dialog_info);
            bld.setMessage(this.getString(R.string.prefs_write_confirm));
            bld.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    dialog.dismiss();
                    writePrefs(filename);
                }
            });
            bld.setNegativeButton(this.getString(android.R.string.no), null);
            bld.create().show();
        } else {
            writePrefs(filename);
            return;
        }
    }

    private boolean writePrefs(String filename) {
        AlertDialog.Builder bld = new AlertDialog.Builder(this);
        bld.setIcon(android.R.drawable.ic_dialog_info);
        bld.setPositiveButton(this.getString(android.R.string.ok), null);
        FileOutputStream fos;
        try {
            fos = new FileOutputStream(filename);
            SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
            XmlUtils.writeMapXml(pref.getAll(), fos);
        } catch (Exception e) {
            bld.setMessage(this.getString(R.string.prefs_write_info_failure));
            bld.create().show();
            return false;
        }
        bld.setMessage(this.getString(R.string.prefs_write_info_success) + "\n\n" + filename);
        bld.create().show();
        return true;
    }

    private boolean readPrefs(Uri uri, boolean clearPrefs) {
        try {
            InputStream is = this.getApplicationContext().getContentResolver().openInputStream(uri);
            SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(this).edit();

            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
            Map<String, ?> entries = XmlUtils.readMapXml(is);

            int error = 0;
            for (Map.Entry<String, ?> entry : entries.entrySet()) {
                if (!prefs.contains(entry.getKey()))
                    error += 1;
                if (error > 3)
                    throw new Exception();
            }
            if (clearPrefs && error == 0)
                prefEdit.clear();

            for (Map.Entry<String, ?> entry : entries.entrySet()) {
                putObject(prefEdit, entry.getKey(), entry.getValue());
            }
            prefEdit.apply();
        } catch (Exception e) {
            AlertDialog.Builder bld = new AlertDialog.Builder(this);
            bld.setIcon(android.R.drawable.ic_dialog_alert);
            bld.setTitle(this.getString(R.string.prefs_read_error_title));
            bld.setMessage(this.getString(R.string.prefs_read_error));
            bld.setPositiveButton(this.getString(android.R.string.ok), null);
            bld.create().show();
            return false;
        }
        return true;
    }

    private SharedPreferences.Editor putObject(final SharedPreferences.Editor edit, final String key,
            final Object val) {
        if (val instanceof Boolean)
            return edit.putBoolean(key, (Boolean) val);
        else if (val instanceof Float)
            return edit.putFloat(key, (Float) val);
        else if (val instanceof Integer)
            return edit.putInt(key, (Integer) val);
        else if (val instanceof Long)
            return edit.putLong(key, (Long) val);
        else if (val instanceof String) {
            String loadVal = (String) val;
            if (key.equals("lib_sh_path")) {
                if (!new File(loadVal).canRead())
                    loadVal = new File(getApplicationContext().getApplicationInfo().nativeLibraryDir) + "/libsh.so";
                return edit.putString(key, loadVal);
            }
            if (key.equals("shell_path")) {
                loadVal = loadVal.replaceFirst(" .*$", "");
                if (!loadVal.equals("") && !new File(loadVal).canRead())
                    loadVal = AndroidCompat.SDK >= 24 ? "" : "/system/bin/sh -";
                return edit.putString(key, loadVal);
            }
            if (key.equals("home_path")) {
                if (!new File(loadVal).canWrite()) {
                    String defValue;
                    if (!BuildConfig.FLAVOR.equals("master")) {
                        defValue = TermService.getAPPFILES() + "/home";
                        File home = new File(defValue);
                        if (!home.exists())
                            home.mkdir();
                    } else {
                        defValue = getDir("HOME", MODE_PRIVATE).getAbsolutePath();
                    }
                    loadVal = defValue;
                }
                return edit.putString(key, loadVal);
            }
            return edit.putString(key, ((String) val));
        }
        return edit;
    }

    @SuppressLint("NewApi")
    void filePicker() {
        if (AndroidCompat.SDK >= 23 && ContextCompat.checkSelfPermission(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
                    REQUEST_STORAGE_FONT_PICKER);
        } else {
            doFilePicker();
        }
    }

    private void setAppPickerLabel(Preference pref, String appId) {
        try {
            PackageManager pm = getPackageManager();
            PackageInfo packageInfo = pm.getPackageInfo(appId, 0);
            String label = packageInfo.applicationInfo.loadLabel(pm).toString();
            pref.setSummary(label);
        } catch (Exception e) {
        }
    }

    private void setAppPickerList() {
        PackageManager pm = this.getApplicationContext().getPackageManager();
        final List<ApplicationInfo> installedAppList = pm.getInstalledApplications(0);
        final TreeMap<String, String> items = new TreeMap<>();
        for (ApplicationInfo app : installedAppList) {
            Intent intent = pm.getLaunchIntentForPackage(app.packageName);
            if (intent != null)
                items.put(app.loadLabel(pm).toString(), app.packageName);
        }
        List<String> list = new ArrayList<>(items.keySet());
        final String labels[] = list.toArray(new String[list.size()]);
        list = new ArrayList<>(items.values());
        final String packageNames[] = list.toArray(new String[list.size()]);

        String id = "external_app_package_name";
        ListPreference packageName = (ListPreference) getPreferenceScreen().findPreference(id);
        packageName.setEntries(labels);
        packageName.setEntryValues(packageNames);
    }

    @Override
    @SuppressLint("NewApi")
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
        case REQUEST_STORAGE_FONT_PICKER:
        case REQUEST_STORAGE_PREFS_PICKER:
            for (int i = 0; i < permissions.length; i++) {
                if (permissions[i].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                        switch (requestCode) {
                        case REQUEST_STORAGE_FONT_PICKER:
                            doFilePicker();
                            break;
                        case REQUEST_STORAGE_PREFS_PICKER:
                            doPrefsPicker();
                            break;
                        default:
                            break;
                        }
                    } else {
                        AlertDialog.Builder bld = new AlertDialog.Builder(this);
                        bld.setIcon(android.R.drawable.ic_dialog_alert);
                        bld.setMessage(this.getString(R.string.storage_permission_error));
                        bld.setPositiveButton(this.getString(android.R.string.ok), null);
                        bld.create().show();
                    }
                }
            }
            break;
        default:
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            break;
        }
    }

    static final String FONT_FILENAME = "font_filename";

    @Override
    protected void onActivityResult(int request, int result, Intent data) {
        super.onActivityResult(request, result, data);
        switch (request) {
        case REQUEST_PREFS_READ_PICKER:
            if (result == RESULT_OK && data != null) {
                Uri uri = data.getData();
                if (readPrefs(uri, false))
                    onCreate(null);
                break;
            }
            break;
        case REQUEST_FONT_PICKER:
            String path;
            if (result == RESULT_OK && data != null) {
                Uri uri = data.getData();
                path = getPath(this, uri);
                if (path != null && path.matches(".*\\.(?i)(ttf|ttc|otf)")) {
                    SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
                    sp.edit().putString(FONT_FILENAME, path).apply();
                } else {
                    AlertDialog.Builder bld = new AlertDialog.Builder(this);
                    bld.setIcon(android.R.drawable.ic_dialog_alert);
                    bld.setMessage(this.getString(R.string.font_file_error));
                    bld.setPositiveButton(this.getString(android.R.string.ok), null);
                    bld.create().show();
                    break;
                }
            }
            break;
        default:
            break;
        }
    }

    private void doFilePicker() {
        AlertDialog.Builder bld = new AlertDialog.Builder(this);
        bld.setIcon(android.R.drawable.ic_dialog_info);
        bld.setMessage(this.getString(R.string.font_file_error));
        bld.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                dialog.dismiss();
                Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                intent.setType("application/octet-stream");
                startActivityForResult(intent, REQUEST_FONT_PICKER);
            }
        });
        bld.setNegativeButton(this.getString(android.R.string.no), null);
        final Activity activity = this;
        bld.setNeutralButton(this.getString(R.string.entry_fontfile_default),
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.dismiss();
                        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(activity);
                        sp.edit().putString(FONT_FILENAME, activity.getString(R.string.entry_fontfile_default))
                                .apply();
                    }
                });
        bld.create().show();
    }

    private ListPreference setFontList(ListPreference fontFileList) {
        File files[] = new File(FONTPATH).listFiles();
        ArrayList<File> fonts = new ArrayList<File>();

        if (files != null) {
            for (File file : files) {
                if (file.isFile() == true && file.getName().matches(".*\\.(?i)(ttf|ttc|otf)")
                        && file.isHidden() == false) {
                    fonts.add(file);
                }
            }
        }
        Collections.sort(fonts);
        int i = fonts.size() + 1;
        CharSequence[] items = new CharSequence[i];
        CharSequence[] values = new CharSequence[i];

        i = 0;
        Resources res = getResources();
        String systemFontName = res.getString(R.string.entry_fontfile_default);
        items[i] = systemFontName;
        values[i] = systemFontName;
        i++;

        Iterator<File> itr = fonts.iterator();
        while (itr.hasNext()) {
            File file = itr.next();
            items[i] = file.getName();
            values[i] = file.getName();
            i++;
        }

        fontFileList.setEntries(items);
        fontFileList.setEntryValues(values);
        return fontFileList;
    }
}