Deprecate AppListPreference and AppListPrefWithSettings

- Convert ManageAssist into DashboardFragment
- Convert default assist pref to DefaultAppPickerFragment
- Add PreferenceController for each pref
- Add tests

Bug: 35203386
Test: make RunSettingsRoboTests

Change-Id: I0350a06cae7457809fb261e2d8ec99eda80cc50a
diff --git a/src/com/android/settings/AppListPreference.java b/src/com/android/settings/AppListPreference.java
deleted file mode 100644
index 8585454..0000000
--- a/src/com/android/settings/AppListPreference.java
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (C) 2013 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;
-
-import android.app.AlertDialog;
-import android.app.AppGlobals;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ImageView;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Extends ListPreference to allow us to show the icons for a given list of applications. We do this
- * because the names of applications are very similar and the user may not be able to determine what
- * app they are selecting without an icon.
- *
- * @deprecated Selecting app from a list should be done in full UI. Use DefaultAppPickerFragment
- * instead.
- */
-@Deprecated
-public class AppListPreference extends CustomListPreference {
-
-    public static final String ITEM_NONE_VALUE = "";
-
-    protected final boolean mForWork;
-    protected final int mUserId;
-
-
-    private boolean mSavesState = true;
-    private Drawable[] mEntryDrawables;
-    private boolean mShowItemNone = false;
-    private CharSequence[] mSummaries;
-    private int mSystemAppIndex = -1;
-
-    public class AppArrayAdapter extends ArrayAdapter<CharSequence> {
-        private Drawable[] mImageDrawables = null;
-        private int mSelectedIndex = 0;
-
-        public AppArrayAdapter(Context context, int textViewResourceId,
-                CharSequence[] objects, Drawable[] imageDrawables, int selectedIndex) {
-            super(context, textViewResourceId, objects);
-            mSelectedIndex = selectedIndex;
-            mImageDrawables = imageDrawables;
-        }
-
-        @Override
-        public boolean isEnabled(int position) {
-            return mSummaries == null || mSummaries[position] == null;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            LayoutInflater inflater = LayoutInflater.from(getContext());
-            View view = inflater.inflate(R.layout.app_preference_item, parent, false);
-            TextView textView = (TextView) view.findViewById(android.R.id.title);
-            textView.setText(getItem(position));
-            if (position == mSelectedIndex && position == mSystemAppIndex) {
-                view.findViewById(R.id.system_default_label).setVisibility(View.VISIBLE);
-            } else if (position == mSelectedIndex) {
-                view.findViewById(R.id.default_label).setVisibility(View.VISIBLE);
-            } else if (position == mSystemAppIndex) {
-                view.findViewById(R.id.system_label).setVisibility(View.VISIBLE);
-            }
-            ImageView imageView = (ImageView) view.findViewById(android.R.id.icon);
-            imageView.setImageDrawable(mImageDrawables[position]);
-            // Summaries are describing why a item is disabled, so anything with a summary
-            // is not enabled.
-            boolean enabled = mSummaries == null || mSummaries[position] == null;
-            view.setEnabled(enabled);
-            if (!enabled) {
-                TextView summary = (TextView) view.findViewById(android.R.id.summary);
-                summary.setText(mSummaries[position]);
-                summary.setVisibility(View.VISIBLE);
-            }
-            return view;
-        }
-    }
-
-    public AppListPreference(Context context, AttributeSet attrs, int defStyle, int defAttrs) {
-        super(context, attrs, defStyle, defAttrs);
-
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WorkPreference, 0, 0);
-        mForWork = a.getBoolean(R.styleable.WorkPreference_forWork, false);
-        final UserHandle managedProfile = Utils.getManagedProfile(UserManager.get(context));
-        mUserId = mForWork && managedProfile != null ? managedProfile.getIdentifier()
-                : UserHandle.myUserId();
-    }
-
-    public AppListPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WorkPreference, 0, 0);
-        mForWork = a.getBoolean(R.styleable.WorkPreference_forWork, false);
-        final UserHandle managedProfile = Utils.getManagedProfile(UserManager.get(context));
-        mUserId = mForWork && managedProfile != null ? managedProfile.getIdentifier()
-                : UserHandle.myUserId();
-    }
-
-    public void setSavesState(boolean savesState) {
-        mSavesState = savesState;
-    }
-
-    public void setShowItemNone(boolean showItemNone) {
-        mShowItemNone = showItemNone;
-    }
-
-    public void setPackageNames(CharSequence[] packageNames, CharSequence defaultPackageName) {
-        setPackageNames(packageNames, defaultPackageName, null);
-    }
-
-    public void setPackageNames(CharSequence[] packageNames, CharSequence defaultPackageName,
-            CharSequence systemPackageName) {
-        // Look up all package names in PackageManager. Skip ones we can't find.
-        PackageManager pm = getContext().getPackageManager();
-        final int entryCount = packageNames.length + (mShowItemNone ? 1 : 0);
-        List<CharSequence> applicationNames = new ArrayList<>(entryCount);
-        List<CharSequence> validatedPackageNames = new ArrayList<>(entryCount);
-        List<Drawable> entryDrawables = new ArrayList<>(entryCount);
-        int selectedIndex = -1;
-        mSystemAppIndex = -1;
-        for (int i = 0; i < packageNames.length; i++) {
-            try {
-                ApplicationInfo appInfo = pm.getApplicationInfoAsUser(packageNames[i].toString(), 0,
-                        mUserId);
-                applicationNames.add(appInfo.loadLabel(pm));
-                validatedPackageNames.add(appInfo.packageName);
-                entryDrawables.add(appInfo.loadIcon(pm));
-                if (defaultPackageName != null &&
-                        appInfo.packageName.contentEquals(defaultPackageName)) {
-                    selectedIndex = i;
-                }
-                if (appInfo.packageName != null && systemPackageName != null &&
-                        appInfo.packageName.contentEquals(systemPackageName)) {
-                    mSystemAppIndex = i;
-                }
-            } catch (NameNotFoundException e) {
-                // Skip unknown packages.
-            }
-        }
-
-        if (mShowItemNone) {
-            applicationNames.add(
-                    getContext().getResources().getText(R.string.app_list_preference_none));
-            validatedPackageNames.add(ITEM_NONE_VALUE);
-            entryDrawables.add(getContext().getDrawable(R.drawable.ic_remove_circle));
-        }
-
-        setEntries(applicationNames.toArray(new CharSequence[applicationNames.size()]));
-        setEntryValues(
-                validatedPackageNames.toArray(new CharSequence[validatedPackageNames.size()]));
-        mEntryDrawables = entryDrawables.toArray(new Drawable[entryDrawables.size()]);
-
-        if (selectedIndex != -1) {
-            setValueIndex(selectedIndex);
-        } else {
-            setValue(null);
-        }
-    }
-
-    public void setComponentNames(ComponentName[] componentNames, ComponentName defaultCN) {
-        setComponentNames(componentNames, defaultCN, null);
-    }
-
-    public void setComponentNames(ComponentName[] componentNames, ComponentName defaultCN,
-            CharSequence[] summaries) {
-        mSummaries = summaries;
-        // Look up all package names in PackageManager. Skip ones we can't find.
-        PackageManager pm = getContext().getPackageManager();
-        final int entryCount = componentNames.length + (mShowItemNone ? 1 : 0);
-        List<CharSequence> applicationNames = new ArrayList<>(entryCount);
-        List<CharSequence> validatedComponentNames = new ArrayList<>(entryCount);
-        List<Drawable> entryDrawables = new ArrayList<>(entryCount);
-        int selectedIndex = -1;
-        for (int i = 0; i < componentNames.length; i++) {
-            try {
-                ActivityInfo activityInfo = AppGlobals.getPackageManager().getActivityInfo(
-                        componentNames[i], 0, mUserId);
-                if (activityInfo != null) {
-                    applicationNames.add(activityInfo.loadLabel(pm));
-                    validatedComponentNames.add(componentNames[i].flattenToString());
-                    entryDrawables.add(activityInfo.loadIcon(pm));
-                } else {
-                    ApplicationInfo appInfo = pm.getApplicationInfoAsUser(
-                            componentNames[i].getPackageName().toString(), 0, mUserId);
-                    applicationNames.add(appInfo.loadLabel(pm));
-                    validatedComponentNames.add(componentNames[i].flattenToString());
-                    entryDrawables.add(appInfo.loadIcon(pm));
-                }
-                if (defaultCN != null && componentNames[i].equals(defaultCN)) {
-                    selectedIndex = i;
-                }
-            } catch (RemoteException|NameNotFoundException e) {
-                // Skip unknown packages.
-            }
-        }
-
-        if (mShowItemNone) {
-            applicationNames.add(
-                    getContext().getResources().getText(R.string.app_list_preference_none));
-            validatedComponentNames.add(ITEM_NONE_VALUE);
-            entryDrawables.add(getContext().getDrawable(R.drawable.ic_remove_circle));
-        }
-
-        setEntries(applicationNames.toArray(new CharSequence[applicationNames.size()]));
-        setEntryValues(
-                validatedComponentNames.toArray(new CharSequence[validatedComponentNames.size()]));
-        mEntryDrawables = entryDrawables.toArray(new Drawable[entryDrawables.size()]);
-
-        if (selectedIndex != -1) {
-            setValueIndex(selectedIndex);
-        } else {
-            setValue(null);
-        }
-    }
-
-    protected ListAdapter createListAdapter() {
-        final String selectedValue = getValue();
-        final boolean selectedNone = selectedValue == null ||
-                (mShowItemNone && selectedValue.contentEquals(ITEM_NONE_VALUE));
-        int selectedIndex = selectedNone ? -1 : findIndexOfValue(selectedValue);
-        return new AppArrayAdapter(getContext(),
-            R.layout.app_preference_item, getEntries(), mEntryDrawables, selectedIndex);
-    }
-
-    @Override
-    protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
-            DialogInterface.OnClickListener listener) {
-        builder.setAdapter(createListAdapter(), listener);
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        Parcelable superState = super.onSaveInstanceState();
-        if (mSavesState) {
-            return new SavedState(getEntryValues(), getValue(), mSummaries, mShowItemNone, superState);
-        } else {
-            return superState;
-        }
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (mSavesState || state instanceof SavedState) {
-            SavedState savedState = (SavedState) state;
-            mShowItemNone = savedState.showItemNone;
-            setPackageNames(savedState.entryValues, savedState.value);
-            mSummaries = savedState.summaries;
-            super.onRestoreInstanceState(savedState.superState);
-        } else {
-            super.onRestoreInstanceState(state);
-        }
-    }
-
-    /**
-     * Sets app label as summary if there is only 1 app applicable to this preference.
-     */
-    protected void setSoleAppLabelAsSummary() {
-        final CharSequence soleLauncherLabel = getSoleAppLabel();
-        if (!TextUtils.isEmpty(soleLauncherLabel)) {
-            setSummary(soleLauncherLabel);
-        }
-    }
-
-    /**
-     * Returns app label if there is only 1 app applicable to this preference.
-     */
-    protected CharSequence getSoleAppLabel() {
-        // Intentionally left empty so subclasses can override with necessary logic.
-        return null;
-    }
-
-    private static class SavedState implements Parcelable {
-
-        public final CharSequence[] entryValues;
-        public final CharSequence value;
-        public final boolean showItemNone;
-        public final Parcelable superState;
-        public final CharSequence[] summaries;
-
-        public SavedState(CharSequence[] entryValues, CharSequence value, CharSequence[] summaries,
-                boolean showItemNone, Parcelable superState) {
-            this.entryValues = entryValues;
-            this.value = value;
-            this.showItemNone = showItemNone;
-            this.superState = superState;
-            this.summaries = summaries;
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeCharSequenceArray(entryValues);
-            dest.writeCharSequence(value);
-            dest.writeInt(showItemNone ? 1 : 0);
-            dest.writeParcelable(superState, flags);
-            dest.writeCharSequenceArray(summaries);
-        }
-
-        public static Creator<SavedState> CREATOR = new Creator<SavedState>() {
-            @Override
-            public SavedState createFromParcel(Parcel source) {
-                CharSequence[] entryValues = source.readCharSequenceArray();
-                CharSequence value = source.readCharSequence();
-                boolean showItemNone = source.readInt() != 0;
-                Parcelable superState = source.readParcelable(getClass().getClassLoader());
-                CharSequence[] summaries = source.readCharSequenceArray();
-                return new SavedState(entryValues, value, summaries, showItemNone, superState);
-            }
-
-            @Override
-            public SavedState[] newArray(int size) {
-                return new SavedState[size];
-            }
-        };
-    }
-}
diff --git a/src/com/android/settings/AppListPreferenceWithSettings.java b/src/com/android/settings/AppListPreferenceWithSettings.java
deleted file mode 100644
index a499a7b..0000000
--- a/src/com/android/settings/AppListPreferenceWithSettings.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.android.settings;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.support.v7.preference.PreferenceViewHolder;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * An AppListPreference with optional settings button.
- */
-@Deprecated
-public class AppListPreferenceWithSettings extends AppListPreference {
-
-    private View mSettingsIcon;
-    private ComponentName mSettingsComponent;
-
-    public AppListPreferenceWithSettings(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setWidgetLayoutResource(R.layout.preference_widget_settings);
-    }
-
-    @Override
-    public void onBindViewHolder(PreferenceViewHolder view) {
-        super.onBindViewHolder(view);
-
-        mSettingsIcon = view.findViewById(R.id.settings_button);
-        mSettingsIcon.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                Intent intent = new Intent(Intent.ACTION_MAIN);
-                intent.setComponent(mSettingsComponent);
-                getContext().startActivity(new Intent(intent));
-            }
-        });
-
-        ViewGroup container = (ViewGroup) mSettingsIcon.getParent();
-        container.setPaddingRelative(0, 0, 0, 0);
-
-        updateSettingsVisibility();
-    }
-
-    private void updateSettingsVisibility() {
-        if (mSettingsIcon == null) {
-            return;
-        }
-
-        if (mSettingsComponent == null) {
-            mSettingsIcon.setVisibility(View.GONE);
-        } else {
-            mSettingsIcon.setVisibility(View.VISIBLE);
-        }
-    }
-
-    protected void setSettingsComponent(ComponentName settings) {
-        mSettingsComponent = settings;
-        updateSettingsVisibility();
-    }
-}
diff --git a/src/com/android/settings/applications/ManageAssist.java b/src/com/android/settings/applications/ManageAssist.java
deleted file mode 100644
index 8dd4d70..0000000
--- a/src/com/android/settings/applications/ManageAssist.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2015 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.applications;
-
-import android.app.AlertDialog;
-import android.content.ComponentName;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.os.Handler;
-import android.provider.Settings;
-import android.support.v14.preference.SwitchPreference;
-import android.support.v7.preference.Preference;
-
-import com.android.internal.app.AssistUtils;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.gestures.AssistGestureFeatureProvider;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.voice.VoiceInputListPreference;
-
-/**
- * Settings screen to manage everything about assist.
- */
-public class ManageAssist extends SettingsPreferenceFragment
-        implements Preference.OnPreferenceChangeListener {
-
-    private static final String KEY_DEFAULT_ASSIST = "default_assist";
-    private static final String KEY_ASSIST_GESTURE = "gesture_assist";
-    private static final String KEY_CONTEXT = "context";
-    private static final String KEY_SCREENSHOT = "screenshot";
-    private static final String KEY_VOICE_INPUT = "voice_input_settings";
-    private static final String KEY_FLASH = "flash";
-
-    private DefaultAssistPreference mDefaultAssitPref;
-    private SwitchPreference mContextPref;
-    private SwitchPreference mScreenshotPref;
-    private SwitchPreference mFlashPref;
-    private VoiceInputListPreference mVoiceInputPref;
-    private Handler mHandler = new Handler();
-
-    private Preference mAssistGesturePref;
-    private AssistGestureFeatureProvider mAssistGestureFeatureProvider;
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        addPreferencesFromResource(R.xml.manage_assist);
-
-        mDefaultAssitPref = (DefaultAssistPreference) findPreference(KEY_DEFAULT_ASSIST);
-        mDefaultAssitPref.setOnPreferenceChangeListener(this);
-
-        mAssistGesturePref = findPreference(KEY_ASSIST_GESTURE);
-        mAssistGestureFeatureProvider =
-                FeatureFactory.getFactory(getContext()).getAssistGestureFeatureProvider();
-
-        mContextPref = (SwitchPreference) findPreference(KEY_CONTEXT);
-        mContextPref.setChecked(Settings.Secure.getInt(getContentResolver(),
-                Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1) != 0);
-        mContextPref.setOnPreferenceChangeListener(this);
-
-        mScreenshotPref = (SwitchPreference) findPreference(KEY_SCREENSHOT);
-        mScreenshotPref.setOnPreferenceChangeListener(this);
-
-        mFlashPref = (SwitchPreference) findPreference(KEY_FLASH);
-        mFlashPref.setOnPreferenceChangeListener(this);
-        mFooterPreferenceMixin.createFooterPreference()
-                .setTitle(R.string.assist_footer);
-        mVoiceInputPref = (VoiceInputListPreference) findPreference(KEY_VOICE_INPUT);
-        updateUi();
-    }
-
-    @Override
-    public int getMetricsCategory() {
-        return MetricsEvent.APPLICATIONS_MANAGE_ASSIST;
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        if (preference == mContextPref) {
-            Settings.Secure.putInt(getContentResolver(), Settings.Secure.ASSIST_STRUCTURE_ENABLED,
-                    (boolean) newValue ? 1 : 0);
-            mHandler.post(() -> {
-                guardFlashPref();
-            });
-            return true;
-        }
-        if (preference == mScreenshotPref) {
-            Settings.Secure.putInt(getContentResolver(), Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
-                    (boolean) newValue ? 1 : 0);
-            return true;
-        }
-        if (preference == mFlashPref) {
-            Settings.Secure.putInt(getContentResolver(), Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
-                    (boolean) newValue ? 1 : 0);
-            return true;
-        }
-        if (preference == mDefaultAssitPref) {
-            String newAssitPackage = (String)newValue;
-            if (newAssitPackage == null ||
-                    newAssitPackage.contentEquals(DefaultAssistPreference.ITEM_NONE_VALUE)) {
-                setDefaultAssist(DefaultAssistPreference.ITEM_NONE_VALUE);
-                return false;
-            }
-
-            final String currentPackage = mDefaultAssitPref.getValue();
-            if (currentPackage == null || !newAssitPackage.contentEquals(currentPackage)) {
-                confirmNewAssist(newAssitPackage);
-            }
-            return false;
-        }
-        return false;
-    }
-
-    private void guardFlashPref() {
-        ComponentName assistant = mDefaultAssitPref.getCurrentAssist();
-
-        boolean isContextChecked = mContextPref.isChecked();
-        boolean willShowFlash = AssistUtils.shouldDisclose(getContext(), assistant);
-        boolean isSystemAssistant = AssistUtils.isPreinstalledAssistant(getContext(), assistant);
-
-        mFlashPref.setEnabled(isContextChecked && isSystemAssistant);
-        mFlashPref.setChecked(willShowFlash);
-    }
-
-    private void updateUi() {
-        mDefaultAssitPref.refreshAssistApps();
-        mVoiceInputPref.refreshVoiceInputs();
-
-        final ComponentName currentAssist = mDefaultAssitPref.getCurrentAssist();
-        final boolean hasAssistant = currentAssist != null;
-        if (hasAssistant) {
-            getPreferenceScreen().addPreference(mContextPref);
-            getPreferenceScreen().addPreference(mScreenshotPref);
-        } else {
-            getPreferenceScreen().removePreference(mContextPref);
-            getPreferenceScreen().removePreference(mScreenshotPref);
-            getPreferenceScreen().removePreference(mFlashPref);
-        }
-
-        if (hasAssistant && mAssistGestureFeatureProvider.isSupported(getContext())) {
-            getPreferenceScreen().addPreference(mAssistGesturePref);
-        } else {
-            getPreferenceScreen().removePreference(mAssistGesturePref);
-        }
-
-        if (hasAssistant && AssistUtils.allowDisablingAssistDisclosure(getContext())) {
-            getPreferenceScreen().addPreference(mFlashPref);
-        } else {
-            getPreferenceScreen().removePreference(mFlashPref);
-        }
-
-        if (isCurrentAssistVoiceService()) {
-            getPreferenceScreen().removePreference(mVoiceInputPref);
-        } else {
-            getPreferenceScreen().addPreference(mVoiceInputPref);
-            mVoiceInputPref.setAssistRestrict(currentAssist);
-        }
-
-        guardFlashPref();
-    }
-
-    private boolean isCurrentAssistVoiceService() {
-        ComponentName currentAssist = mDefaultAssitPref.getCurrentAssist();
-        ComponentName activeService = mVoiceInputPref.getCurrentService();
-        return currentAssist == null && activeService == null ||
-                currentAssist != null && currentAssist.equals(activeService);
-    }
-
-    private void confirmNewAssist(final String newAssitPackage) {
-        final int selected = mDefaultAssitPref.findIndexOfValue(newAssitPackage);
-        final CharSequence appLabel = mDefaultAssitPref.getEntries()[selected];
-
-        final String title = getString(R.string.assistant_security_warning_title, appLabel);
-        final String message = getString(R.string.assistant_security_warning, appLabel);
-
-        final DialogInterface.OnClickListener onAgree = new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                setDefaultAssist(newAssitPackage);
-            }
-        };
-
-        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
-        builder.setTitle(title)
-                .setMessage(message)
-                .setCancelable(true)
-                .setPositiveButton(R.string.assistant_security_warning_agree, onAgree)
-                .setNegativeButton(R.string.assistant_security_warning_disagree, null);
-        AlertDialog dialog = builder.create();
-        dialog.show();
-    }
-
-    private void setDefaultAssist(String assistPackage) {
-        mDefaultAssitPref.setValue(assistPackage);
-        updateUi();
-    }
-}
diff --git a/src/com/android/settings/applications/assist/AssistContextPreferenceController.java b/src/com/android/settings/applications/assist/AssistContextPreferenceController.java
new file mode 100644
index 0000000..05bc8d6
--- /dev/null
+++ b/src/com/android/settings/applications/assist/AssistContextPreferenceController.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2017 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.applications.assist;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.support.v7.preference.TwoStatePreference;
+
+import com.android.internal.app.AssistUtils;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.core.lifecycle.Lifecycle;
+import com.android.settings.core.lifecycle.LifecycleObserver;
+import com.android.settings.core.lifecycle.events.OnPause;
+import com.android.settings.core.lifecycle.events.OnResume;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class AssistContextPreferenceController extends PreferenceController
+        implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
+
+    private static final String KEY_CONTEXT = "context";
+
+    private final AssistUtils mAssistUtils;
+    private final SettingObserver mSettingObserver;
+
+    private Preference mPreference;
+    private PreferenceScreen mScreen;
+
+    public AssistContextPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context);
+        mAssistUtils = new AssistUtils(context);
+        mSettingObserver = new SettingObserver();
+
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mAssistUtils.getAssistComponentForUser(UserHandle.myUserId()) != null;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_CONTEXT;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        mScreen = screen;
+        mPreference = screen.findPreference(getPreferenceKey());
+        super.displayPreference(screen);
+    }
+
+    @Override
+    public void onResume() {
+        mSettingObserver.register(mContext.getContentResolver(), true);
+        updatePreference();
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        updatePreference();
+    }
+
+    @Override
+    public void onPause() {
+        mSettingObserver.register(mContext.getContentResolver(), false);
+    }
+
+
+    private void updatePreference() {
+        if (mPreference == null || !(mPreference instanceof TwoStatePreference)) {
+            return;
+        }
+        if (isAvailable()) {
+            if (mScreen.findPreference(getPreferenceKey()) == null) {
+                // add it if it's not on scree
+                mScreen.addPreference(mPreference);
+            }
+        } else {
+            mScreen.removePreference(mPreference);
+        }
+
+        ((TwoStatePreference) mPreference).setChecked(isChecked(mContext));
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.ASSIST_STRUCTURE_ENABLED,
+                (boolean) newValue ? 1 : 0);
+        return true;
+    }
+
+    static boolean isChecked(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1) != 0;
+    }
+
+    class SettingObserver extends AssistSettingObserver {
+
+        private final Uri URI =
+                Settings.Secure.getUriFor(Settings.Secure.ASSIST_STRUCTURE_ENABLED);
+
+        @Override
+        protected List<Uri> getSettingUris() {
+            return Arrays.asList(URI);
+        }
+
+        @Override
+        public void onSettingChange() {
+            updatePreference();
+        }
+    }
+}
diff --git a/src/com/android/settings/applications/assist/AssistFlashScreenPreferenceController.java b/src/com/android/settings/applications/assist/AssistFlashScreenPreferenceController.java
new file mode 100644
index 0000000..e44e70f
--- /dev/null
+++ b/src/com/android/settings/applications/assist/AssistFlashScreenPreferenceController.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2017 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.applications.assist;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.support.v7.preference.TwoStatePreference;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.AssistUtils;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.core.lifecycle.Lifecycle;
+import com.android.settings.core.lifecycle.LifecycleObserver;
+import com.android.settings.core.lifecycle.events.OnPause;
+import com.android.settings.core.lifecycle.events.OnResume;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class AssistFlashScreenPreferenceController extends PreferenceController
+        implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
+
+    private static final String KEY_FLASH = "flash";
+
+    private final AssistUtils mAssistUtils;
+    private final SettingObserver mSettingObserver;
+    private PreferenceScreen mScreen;
+    private Preference mPreference;
+
+    public AssistFlashScreenPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context);
+        mAssistUtils = new AssistUtils(context);
+        mSettingObserver = new SettingObserver();
+
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return getCurrentAssist() != null && allowDisablingAssistDisclosure();
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_FLASH;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        mScreen = screen;
+        mPreference = screen.findPreference(getPreferenceKey());
+        super.displayPreference(screen);
+    }
+
+    @Override
+    public void onResume() {
+        mSettingObserver.register(mContext.getContentResolver(), true);
+        updatePreference();
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        updatePreference();
+    }
+
+    @Override
+    public void onPause() {
+        mSettingObserver.register(mContext.getContentResolver(), false);
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
+                (boolean) newValue ? 1 : 0);
+        return true;
+    }
+
+    private void updatePreference() {
+        if (mPreference == null || !(mPreference instanceof TwoStatePreference)) {
+            return;
+        }
+        if (isAvailable()) {
+            if (mScreen.findPreference(getPreferenceKey()) == null) {
+                // add it if it's not on scree
+                mScreen.addPreference(mPreference);
+            }
+        } else {
+            mScreen.removePreference(mPreference);
+        }
+        ComponentName assistant = getCurrentAssist();
+
+        boolean isContextChecked = AssistContextPreferenceController.isChecked(mContext);
+
+        mPreference.setEnabled(isContextChecked && isPreInstalledAssistant(assistant));
+        ((TwoStatePreference) mPreference).setChecked(willShowFlash(assistant));
+    }
+
+    @VisibleForTesting
+    boolean willShowFlash(ComponentName assistant) {
+        return AssistUtils.shouldDisclose(mContext, assistant);
+    }
+
+    @VisibleForTesting
+    boolean isPreInstalledAssistant(ComponentName assistant) {
+        return AssistUtils.isPreinstalledAssistant(mContext, assistant);
+    }
+
+    @VisibleForTesting
+    boolean allowDisablingAssistDisclosure() {
+        return AssistUtils.allowDisablingAssistDisclosure(mContext);
+    }
+
+    private ComponentName getCurrentAssist() {
+        return mAssistUtils.getAssistComponentForUser(UserHandle.myUserId());
+    }
+
+    class SettingObserver extends AssistSettingObserver {
+
+        private final Uri URI =
+                Settings.Secure.getUriFor(Settings.Secure.ASSIST_DISCLOSURE_ENABLED);
+        private final Uri CONTEXT_URI =
+                Settings.Secure.getUriFor(Settings.Secure.ASSIST_STRUCTURE_ENABLED);
+
+        @Override
+        protected List<Uri> getSettingUris() {
+            return Arrays.asList(URI, CONTEXT_URI);
+        }
+
+        @Override
+        public void onSettingChange() {
+            updatePreference();
+        }
+    }
+
+
+}
diff --git a/src/com/android/settings/applications/assist/AssistScreenshotPreferenceController.java b/src/com/android/settings/applications/assist/AssistScreenshotPreferenceController.java
new file mode 100644
index 0000000..21dfe19
--- /dev/null
+++ b/src/com/android/settings/applications/assist/AssistScreenshotPreferenceController.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2017 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.applications.assist;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.support.v7.preference.TwoStatePreference;
+
+import com.android.internal.app.AssistUtils;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.core.lifecycle.Lifecycle;
+import com.android.settings.core.lifecycle.LifecycleObserver;
+import com.android.settings.core.lifecycle.events.OnPause;
+import com.android.settings.core.lifecycle.events.OnResume;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class AssistScreenshotPreferenceController extends PreferenceController
+        implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
+
+    private static final String KEY_SCREENSHOT = "screenshot";
+
+    private final AssistUtils mAssistUtils;
+    private final SettingObserver mSettingObserver;
+
+    private PreferenceScreen mScreen;
+    private Preference mPreference;
+
+    public AssistScreenshotPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context);
+        mAssistUtils = new AssistUtils(context);
+        mSettingObserver = new SettingObserver();
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mAssistUtils.getAssistComponentForUser(UserHandle.myUserId()) != null;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        mScreen = screen;
+        mPreference = screen.findPreference(getPreferenceKey());
+        super.displayPreference(screen);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_SCREENSHOT;
+    }
+
+    @Override
+    public void onResume() {
+        mSettingObserver.register(mContext.getContentResolver(), true);
+        updatePreference();
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        updatePreference();
+    }
+
+    @Override
+    public void onPause() {
+        mSettingObserver.register(mContext.getContentResolver(), false);
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
+                (boolean) newValue ? 1 : 0);
+        return true;
+    }
+
+    private void updatePreference() {
+        if (mPreference == null || !(mPreference instanceof TwoStatePreference)) {
+            return;
+        }
+        if (isAvailable()) {
+            if (mScreen.findPreference(getPreferenceKey()) == null) {
+                // add it if it's not on scree
+                mScreen.addPreference(mPreference);
+            }
+        } else {
+            mScreen.removePreference(mPreference);
+        }
+        final boolean checked = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ASSIST_SCREENSHOT_ENABLED, 1) != 0;
+        ((TwoStatePreference) mPreference).setChecked(checked);
+        final boolean contextChecked = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1) != 0;
+        mPreference.setEnabled(contextChecked);
+    }
+
+    class SettingObserver extends AssistSettingObserver {
+
+        private final Uri URI =
+                Settings.Secure.getUriFor(Settings.Secure.ASSIST_SCREENSHOT_ENABLED);
+        private final Uri CONTEXT_URI =
+                Settings.Secure.getUriFor(Settings.Secure.ASSIST_STRUCTURE_ENABLED);
+
+        @Override
+        protected List<Uri> getSettingUris() {
+            return Arrays.asList(URI, CONTEXT_URI);
+        }
+
+        @Override
+        public void onSettingChange() {
+            updatePreference();
+        }
+    }
+}
diff --git a/src/com/android/settings/applications/assist/AssistSettingObserver.java b/src/com/android/settings/applications/assist/AssistSettingObserver.java
new file mode 100644
index 0000000..5d3d4a7
--- /dev/null
+++ b/src/com/android/settings/applications/assist/AssistSettingObserver.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 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.applications.assist;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+
+import java.util.List;
+
+public abstract class AssistSettingObserver extends ContentObserver {
+
+    private final Uri ASSIST_URI =
+            Settings.Secure.getUriFor(Settings.Secure.ASSISTANT);
+
+    public AssistSettingObserver() {
+        super(new Handler());
+    }
+
+    public void register(ContentResolver cr, boolean register) {
+        if (register) {
+            cr.registerContentObserver(ASSIST_URI, false, this);
+            final List<Uri> settingUri = getSettingUris();
+            if (settingUri != null) {
+                for (Uri uri : settingUri)
+                cr.registerContentObserver(uri, false, this);
+            }
+        } else {
+            cr.unregisterContentObserver(this);
+        }
+    }
+
+    @Override
+    public void onChange(boolean selfChange, Uri uri) {
+        super.onChange(selfChange, uri);
+        boolean shouldUpdatePreference = false;
+        final List<Uri> settingUri = getSettingUris();
+        if (ASSIST_URI.equals(uri) || (settingUri != null && settingUri.contains(uri))) {
+            shouldUpdatePreference = true;
+        }
+        if (shouldUpdatePreference) {
+            onSettingChange();
+        }
+    }
+
+    protected abstract List<Uri> getSettingUris();
+
+    public abstract void onSettingChange();
+}
diff --git a/src/com/android/settings/applications/DefaultAssistPreference.java b/src/com/android/settings/applications/assist/DefaultAssistPicker.java
similarity index 62%
rename from src/com/android/settings/applications/DefaultAssistPreference.java
rename to src/com/android/settings/applications/assist/DefaultAssistPicker.java
index 0bd729e..703665a 100644
--- a/src/com/android/settings/applications/DefaultAssistPreference.java
+++ b/src/com/android/settings/applications/assist/DefaultAssistPicker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -11,49 +11,105 @@
  * 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
