Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b4e7b172 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add AvailableVirtualKeyboardFragment to TV"

parents ca022e2b 2ec69563
Loading
Loading
Loading
Loading
+0 −10
Original line number Diff line number Diff line
@@ -3880,13 +3880,6 @@
    <string name="show_password">Show passwords</string>
    <!-- On Security & location settings screen. This is a short summary text describing what "Show passwords" setting does -->
    <string name="show_password_summary">Display characters briefly as you type</string>
    <!-- Warning message about security implications of enabling an input method, displayed as a dialog
         message when the user selects to enable an IME. -->
    <string name="ime_security_warning">This input method may be able to collect
    all the text you type, including personal data like passwords and credit
    card numbers.  It comes from the app
    <xliff:g id="ime_application_name">%1$s</xliff:g>.
    Use this input method?</string>
    <!-- Warning message about security implications of enabling a spell checker, displayed as a dialog
         message when the user selects to enable a spell checker. -->
    <string name="spellchecker_security_warning">This spell checker may be able to collect
@@ -3898,9 +3891,6 @@
    <string name="spellchecker_quick_settings">Settings</string>
    <!-- Image button description for spell checker language. -->
    <string name="spellchecker_language">Language</string>
    <!-- Toast that settings for an application is failed to open. -->
    <string name="failed_to_open_app_settings_toast">Failed to open settings for <xliff:g id="spell_application_name">%1$s</xliff:g></string>
    <!-- Title for the 'keyboard and input methods' preference category. [CHAR LIMIT=45] -->
    <string name="keyboard_and_input_methods_category">Keyboard &amp; inputs</string>
    <!-- Title for the 'virtual keyboard' preference sub-screen. [CHAR LIMIT=35] -->
+7 −15
Original line number Diff line number Diff line
@@ -43,12 +43,11 @@ import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
import com.android.settings.search.SearchIndexableRaw;
import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
import com.android.settingslib.inputmethod.InputMethodPreference;
import com.android.settingslib.inputmethod.InputMethodSettingValuesWrapper;