+ * limitations under the License.
  */
 
-package com.android.settings.applications;
+package com.android.settings.applications.assist;
 
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.voice.VoiceInteractionService;
 import android.service.voice.VoiceInteractionServiceInfo;
 import android.speech.RecognitionService;
-import android.util.AttributeSet;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.app.AssistUtils;
-import com.android.settings.AppListPreferenceWithSettings;
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
+import com.android.settings.applications.defaultapps.DefaultAppInfo;
+import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
 
 import java.util.ArrayList;
 import java.util.List;
 
-public class DefaultAssistPreference extends AppListPreferenceWithSettings {
+public class DefaultAssistPicker extends DefaultAppPickerFragment {
 
-    private static final String TAG = DefaultAssistPreference.class.getSimpleName();
-
+    private static final String TAG = "DefaultAssistPicker";
+    private static final Intent ASSIST_SERVICE_PROBE =
+            new Intent(VoiceInteractionService.SERVICE_INTERFACE);
+    private static final Intent ASSIST_ACTIVITY_PROBE =
+            new Intent(Intent.ACTION_ASSIST);
     private final List<Info> mAvailableAssistants = new ArrayList<>();
 
-    private final AssistUtils mAssistUtils;
+    private AssistUtils mAssistUtils;
 
-    public DefaultAssistPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setShowItemNone(true);
-        setDialogTitle(R.string.choose_assist_title);
+    @Override
+    public int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.DEFAULT_ASSIST_PICKER;
+    }
+
+    @Override
+    protected boolean shouldShowItemNone() {
+        return true;
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
         mAssistUtils = new AssistUtils(context);
     }
 
     @Override
-    protected boolean persistString(String value) {
-        final Info info = findAssistantByPackageName(value);
+    protected List<DefaultAppInfo> getCandidates() {
+        mAvailableAssistants.clear();
+        addAssistServices();
+        addAssistActivities();
+
+        final List<String> packages = new ArrayList<>();
+        final List<DefaultAppInfo> candidates = new ArrayList<>();
+        for (Info info : mAvailableAssistants) {
+            final String packageName = info.component.getPackageName();
+            if (packages.contains(packageName)) {
+                // A service appears before an activity thus overrides it if from the same package.
+                continue;
+            }
+            packages.add(packageName);
+            candidates.add(new DefaultAppInfo(mUserId, info.component));
+        }
+        return candidates;
+    }
+
+    @Override
+    protected String getDefaultAppKey() {
+        final ComponentName cn = getCurrentAssist();
+        if (cn != null) {
+            return new DefaultAppInfo(mUserId, cn).getKey();
+        }
+        return null;
+    }
+
+    @Override
+    protected String getConfirmationMessage(DefaultAppInfo appInfo) {
+        if (appInfo == null) {
+            return null;
+        }
+        return getContext().getString(R.string.assistant_security_warning,
+                appInfo.loadLabel(mPm.getPackageManager()));
+    }
+
+    @Override
+    protected boolean setDefaultAppKey(String key) {
+        if (TextUtils.isEmpty(key)) {
+            setAssistNone();
+            return true;
+        }
+        ComponentName cn = ComponentName.unflattenFromString(key);
+        final Info info = findAssistantByPackageName(cn.getPackageName());
         if (info == null) {
             setAssistNone();
             return true;
@@ -67,20 +123,60 @@
         return true;
     }
 
+    public ComponentName getCurrentAssist() {
+        return mAssistUtils.getAssistComponentForUser(mUserId);
+    }
+
+    private void addAssistServices() {
+        final PackageManager pm = mPm.getPackageManager();
+        final List<ResolveInfo> services = pm.queryIntentServices(
+                ASSIST_SERVICE_PROBE, PackageManager.GET_META_DATA);
+        for (ResolveInfo resolveInfo : services) {
+            VoiceInteractionServiceInfo voiceInteractionServiceInfo =
+                    new VoiceInteractionServiceInfo(pm, resolveInfo.serviceInfo);
+            if (!voiceInteractionServiceInfo.getSupportsAssist()) {
+                continue;
+            }
+
+            mAvailableAssistants.add(new Info(
+                    new ComponentName(resolveInfo.serviceInfo.packageName,
+                            resolveInfo.serviceInfo.name),
+                    voiceInteractionServiceInfo));
+        }
+    }
+
+    private void addAssistActivities() {
+        final PackageManager pm = mPm.getPackageManager();
+        final List<ResolveInfo> activities = pm.queryIntentActivities(
+                ASSIST_ACTIVITY_PROBE, PackageManager.MATCH_DEFAULT_ONLY);
+        for (ResolveInfo resolveInfo : activities) {
+            mAvailableAssistants.add(new Info(
+                    new ComponentName(resolveInfo.activityInfo.packageName,
+                            resolveInfo.activityInfo.name)));
+        }
+    }
+
+    private Info findAssistantByPackageName(String packageName) {
+        for (Info info : mAvailableAssistants) {
+            if (TextUtils.equals(info.component.getPackageName(), packageName)) {
+                return info;
+            }
+        }
+        return null;
+    }
+
     private void setAssistNone() {
         Settings.Secure.putString(getContext().getContentResolver(),
-                Settings.Secure.ASSISTANT, ITEM_NONE_VALUE);
+                Settings.Secure.ASSISTANT, "");
         Settings.Secure.putString(getContext().getContentResolver(),
                 Settings.Secure.VOICE_INTERACTION_SERVICE, "");
         Settings.Secure.putString(getContext().getContentResolver(),
                 Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer());
-
-        setSummary(getContext().getText(R.string.default_assist_none));
-        setSettingsComponent(null);
     }
 
     private void setAssistService(Info serviceInfo) {
-        final String serviceComponentName = serviceInfo.component.flattenToShortString();
+        final String serviceComponentName = serviceInfo.component.
+                flattenToShortString();
         final String serviceRecognizerName = new ComponentName(
                 serviceInfo.component.getPackageName(),
                 serviceInfo.voiceInteractionServiceInfo.getRecognitionService())
@@ -92,13 +188,6 @@
                 Settings.Secure.VOICE_INTERACTION_SERVICE, serviceComponentName);
         Settings.Secure.putString(getContext().getContentResolver(),
                 Settings.Secure.VOICE_RECOGNITION_SERVICE, serviceRecognizerName);
-
-        setSummary(getEntry());
-        final String settingsActivity =
-                serviceInfo.voiceInteractionServiceInfo.getSettingsActivity();
-        setSettingsComponent(settingsActivity == null ?
-                null :
-                new ComponentName(serviceInfo.component.getPackageName(), settingsActivity));
     }
 
     private void setAssistActivity(Info activityInfo) {
@@ -108,13 +197,10 @@
                 Settings.Secure.VOICE_INTERACTION_SERVICE, "");
         Settings.Secure.putString(getContext().getContentResolver(),
                 Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer());
-
-        setSummary(getEntry());
-        setSettingsComponent(null);
     }
 
     private String getDefaultRecognizer() {
-        ResolveInfo resolveInfo = getContext().getPackageManager().resolveService(
+        final ResolveInfo resolveInfo = mPm.getPackageManager().resolveService(
                 new Intent(RecognitionService.SERVICE_INTERFACE),
                 PackageManager.GET_META_DATA);
         if (resolveInfo == null || resolveInfo.serviceInfo == null) {
@@ -126,76 +212,7 @@
                 resolveInfo.serviceInfo.name).flattenToShortString();
     }
 
-    private Info findAssistantByPackageName(String packageName) {
-        for (int i = 0; i < mAvailableAssistants.size(); ++i) {
-            Info info = mAvailableAssistants.get(i);
-            if (info.component.getPackageName().equals(packageName)) {
-                return info;
-            }
-        }
-        return null;
-    }
-
-    private void addAssistServices() {
-        PackageManager pm = getContext().getPackageManager();
-
-        List<ResolveInfo> services = pm.queryIntentServices(
-                new Intent(VoiceInteractionService.SERVICE_INTERFACE),
-                PackageManager.GET_META_DATA);
-        for (int i = 0; i < services.size(); ++i) {
-            ResolveInfo resolveInfo = services.get(i);
-            VoiceInteractionServiceInfo voiceInteractionServiceInfo =
-                    new VoiceInteractionServiceInfo(pm, resolveInfo.serviceInfo);
-            if (!voiceInteractionServiceInfo.getSupportsAssist()) {
-                continue;
-            }
-
-            mAvailableAssistants.add(new Info(
-                    new ComponentName(resolveInfo.serviceInfo.packageName,
-                                      resolveInfo.serviceInfo.name),
-                    voiceInteractionServiceInfo));
-        }
-    }
-
-    private void addAssistActivities() {
-        PackageManager pm = getContext().getPackageManager();
-
-        List<ResolveInfo> activities = pm.queryIntentActivities(
-                new Intent(Intent.ACTION_ASSIST),
-                PackageManager.MATCH_DEFAULT_ONLY);
-        for (int i = 0; i < activities.size(); ++i) {
-            ResolveInfo resolveInfo = activities.get(i);
-            mAvailableAssistants.add(new Info(
-                    new ComponentName(resolveInfo.activityInfo.packageName,
-                                      resolveInfo.activityInfo.name)));
-        }
-    }
-
-    public ComponentName getCurrentAssist() {
-        return mAssistUtils.getAssistComponentForUser(UserHandle.myUserId());
-    }
-
-    public void refreshAssistApps() {
-        mAvailableAssistants.clear();
-        addAssistServices();
-        addAssistActivities();
-
-        List<String> packages = new ArrayList<>();
-        for (int i = 0; i < mAvailableAssistants.size(); ++i) {
-            String packageName = mAvailableAssistants.get(i).component.getPackageName();
-            if (packages.contains(packageName)) {
-                // A service appears before an activity thus overrides it if from the same package.
-                continue;
-            }
-            packages.add(packageName);
-        }
-
-        ComponentName currentAssist = getCurrentAssist();
-        setPackageNames(packages.toArray(new String[packages.size()]),
-                currentAssist == null ? null : currentAssist.getPackageName());
-    }
-
-    private static class Info {
+    static class Info {
         public final ComponentName component;
         public final VoiceInteractionServiceInfo voiceInteractionServiceInfo;
 
diff --git a/src/com/android/settings/applications/assist/DefaultAssistPreferenceController.java b/src/com/android/settings/applications/assist/DefaultAssistPreferenceController.java
new file mode 100644
index 0000000..df34501
--- /dev/null
+++ b/src/com/android/settings/applications/assist/DefaultAssistPreferenceController.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 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.applications.assist;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.service.voice.VoiceInteractionService;
+import android.service.voice.VoiceInteractionServiceInfo;
+
+import com.android.internal.app.AssistUtils;
+import com.android.settings.applications.defaultapps.DefaultAppInfo;
+import com.android.settings.applications.defaultapps.DefaultAppPreferenceController;
+
+import java.util.List;
+
+public class DefaultAssistPreferenceController extends DefaultAppPreferenceController {
+
+    private static final String KEY_DEFAULT_ASSIST = "default_assist";
+
+    private AssistUtils mAssistUtils;
+
+    public DefaultAssistPreferenceController(Context context) {
+        super(context);
+        mAssistUtils = new AssistUtils(context);
+    }
+
+    @Override
+    protected Intent getSettingIntent(DefaultAppInfo info) {
+        final ComponentName cn = mAssistUtils.getAssistComponentForUser(mUserId);
+        if (cn == null) {
+            return null;
+        }
+        final Intent probe = new Intent(VoiceInteractionService.SERVICE_INTERFACE)
+                .setPackage(cn.getPackageName());
+
+        final PackageManager pm = mPackageManager.getPackageManager();
+        final List<ResolveInfo> services = pm.queryIntentServices(probe, PackageManager
+                .GET_META_DATA);
+        if (services == null || services.isEmpty()) {
+            return null;
+        }
+        final ResolveInfo resolveInfo = services.get(0);
+        final VoiceInteractionServiceInfo voiceInfo =
+                new VoiceInteractionServiceInfo(pm, resolveInfo.serviceInfo);
+        if (!voiceInfo.getSupportsAssist()) {
+            return null;
+        }
+        final String activity = voiceInfo.getSettingsActivity();
+        return new Intent(Intent.ACTION_MAIN)
+                .setComponent(new ComponentName(cn.getPackageName(), activity));
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_DEFAULT_ASSIST;
+    }
+
+    @Override
+    protected DefaultAppInfo getDefaultAppInfo() {
+        final ComponentName cn = mAssistUtils.getAssistComponentForUser(mUserId);
+        if (cn == null) {
+            return null;
+        }
+        return new DefaultAppInfo(mUserId, cn);
+    }
+}
diff --git a/src/com/android/settings/applications/assist/DefaultVoiceInputPicker.java b/src/com/android/settings/applications/assist/DefaultVoiceInputPicker.java
new file mode 100644
index 0000000..7168aa5
--- /dev/null
+++ b/src/com/android/settings/applications/assist/DefaultVoiceInputPicker.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 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.applications.assist;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import com.android.internal.app.AssistUtils;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.applications.defaultapps.DefaultAppInfo;
+import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultVoiceInputPicker extends DefaultAppPickerFragment {
+
+    private VoiceInputHelper mHelper;
+    private AssistUtils mAssistUtils;
+    private String mAssistRestrict;
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.DEFAULT_VOICE_INPUT_PICKER;
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mAssistUtils = new AssistUtils(context);
+        mHelper = new VoiceInputHelper(context);
+        mHelper.buildUi();
+        final ComponentName assist = getCurrentAssist();
+        if (isCurrentAssistVoiceService(assist, getCurrentService(mHelper))) {
+            mAssistRestrict = assist.flattenToShortString();
+        }
+    }
+
+    @Override
+    protected List<VoiceInputDefaultAppInfo> getCandidates() {
+        final List<VoiceInputDefaultAppInfo> candidates = new ArrayList<>();
+        boolean hasEnabled = true;
+        for (VoiceInputHelper.InteractionInfo info : mHelper.mAvailableInteractionInfos) {
+            final boolean enabled = TextUtils.equals(info.key, mAssistRestrict);
+            hasEnabled |= enabled;
+            candidates.add(new VoiceInputDefaultAppInfo(mUserId, info, enabled));
+        }
+
+        final boolean assistIsService = !hasEnabled;
+        for (VoiceInputHelper.RecognizerInfo info : mHelper.mAvailableRecognizerInfos) {
+            final boolean enabled = !assistIsService;
+            candidates.add(new VoiceInputDefaultAppInfo(mUserId, info, enabled));
+        }
+        return candidates;
+    }
+
+    @Override
+    protected String getDefaultAppKey() {
+        final ComponentName currentService = getCurrentService(mHelper);
+        if (currentService == null) {
+            return null;
+        }
+        return currentService.flattenToShortString();
+    }
+
+    @Override
+    protected boolean setDefaultAppKey(String value) {
+        for (VoiceInputHelper.InteractionInfo info : mHelper.mAvailableInteractionInfos) {
+            if (TextUtils.equals(value, info.key)) {
+                Settings.Secure.putString(getContext().getContentResolver(),
+                        Settings.Secure.VOICE_INTERACTION_SERVICE, value);
+                Settings.Secure.putString(getContext().getContentResolver(),
+                        Settings.Secure.VOICE_RECOGNITION_SERVICE,
+                        new ComponentName(info.service.packageName,
+                                info.serviceInfo.getRecognitionService())
+                                .flattenToShortString());
+                return true;
+            }
+        }
+
+        for (VoiceInputHelper.RecognizerInfo info : mHelper.mAvailableRecognizerInfos) {
+            if (TextUtils.equals(value, info.key)) {
+                Settings.Secure.putString(getContext().getContentResolver(),
+                        Settings.Secure.VOICE_INTERACTION_SERVICE, "");
+                Settings.Secure.putString(getContext().getContentResolver(),
+                        Settings.Secure.VOICE_RECOGNITION_SERVICE, value);
+                return true;
+            }
+        }
+        return true;
+    }
+
+    public static ComponentName getCurrentService(VoiceInputHelper helper) {
+        if (helper.mCurrentVoiceInteraction != null) {
+            return helper.mCurrentVoiceInteraction;
+        } else if (helper.mCurrentRecognizer != null) {
+            return helper.mCurrentRecognizer;
+        } else {
+            return null;
+        }
+    }
+
+    private ComponentName getCurrentAssist() {
+        return mAssistUtils.getAssistComponentForUser(mUserId);
+    }
+
+    public static boolean isCurrentAssistVoiceService(ComponentName currentAssist,
+            ComponentName currentVoiceService) {
+        return currentAssist == null && currentVoiceService == null ||
+                currentAssist != null && currentAssist.equals(currentVoiceService);
+    }
+
+    public static class VoiceInputDefaultAppInfo extends DefaultAppInfo {
+
+        public VoiceInputHelper.BaseInfo mInfo;
+
+        public VoiceInputDefaultAppInfo(int userId, VoiceInputHelper.BaseInfo info,
+                boolean enabled) {
+            super(userId, info.componentName, null /* summary */, enabled);
+            mInfo = info;
+        }
+
+        @Override
+        public String getKey() {
+            return mInfo.key;
+        }
+
+        @Override
+        public CharSequence loadLabel(PackageManager pm) {
+            if (mInfo instanceof VoiceInputHelper.InteractionInfo) {
+                return mInfo.appLabel;
+            } else {
+                return mInfo.label;
+            }
+        }
+
+        public Intent getSettingIntent() {
+            if (mInfo.settings == null) {
+                return null;
+            }
+            return new Intent(Intent.ACTION_MAIN).setComponent(mInfo.settings);
+        }
+    }
+}
diff --git a/src/com/android/settings/applications/assist/DefaultVoiceInputPreferenceController.java b/src/com/android/settings/applications/assist/DefaultVoiceInputPreferenceController.java
new file mode 100644
index 0000000..918ec9c
--- /dev/null
+++ b/src/com/android/settings/applications/assist/DefaultVoiceInputPreferenceController.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 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.applications.assist;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+
+import com.android.internal.app.AssistUtils;
+import com.android.settings.applications.defaultapps.DefaultAppInfo;
+import com.android.settings.applications.defaultapps.DefaultAppPreferenceController;
+import com.android.settings.core.lifecycle.Lifecycle;
+import com.android.settings.core.lifecycle.LifecycleObserver;
+import com.android.settings.core.lifecycle.events.OnPause;
+import com.android.settings.core.lifecycle.events.OnResume;
+
+import java.util.List;
+
+public class DefaultVoiceInputPreferenceController extends DefaultAppPreferenceController
+        implements LifecycleObserver, OnResume, OnPause {
+
+    private static final String KEY_VOICE_INPUT = "voice_input_settings";
+
+    private VoiceInputHelper mHelper;
+    private AssistUtils mAssistUtils;
+    private PreferenceScreen mScreen;
+    private Preference mPreference;
+    private SettingObserver mSettingObserver;
+
+    public DefaultVoiceInputPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context);
+        mSettingObserver = new SettingObserver();
+        mAssistUtils = new AssistUtils(context);
+        mHelper = new VoiceInputHelper(context);
+        mHelper.buildUi();
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        // If current assist is also voice service, don't show voice preference.
+        final ComponentName currentVoiceService =
+                DefaultVoiceInputPicker.getCurrentService(mHelper);
+        final ComponentName currentAssist =
+                mAssistUtils.getAssistComponentForUser(mUserId);
+        return !DefaultVoiceInputPicker.isCurrentAssistVoiceService(
+                currentAssist, currentVoiceService);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_VOICE_INPUT;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        mScreen = screen;
+        mPreference = screen.findPreference(getPreferenceKey());
+        super.displayPreference(screen);
+    }
+
+    @Override
+    public void onResume() {
+        mSettingObserver.register(mContext.getContentResolver(), true);
+        updatePreference();
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(mPreference);
+        updatePreference();
+    }
+
+    @Override
+    public void onPause() {
+        mSettingObserver.register(mContext.getContentResolver(), false);
+    }
+
+    @Override
+    protected DefaultAppInfo getDefaultAppInfo() {
+        final String defaultKey = getDefaultAppKey();
+        if (defaultKey == null) {
+            return null;
+        }
+        for (VoiceInputHelper.InteractionInfo info : mHelper.mAvailableInteractionInfos) {
+            if (TextUtils.equals(defaultKey, info.key)) {
+                return new DefaultVoiceInputPicker.VoiceInputDefaultAppInfo(
+                        mUserId, info, true /* enabled */);
+            }
+        }
+
+        for (VoiceInputHelper.RecognizerInfo info : mHelper.mAvailableRecognizerInfos) {
+            if (TextUtils.equals(defaultKey, info.key)) {
+                return new DefaultVoiceInputPicker.VoiceInputDefaultAppInfo(
+                        mUserId, info, true /* enabled */);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    protected Intent getSettingIntent(DefaultAppInfo info) {
+        final DefaultAppInfo appInfo = getDefaultAppInfo();
+        if (appInfo == null
+                || !(appInfo instanceof DefaultVoiceInputPicker.VoiceInputDefaultAppInfo)) {
+            return null;
+        }
+        return ((DefaultVoiceInputPicker.VoiceInputDefaultAppInfo) appInfo).getSettingIntent();
+    }
+
+    private void updatePreference() {
+        if (mPreference == null) {
+            return;
+        }
+        mHelper.buildUi();
+        if (isAvailable()) {
+            if (mScreen.findPreference(getPreferenceKey()) == null) {
+                // add it if it's not on scree
+                mScreen.addPreference(mPreference);
+            }
+        } else {
+            mScreen.removePreference(mPreference);
+        }
+    }
+
+    private String getDefaultAppKey() {
+        final ComponentName currentService = DefaultVoiceInputPicker.getCurrentService(mHelper);
+        if (currentService == null) {
+            return null;
+        }
+        return currentService.flattenToShortString();
+    }
+
+    class SettingObserver extends AssistSettingObserver {
+        @Override
+        protected List<Uri> getSettingUris() {
+            return null;
+        }
+
+        @Override
+        public void onSettingChange() {
+            updatePreference();
+        }
+    }
+}
diff --git a/src/com/android/settings/applications/assist/GestureAssistPreferenceController.java b/src/com/android/settings/applications/assist/GestureAssistPreferenceController.java
new file mode 100644
index 0000000..bd85a03
--- /dev/null
+++ b/src/com/android/settings/applications/assist/GestureAssistPreferenceController.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 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.applications.assist;
+
+import android.content.Context;
+
+import com.android.settings.core.PreferenceController;
+import com.android.settings.gestures.AssistGestureFeatureProvider;
+import com.android.settings.overlay.FeatureFactory;
+
+public class GestureAssistPreferenceController extends PreferenceController {
+
+    private static final String KEY_ASSIST_GESTURE = "gesture_assist";
+
+    private AssistGestureFeatureProvider mFeatureProvider;
+
+    public GestureAssistPreferenceController(Context context) {
+        super(context);
+        mFeatureProvider = FeatureFactory.getFactory(context)
+                .getAssistGestureFeatureProvider();
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mFeatureProvider.isSupported(mContext);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_ASSIST_GESTURE;
+    }
+}
diff --git a/src/com/android/settings/applications/assist/ManageAssist.java b/src/com/android/settings/applications/assist/ManageAssist.java
new file mode 100644
index 0000000..b1ca5c6
--- /dev/null
+++ b/src/com/android/settings/applications/assist/ManageAssist.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 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.applications.assist;
+
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.core.lifecycle.Lifecycle;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Settings screen to manage everything about assist.
+ */
+public class ManageAssist extends DashboardFragment {
+
+    private static final String TAG = "ManageAssist";
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.manage_assist;
+    }
+
+    @Override
+    protected List<PreferenceController> getPreferenceControllers(Context context) {
+        final Lifecycle lifecycle = getLifecycle();
+        final List<PreferenceController> controllers = new ArrayList<>();
+        controllers.add(new DefaultAssistPreferenceController(context));
+        controllers.add(new GestureAssistPreferenceController(context));
+        controllers.add(new AssistContextPreferenceController(context, lifecycle));
+        controllers.add(new AssistScreenshotPreferenceController(context, lifecycle));
+        controllers.add(new AssistFlashScreenPreferenceController(context, lifecycle));
+        controllers.add(new DefaultVoiceInputPreferenceController(context, lifecycle));
+        return controllers;
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.APPLICATIONS_MANAGE_ASSIST;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        mFooterPreferenceMixin.createFooterPreference()
+                .setTitle(R.string.assist_footer);
+    }
+
+    public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider() {
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
+                        boolean enabled) {
+                    final SearchIndexableResource sir = new SearchIndexableResource(context);
+                    sir.xmlResId = R.xml.manage_assist;
+                    return Arrays.asList(sir);
+                }
+
+                @Override
+                public List<String> getNonIndexableKeys(Context context) {
+                    List<String> result = new ArrayList<>();
+                    new DefaultAssistPreferenceController(context).updateNonIndexableKeys(result);
+                    new GestureAssistPreferenceController(context).updateNonIndexableKeys(result);
+                    new AssistContextPreferenceController(context, null)
+                            .updateNonIndexableKeys(result);
+                    new AssistScreenshotPreferenceController(context, null)
+                            .updateNonIndexableKeys(result);
+                    new AssistFlashScreenPreferenceController(context, null)
+                            .updateNonIndexableKeys(result);
+                    new DefaultVoiceInputPreferenceController(context, null)
+                            .updateNonIndexableKeys(result);
+                    return result;
+                }
+            };
+}
diff --git a/src/com/android/settings/voice/VoiceInputHelper.java b/src/com/android/settings/applications/assist/VoiceInputHelper.java
similarity index 92%
rename from src/com/android/settings/voice/VoiceInputHelper.java
rename to src/com/android/settings/applications/assist/VoiceInputHelper.java
index ac73d84..58c0d49 100644
--- a/src/com/android/settings/voice/VoiceInputHelper.java
+++ b/src/com/android/settings/applications/assist/VoiceInputHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.settings.voice;
+package com.android.settings.applications.assist;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -71,7 +71,7 @@
 
         @Override
         public int compareTo(Object another) {
-            return labelStr.compareTo(((BaseInfo)another).labelStr);
+            return labelStr.compareTo(((BaseInfo) another).labelStr);
         }
     }
 
@@ -100,15 +100,11 @@
         mContext = context;
 
         mAvailableVoiceInteractions = mContext.getPackageManager().queryIntentServices(
-                        new Intent(VoiceInteractionService.SERVICE_INTERFACE),
-                        PackageManager.GET_META_DATA);
+                new Intent(VoiceInteractionService.SERVICE_INTERFACE),
+                PackageManager.GET_META_DATA);
         mAvailableRecognition = mContext.getPackageManager().queryIntentServices(
-                        new Intent(RecognitionService.SERVICE_INTERFACE),
-                        PackageManager.GET_META_DATA);
-    }
-
-    public boolean hasItems() {
-        return mAvailableVoiceInteractions.size() > 0 || mAvailableRecognition.size() > 0;
+                new Intent(RecognitionService.SERVICE_INTERFACE),
+                PackageManager.GET_META_DATA);
     }
 
     public void buildUi() {
@@ -178,7 +174,7 @@
                 AttributeSet attrs = Xml.asAttributeSet(parser);
 
                 int type;
-                while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                         && type != XmlPullParser.START_TAG) {
                 }
 
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAppInfo.java b/src/com/android/settings/applications/defaultapps/DefaultAppInfo.java
index ea3a27a..96512f5 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAppInfo.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAppInfo.java
@@ -37,13 +37,23 @@
     public final String summary;
     // Description for why this item is disabled, if null, the item is enabled.
     public final String disabledDescription;