import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFragment
@@ -115,7 +114,7 @@ public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFr
    private static Drawable getInputMethodIcon(@NonNull final PackageManager packageManager,
            @NonNull final InputMethodInfo imi) {
        final ServiceInfo si = imi.getServiceInfo();
        final ApplicationInfo ai = si.applicationInfo;
        final ApplicationInfo ai = si != null ? si.applicationInfo : null;
        final String packageName = imi.getPackageName();
        if (si == null || ai == null || packageName == null) {
            return new ColorDrawable(Color.TRANSPARENT);
@@ -151,8 +150,8 @@ public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFr
        final Context context = getPrefContext();
        final PackageManager packageManager = getActivity().getPackageManager();
        final List<InputMethodInfo> imis = mInputMethodSettingValues.getInputMethodList();
        final int N = (imis == null ? 0 : imis.size());
        for (int i = 0; i < N; ++i) {
        final int numImis = (imis == null ? 0 : imis.size());
        for (int i = 0; i < numImis; ++i) {
            final InputMethodInfo imi = imis.get(i);
            final boolean isAllowedByOrganization = permittedList == null
                    || permittedList.contains(imi.getPackageName());
@@ -162,14 +161,9 @@ public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFr
            mInputMethodPreferenceList.add(pref);
        }
        final Collator collator = Collator.getInstance();
        Collections.sort(mInputMethodPreferenceList, new Comparator<InputMethodPreference>() {
            @Override
            public int compare(InputMethodPreference lhs, InputMethodPreference rhs) {
                return lhs.compareTo(rhs, collator);
            }
        });
        mInputMethodPreferenceList.sort((lhs, rhs) -> lhs.compareTo(rhs, collator));
        getPreferenceScreen().removeAll();
        for (int i = 0; i < N; ++i) {
        for (int i = 0; i < numImis; ++i) {
            final InputMethodPreference pref = mInputMethodPreferenceList.get(i);
            pref.setOrder(i);
            getPreferenceScreen().addPreference(pref);
@@ -190,8 +184,6 @@ public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFr
    static List<SearchIndexableRaw> buildSearchIndexOfInputMethods(final Context context,
            final List<InputMethodInfo> inputMethods, final String screenTitle) {
        final List<SearchIndexableRaw> indexes = new ArrayList<>();
        final InputMethodManager imm = (InputMethodManager) context.getSystemService(
                Context.INPUT_METHOD_SERVICE);
        for (int i = 0; i < inputMethods.size(); i++) {
            final InputMethodInfo imi = inputMethods.get(i);
            final ServiceInfo serviceInfo = imi.getServiceInfo();
@@ -207,7 +199,7 @@ public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFr
        return indexes;
    }

    public static Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
    public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
            new BaseSearchIndexProvider() {
        @Override
        public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
+0 −308
Original line number Diff line number Diff line
/*
 * Copyright (C) 2011 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 com.android.settings.inputmethod;

import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;

import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.UserHandle;
import android.support.v7.preference.Preference;
import android.support.v7.preference.Preference.OnPreferenceChangeListener;
import android.support.v7.preference.Preference.OnPreferenceClickListener;
import android.text.TextUtils;
import android.util.Log;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import android.widget.Toast;

import com.android.internal.inputmethod.InputMethodUtils;
import com.android.settings.R;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
import com.android.settingslib.inputmethod.InputMethodSettingValuesWrapper;

import java.text.Collator;
import java.util.List;

/**
 * Input method preference.
 *
 * This preference represents an IME. It is used for two purposes. 1) An instance with a switch
 * is used to enable or disable the IME. 2) An instance without a switch is used to invoke the
 * setting activity of the IME.
 */
class InputMethodPreference extends RestrictedSwitchPreference implements OnPreferenceClickListener,
        OnPreferenceChangeListener {
    private static final String TAG = InputMethodPreference.class.getSimpleName();
    private static final String EMPTY_TEXT = "";
    private static final int NO_WIDGET = 0;

    interface OnSavePreferenceListener {
        /**
         * Called when this preference needs to be saved its state.
         *
         * Note that this preference is non-persistent and needs explicitly to be saved its state.
         * Because changing one IME state may change other IMEs' state, this is a place to update
         * other IMEs' state as well.
         *
         * @param pref This preference.
         */
        public void onSaveInputMethodPreference(InputMethodPreference pref);
    }

    private final InputMethodInfo mImi;
    private final boolean mHasPriorityInSorting;
    private final OnSavePreferenceListener mOnSaveListener;
    private final InputMethodSettingValuesWrapper mInputMethodSettingValues;
    private final boolean mIsAllowedByOrganization;

    private AlertDialog mDialog = null;

    /**
     * A preference entry of an input method.
     *
     * @param context The Context this is associated with.
     * @param imi The {@link InputMethodInfo} of this preference.
     * @param isImeEnabler true if this preference is the IME enabler that has enable/disable
     *     switches for all available IMEs, not the list of enabled IMEs.
     * @param isAllowedByOrganization false if the IME has been disabled by a device or profile
     *     owner.
     * @param onSaveListener The listener called when this preference has been changed and needs
     *     to save the state to shared preference.
     */
    InputMethodPreference(final Context context, final InputMethodInfo imi,
            final boolean isImeEnabler, final boolean isAllowedByOrganization,
            final OnSavePreferenceListener onSaveListener) {
        super(context);
        setPersistent(false);
        mImi = imi;
        mIsAllowedByOrganization = isAllowedByOrganization;
        mOnSaveListener = onSaveListener;
        if (!isImeEnabler) {
            // Remove switch widget.
            setWidgetLayoutResource(NO_WIDGET);
        }
        // Disable on/off switch texts.
        setSwitchTextOn(EMPTY_TEXT);
        setSwitchTextOff(EMPTY_TEXT);
        setKey(imi.getId());
        setTitle(imi.loadLabel(context.getPackageManager()));
        final String settingsActivity = imi.getSettingsActivity();
        if (TextUtils.isEmpty(settingsActivity)) {
            setIntent(null);
        } else {
            // Set an intent to invoke settings activity of an input method.
            final Intent intent = new Intent(Intent.ACTION_MAIN);
            intent.setClassName(imi.getPackageName(), settingsActivity);
            setIntent(intent);
        }
        mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(context);
        mHasPriorityInSorting = InputMethodUtils.isSystemIme(imi)
                && mInputMethodSettingValues.isValidSystemNonAuxAsciiCapableIme(imi, context);
        setOnPreferenceClickListener(this);
        setOnPreferenceChangeListener(this);
    }

    public InputMethodInfo getInputMethodInfo() {
        return mImi;
    }

    private boolean isImeEnabler() {
        // If this {@link SwitchPreference} doesn't have a widget layout, we explicitly hide the
        // switch widget at constructor.
        return getWidgetLayoutResource() != NO_WIDGET;
    }

    @Override
    public boolean onPreferenceChange(final Preference preference, final Object newValue) {
        // Always returns false to prevent default behavior.
        // See {@link TwoStatePreference#onClick()}.
        if (!isImeEnabler()) {
            // Prevent disabling an IME because this preference is for invoking a settings activity.
            return false;
        }
        if (isChecked()) {
            // Disable this IME.
            setCheckedInternal(false);
            return false;
        }
        if (InputMethodUtils.isSystemIme(mImi)) {
            // Enable a system IME. No need to show a security warning dialog,
            // but we might need to prompt if it's not Direct Boot aware.
            if (mImi.getServiceInfo().directBootAware) {
                setCheckedInternal(true);
            } else {
                showDirectBootWarnDialog();
            }
        } else {
            // Once security is confirmed, we might prompt if the IME isn't
            // Direct Boot aware.
            showSecurityWarnDialog();
        }
        return false;
    }

    @Override
    public boolean onPreferenceClick(final Preference preference) {
        // Always returns true to prevent invoking an intent without catching exceptions.
        // See {@link Preference#performClick(PreferenceScreen)}/
        if (isImeEnabler()) {
            // Prevent invoking a settings activity because this preference is for enabling and
            // disabling an input method.
            return true;
        }
        final Context context = getContext();
        try {
            final Intent intent = getIntent();
            if (intent != null) {
                // Invoke a settings activity of an input method.
                context.startActivity(intent);
            }
        } catch (final ActivityNotFoundException e) {
            Log.d(TAG, "IME's Settings Activity Not Found", e);
            final String message = context.getString(
                    R.string.failed_to_open_app_settings_toast,
                    mImi.loadLabel(context.getPackageManager()));
            Toast.makeText(context, message, Toast.LENGTH_LONG).show();
        }
        return true;
    }

    void updatePreferenceViews() {
        final boolean isAlwaysChecked = mInputMethodSettingValues.isAlwaysCheckedIme(
                mImi, getContext());
        // When this preference has a switch and an input method should be always enabled,
        // this preference should be disabled to prevent accidentally disabling an input method.
        // This preference should also be disabled in case the admin does not allow this input
        // method.
        if (isAlwaysChecked && isImeEnabler()) {
            setDisabledByAdmin(null);
            setEnabled(false);
        } else if (!mIsAllowedByOrganization) {
            EnforcedAdmin admin =
                    RestrictedLockUtils.checkIfInputMethodDisallowed(getContext(),
                            mImi.getPackageName(), UserHandle.myUserId());
            setDisabledByAdmin(admin);
        } else {
            setEnabled(true);
        }
        setChecked(mInputMethodSettingValues.isEnabledImi(mImi));
        if (!isDisabledByAdmin()) {
            setSummary(getSummaryString());
        }
    }

    private InputMethodManager getInputMethodManager() {
        return (InputMethodManager)getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
    }

    private String getSummaryString() {
        final InputMethodManager imm = getInputMethodManager();
        final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(mImi, true);
        return InputMethodAndSubtypeUtil.getSubtypeLocaleNameListAsSentence(
                subtypes, getContext(), mImi);
    }

    private void setCheckedInternal(boolean checked) {
        super.setChecked(checked);
        mOnSaveListener.onSaveInputMethodPreference(InputMethodPreference.this);
        notifyChanged();
    }

    private void showSecurityWarnDialog() {
        if (mDialog != null && mDialog.isShowing()) {
            mDialog.dismiss();
        }
        final Context context = getContext();
        final AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setCancelable(true /* cancelable */);
        builder.setTitle(android.R.string.dialog_alert_title);
        final CharSequence label = mImi.getServiceInfo().applicationInfo.loadLabel(
                context.getPackageManager());
        builder.setMessage(context.getString(R.string.ime_security_warning, label));
        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(final DialogInterface dialog, final int which) {
                // The user confirmed to enable a 3rd party IME, but we might
                // need to prompt if it's not Direct Boot aware.
                if (mImi.getServiceInfo().directBootAware) {
                    setCheckedInternal(true);
                } else {
                    showDirectBootWarnDialog();
                }
            }
        });
        builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(final DialogInterface dialog, final int which) {
                // The user canceled to enable a 3rd party IME.
                setCheckedInternal(false);
            }
        });
        mDialog = builder.create();
        mDialog.show();
    }

    private void showDirectBootWarnDialog() {
        if (mDialog != null && mDialog.isShowing()) {
            mDialog.dismiss();
        }
        final Context context = getContext();
        final AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setCancelable(true /* cancelable */);
        builder.setMessage(context.getText(R.string.direct_boot_unaware_dialog_message));
        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(final DialogInterface dialog, final int which) {
                setCheckedInternal(true);
            }
        });
        builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(final DialogInterface dialog, final int which) {
                setCheckedInternal(false);
            }
        });
        mDialog = builder.create();
        mDialog.show();
    }

    int compareTo(final InputMethodPreference rhs, final Collator collator) {
        if (this == rhs) {
            return 0;
        }
        if (mHasPriorityInSorting == rhs.mHasPriorityInSorting) {
            final CharSequence t0 = getTitle();
            final CharSequence t1 = rhs.getTitle();
            if (TextUtils.isEmpty(t0)) {
                return 1;
            }
            if (TextUtils.isEmpty(t1)) {
                return -1;
            }
            return collator.compare(t0.toString(), t1.toString());
        }
        // Prefer always checked system IMEs
        return mHasPriorityInSorting ? -1 : 1;
    }
}
+2 −6
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
import com.android.settings.search.SearchIndexableRaw;
import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
import com.android.settingslib.inputmethod.InputMethodPreference;

import java.text.Collator;
import java.util.ArrayList;
@@ -104,12 +105,7 @@ public final class VirtualKeyboardFragment extends SettingsPreferenceFragment im
            mInputMethodPreferenceList.add(pref);
        }
        final Collator collator = Collator.getInstance();
        Collections.sort(mInputMethodPreferenceList, new Comparator<InputMethodPreference>() {
            @Override
            public int compare(InputMethodPreference lhs, InputMethodPreference rhs) {
                return lhs.compareTo(rhs, collator);
            }
        });
        mInputMethodPreferenceList.sort((lhs, rhs) -> lhs.compareTo(rhs, collator));
        getPreferenceScreen().removeAll();
        for (int i = 0; i < N; ++i) {
            final InputMethodPreference pref = mInputMethodPreferenceList.get(i);