+    public final boolean enabled;
+
+    public DefaultAppInfo(int uid, ComponentName cn) {
+        this(uid, cn, null /* summary */);
+    }
 
     public DefaultAppInfo(int uid, ComponentName cn, String summary) {
+        this(uid, cn, summary, true /* enabled */);
+    }
+
+    public DefaultAppInfo(int uid, ComponentName cn, String summary, boolean enabled) {
         packageItemInfo = null;
         userId = uid;
         componentName = cn;
         this.summary = summary;
         this.disabledDescription = null;
+        this.enabled = enabled;
     }
 
     public DefaultAppInfo(PackageItemInfo info, String description) {
@@ -52,6 +62,7 @@
         componentName = null;
         summary = null;
         this.disabledDescription = description;
+        enabled = true;
     }
 
     public DefaultAppInfo(PackageItemInfo info) {
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragment.java b/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragment.java
index 358b5f4..e4bcf06 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragment.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragment.java
@@ -97,7 +97,7 @@
     @VisibleForTesting
     public void updateCandidates() {
         mCandidates.clear();
-        final List<DefaultAppInfo> candidateList = getCandidates();
+        final List<? extends DefaultAppInfo> candidateList = getCandidates();
         if (candidateList != null) {
             for (DefaultAppInfo info : candidateList) {
                 mCandidates.put(info.getKey(), info);
@@ -134,6 +134,7 @@
                 pref.setEnabled(false);
                 pref.setSummary(app.getValue().disabledDescription);
             }
+            pref.setEnabled(info.enabled);
             pref.setOnClickListener(this);
             screen.addPreference(pref);
         }
@@ -200,14 +201,15 @@
         return null;
     }
 
-    protected abstract List<DefaultAppInfo> getCandidates();
+    protected abstract List<? extends DefaultAppInfo> getCandidates();
 
     protected abstract String getDefaultAppKey();
 
     protected abstract boolean setDefaultAppKey(String key);
 
     // Called after the user tries to select an item.
-    protected void onSelectionPerformed(boolean success) {}
+    protected void onSelectionPerformed(boolean success) {
+    }
 
     protected String getConfirmationMessage(DefaultAppInfo appInfo) {
         return null;
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAutoFillPicker.java b/src/com/android/settings/applications/defaultapps/DefaultAutoFillPicker.java
index 5b9fed4..b8190cd 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAutoFillPicker.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAutoFillPicker.java
@@ -54,7 +54,7 @@
                 .queryIntentServices(AUTO_FILL_PROBE, PackageManager.GET_META_DATA);
         for (ResolveInfo info : resolveInfos) {
             candidates.add(new DefaultAppInfo(mUserId, new ComponentName(
-                    info.serviceInfo.packageName, info.serviceInfo.name), null /* summary */));
+                    info.serviceInfo.packageName, info.serviceInfo.name)));
         }
         return candidates;
     }
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAutoFillPreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultAutoFillPreferenceController.java
index 35e4ade..b7f3b77 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAutoFillPreferenceController.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAutoFillPreferenceController.java
@@ -55,7 +55,7 @@
                 DefaultAutoFillPicker.SETTING);
         if (!TextUtils.isEmpty(flattenComponent)) {
             DefaultAppInfo appInfo = new DefaultAppInfo(
-                    mUserId, ComponentName.unflattenFromString(flattenComponent), null /*summary*/);
+                    mUserId, ComponentName.unflattenFromString(flattenComponent));
             return appInfo;
         }
         return null;
diff --git a/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceController.java
index 055c23b..7366d18 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceController.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceController.java
@@ -75,7 +75,7 @@
         final ArrayList<ResolveInfo> homeActivities = new ArrayList<>();
         final ComponentName currentDefaultHome = mPackageManager.getHomeActivities(homeActivities);
 
-        return new DefaultAppInfo(mUserId, currentDefaultHome, null /* summary */);
+        return new DefaultAppInfo(mUserId, currentDefaultHome);
     }
 
     private String getOnlyAppLabel() {
diff --git a/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPicker.java b/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPicker.java
index af67917..06c5401 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPicker.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPicker.java
@@ -76,7 +76,7 @@
             }
 
             candidates.add(new DefaultAppInfo(
-                    mUserId, new ComponentName(info.packageName, info.name), null /* summary */));
+                    mUserId, new ComponentName(info.packageName, info.name)));
         }
         return candidates;
     }
diff --git a/src/com/android/settings/applications/defaultapps/DefaultSmsPreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultSmsPreferenceController.java
index 90b9d83..81a3af0 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultSmsPreferenceController.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultSmsPreferenceController.java
@@ -47,7 +47,7 @@
     protected DefaultAppInfo getDefaultAppInfo() {
         final ComponentName app = SmsApplication.getDefaultSmsApplication(mContext, true);
         if (app != null) {
-            return new DefaultAppInfo(mUserId, app, null /* summary */);
+            return new DefaultAppInfo(mUserId, app);
         }
         return null;
     }
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 92d6ace..ad8bdaf 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -54,7 +54,7 @@
 import com.android.settings.applications.DrawOverlayDetails;
 import com.android.settings.applications.InstalledAppDetails;
 import com.android.settings.applications.ManageApplications;
-import com.android.settings.applications.ManageAssist;
+import com.android.settings.applications.assist.ManageAssist;
 import com.android.settings.applications.ManageDomainUrls;
 import com.android.settings.applications.NotificationApps;
 import com.android.settings.applications.PictureInPictureSettings;
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index 700ab72..6688b99 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -37,6 +37,7 @@
 import com.android.settings.applications.AdvancedAppSettings;
 import com.android.settings.applications.AppAndNotificationDashboardFragment;
 import com.android.settings.applications.SpecialAccessSettings;
+import com.android.settings.applications.assist.ManageAssist;
 import com.android.settings.backup.BackupSettingsFragment;
 import com.android.settings.bluetooth.BluetoothSettings;
 import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
@@ -133,6 +134,7 @@
         addIndex(BatterySaverSettings.class,
                 R.xml.battery_saver_settings, R.drawable.ic_settings_battery);
         addIndex(AdvancedAppSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_applications);
+        addIndex(ManageAssist.class, NO_DATA_RES_ID, R.drawable.ic_settings_applications);
         addIndex(SpecialAccessSettings.class,
                 R.xml.special_access, R.drawable.ic_settings_applications);
         addIndex(UserSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_multiuser);
diff --git a/src/com/android/settings/voice/VoiceInputListPreference.java b/src/com/android/settings/voice/VoiceInputListPreference.java
deleted file mode 100644
index ddb56d1..0000000
--- a/src/com/android/settings/voice/VoiceInputListPreference.java
+++ /dev/null
@@ -1,158 +0,0 @@
-package com.android.settings.voice;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.provider.Settings;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ListAdapter;
-
-import com.android.settings.AppListPreferenceWithSettings;
-import com.android.settings.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class VoiceInputListPreference extends AppListPreferenceWithSettings {
-
-    private VoiceInputHelper mHelper;
-
-    // The assist component name to restrict available voice inputs.
-    private ComponentName mAssistRestrict;
-
-    private final List<Integer> mAvailableIndexes = new ArrayList<>();
-
-    public VoiceInputListPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setDialogTitle(R.string.choose_voice_input_title);
-    }
-
-    @Override
-    protected ListAdapter createListAdapter() {
-        return new CustomAdapter(getContext(), getEntries());
-    }
-
-    @Override
-    protected boolean persistString(String value) {
-        for (int i = 0; i < mHelper.mAvailableInteractionInfos.size(); ++i) {
-            VoiceInputHelper.InteractionInfo info = mHelper.mAvailableInteractionInfos.get(i);
-            if (info.key.equals(value)) {
-                Settings.Secure.putString(getContext().getContentResolver(),
-                        Settings.Secure.VOICE_INTERACTION_SERVICE, value);
-                Settings.Secure.putString(getContext().getContentResolver(),
-                        Settings.Secure.VOICE_RECOGNITION_SERVICE,
-                        new ComponentName(info.service.packageName,
-                                info.serviceInfo.getRecognitionService())
-                                .flattenToShortString());
-                setSummary(getEntry());
-                setSettingsComponent(info.settings);
-                return true;
-            }
-        }
-
-        for (int i = 0; i < mHelper.mAvailableRecognizerInfos.size(); ++i) {
-            VoiceInputHelper.RecognizerInfo info = mHelper.mAvailableRecognizerInfos.get(i);
-            if (info.key.equals(value)) {
-                Settings.Secure.putString(getContext().getContentResolver(),
-                        Settings.Secure.VOICE_INTERACTION_SERVICE, "");
-                Settings.Secure.putString(getContext().getContentResolver(),
-                        Settings.Secure.VOICE_RECOGNITION_SERVICE, value);
-                setSummary(getEntry());
-                setSettingsComponent(info.settings);
-               return true;
-            }
-        }
-
-        setSettingsComponent(null);
-        return true;
-    }
-
-    @Override
-    public void setPackageNames(CharSequence[] packageNames, CharSequence defaultPackageName) {
-        // Skip since all entries are created from |mHelper|.
-    }
-
-    public void setAssistRestrict(ComponentName assistRestrict) {
-        mAssistRestrict = assistRestrict;
-    }
-
-    public void refreshVoiceInputs() {
-        mHelper = new VoiceInputHelper(getContext());
-        mHelper.buildUi();
-
-        final String assistKey =
-                mAssistRestrict == null ? "" : mAssistRestrict.flattenToShortString();
-
-        mAvailableIndexes.clear();
-        List<CharSequence> entries = new ArrayList<>();
-        List<CharSequence> values = new ArrayList<>();
-        for (int i = 0; i < mHelper.mAvailableInteractionInfos.size(); ++i) {
-            VoiceInputHelper.InteractionInfo info = mHelper.mAvailableInteractionInfos.get(i);
-            entries.add(info.appLabel);
-            values.add(info.key);
-
-            if (info.key.contentEquals(assistKey)) {
-                mAvailableIndexes.add(i);
-            }
-        }
-
-        final boolean assitIsService = !mAvailableIndexes.isEmpty();
-        final int serviceCount = entries.size();
-
-        for (int i = 0; i < mHelper.mAvailableRecognizerInfos.size(); ++i) {
-            VoiceInputHelper.RecognizerInfo info = mHelper.mAvailableRecognizerInfos.get(i);
-            entries.add(info.label);
-            values.add(info.key);
-            if (!assitIsService) {
-                mAvailableIndexes.add(serviceCount + i);
-            }
-        }
-        setEntries(entries.toArray(new CharSequence[entries.size()]));
-        setEntryValues(values.toArray(new CharSequence[values.size()]));
-
-        if (mHelper.mCurrentVoiceInteraction != null) {
-            setValue(mHelper.mCurrentVoiceInteraction.flattenToShortString());
-        } else if (mHelper.mCurrentRecognizer != null) {
-            setValue(mHelper.mCurrentRecognizer.flattenToShortString());
-        } else {
-            setValue(null);
-        }
-    }
-
-    public ComponentName getCurrentService() {
-        if (mHelper.mCurrentVoiceInteraction != null) {
-            return mHelper.mCurrentVoiceInteraction;
-        } else if (mHelper.mCurrentRecognizer != null) {
-            return mHelper.mCurrentRecognizer;
-        } else {
-            return null;
-        }
-    }
-
-    private class CustomAdapter extends ArrayAdapter<CharSequence> {
-
-        public CustomAdapter(Context context, CharSequence[] objects) {
-            super(context, com.android.internal.R.layout.select_dialog_singlechoice_material,
-                    android.R.id.text1, objects);
-        }
-
-        @Override
-        public boolean areAllItemsEnabled() {
-            return false;
-        }
-
-        @Override
-        public boolean isEnabled(int position) {
-            return mAvailableIndexes.contains(position);
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            View view = super.getView(position, convertView, parent);
-            view.setEnabled(isEnabled(position));
-            return view;
-        }
-    }
-}