diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 9c8a67769f5..c5a98578bdd 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -614,7 +614,7 @@ + android:value="com.android.settings.applications.assist.ManageAssist" /> Assist app - - None - - - Choose Assist app - Make %s your assistant? diff --git a/res/xml/app_default_settings.xml b/res/xml/app_default_settings.xml index 359d4f7c997..a382bc9c8da 100644 --- a/res/xml/app_default_settings.xml +++ b/res/xml/app_default_settings.xml @@ -23,7 +23,7 @@ - + android:summary="@string/app_list_preference_none" + android:fragment="com.android.settings.applications.assist.DefaultAssistPicker"/> @@ -45,8 +45,9 @@ android:title="@string/assist_flash_title" android:summary="@string/assist_flash_summary"/> - + android:title="@string/voice_input_settings_title" + android:fragment="com.android.settings.applications.assist.DefaultVoiceInputPicker"/> diff --git a/src/com/android/settings/AppListPreference.java b/src/com/android/settings/AppListPreference.java deleted file mode 100644 index 85854548cb2..00000000000 --- 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 { - 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 applicationNames = new ArrayList<>(entryCount); - List validatedPackageNames = new ArrayList<>(entryCount); - List 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 applicationNames = new ArrayList<>(entryCount); - List validatedComponentNames = new ArrayList<>(entryCount); - List 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 CREATOR = new Creator() { - @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 a499a7ba926..00000000000 --- 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 8dd4d70204a..00000000000 --- 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 00000000000..05bc8d60d52 --- /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 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 00000000000..e44e70ff78b --- /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 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 00000000000..21dfe19b95c --- /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 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 00000000000..5d3d4a75bb5 --- /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 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 settingUri = getSettingUris(); + if (ASSIST_URI.equals(uri) || (settingUri != null && settingUri.contains(uri))) { + shouldUpdatePreference = true; + } + if (shouldUpdatePreference) { + onSettingChange(); + } + } + + protected abstract List 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 0bd729e7b5c..703665a86ed 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 { - - private static final String TAG = DefaultAssistPreference.class.getSimpleName(); +public class DefaultAssistPicker extends DefaultAppPickerFragment { + 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 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 getCandidates() { + mAvailableAssistants.clear(); + addAssistServices(); + addAssistActivities(); + + final List packages = new ArrayList<>(); + final List 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 @@ public class DefaultAssistPreference extends AppListPreferenceWithSettings { return true; } + public ComponentName getCurrentAssist() { + return mAssistUtils.getAssistComponentForUser(mUserId); + } + + private void addAssistServices() { + final PackageManager pm = mPm.getPackageManager(); + final List 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 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 @@ public class DefaultAssistPreference extends AppListPreferenceWithSettings { 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 @@ public class DefaultAssistPreference extends AppListPreferenceWithSettings { 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 @@ public class DefaultAssistPreference extends AppListPreferenceWithSettings { 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 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 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 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 00000000000..df345011901 --- /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 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 00000000000..7168aa5cebe --- /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 getCandidates() { + final List 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 00000000000..918ec9c7864 --- /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 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 00000000000..bd85a030495 --- /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 00000000000..b1ca5c659c7 --- /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 getPreferenceControllers(Context context) { + final Lifecycle lifecycle = getLifecycle(); + final List 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 getXmlResourcesToIndex(Context context, + boolean enabled) { + final SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.manage_assist; + return Arrays.asList(sir); + } + + @Override + public List getNonIndexableKeys(Context context) { + List 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 93% rename from src/com/android/settings/voice/VoiceInputHelper.java rename to src/com/android/settings/applications/assist/VoiceInputHelper.java index ac73d843ebc..58c0d49a0a0 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 @@ public final class VoiceInputHelper { @Override public int compareTo(Object another) { - return labelStr.compareTo(((BaseInfo)another).labelStr); + return labelStr.compareTo(((BaseInfo) another).labelStr); } } @@ -100,15 +100,11 @@ public final class VoiceInputHelper { 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 @@ public final class VoiceInputHelper { 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 ea3a27af5c8..96512f5d54e 100644 --- a/src/com/android/settings/applications/defaultapps/DefaultAppInfo.java +++ b/src/com/android/settings/applications/defaultapps/DefaultAppInfo.java @@ -37,13 +37,23 @@ public class DefaultAppInfo { 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 @@ public class DefaultAppInfo { 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 358b5f4d1c7..e4bcf067e72 100644 --- a/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragment.java +++ b/src/com/android/settings/applications/defaultapps/DefaultAppPickerFragment.java @@ -97,7 +97,7 @@ public abstract class DefaultAppPickerFragment extends InstrumentedPreferenceFra @VisibleForTesting public void updateCandidates() { mCandidates.clear(); - final List candidateList = getCandidates(); + final List candidateList = getCandidates(); if (candidateList != null) { for (DefaultAppInfo info : candidateList) { mCandidates.put(info.getKey(), info); @@ -134,6 +134,7 @@ public abstract class DefaultAppPickerFragment extends InstrumentedPreferenceFra pref.setEnabled(false); pref.setSummary(app.getValue().disabledDescription); } + pref.setEnabled(info.enabled); pref.setOnClickListener(this); screen.addPreference(pref); } @@ -200,14 +201,15 @@ public abstract class DefaultAppPickerFragment extends InstrumentedPreferenceFra return null; } - protected abstract List getCandidates(); + protected abstract List 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 5b9fed4ce57..b8190cdbfa2 100644 --- a/src/com/android/settings/applications/defaultapps/DefaultAutoFillPicker.java +++ b/src/com/android/settings/applications/defaultapps/DefaultAutoFillPicker.java @@ -54,7 +54,7 @@ public class DefaultAutoFillPicker extends DefaultAppPickerFragment { .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 35e4ade09c4..b7f3b7711d7 100644 --- a/src/com/android/settings/applications/defaultapps/DefaultAutoFillPreferenceController.java +++ b/src/com/android/settings/applications/defaultapps/DefaultAutoFillPreferenceController.java @@ -55,7 +55,7 @@ public class DefaultAutoFillPreferenceController extends DefaultAppPreferenceCon 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 055c23bab83..7366d181b2d 100644 --- a/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceController.java +++ b/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceController.java @@ -75,7 +75,7 @@ public class DefaultHomePreferenceController extends DefaultAppPreferenceControl final ArrayList 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 af67917283a..06c54011fe1 100644 --- a/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPicker.java +++ b/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPicker.java @@ -76,7 +76,7 @@ public class DefaultNotificationAssistantPicker extends DefaultAppPickerFragment } 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 90b9d83b09c..81a3af0dd40 100644 --- a/src/com/android/settings/applications/defaultapps/DefaultSmsPreferenceController.java +++ b/src/com/android/settings/applications/defaultapps/DefaultSmsPreferenceController.java @@ -47,7 +47,7 @@ public class DefaultSmsPreferenceController extends DefaultAppPreferenceControll 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 92d6ace2b29..ad8bdafa0f1 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.AppAndNotificationDashboardFragment; 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 700ab72a5c8..6688b9978c8 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.accounts.UserAndAccountDashboardFragment; 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 @@ public final class SearchIndexableResources { 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 ddb56d1f792..00000000000 --- 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 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 entries = new ArrayList<>(); - List 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 { - - 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; - } - } -} diff --git a/tests/robotests/assets/grandfather_not_implementing_indexable b/tests/robotests/assets/grandfather_not_implementing_indexable index 3e4a9a31bd5..e57f9d55327 100644 --- a/tests/robotests/assets/grandfather_not_implementing_indexable +++ b/tests/robotests/assets/grandfather_not_implementing_indexable @@ -65,7 +65,7 @@ com.android.settings.fuelgauge.BatteryHistoryDetail com.android.settings.fuelgauge.PowerUsageSummary com.android.settings.applications.RunningServices com.android.settings.wifi.p2p.WifiP2pSettings -com.android.settings.applications.ManageAssist +com.android.settings.applications.assistant.ManageAssist com.android.settings.applications.ConfirmConvertToFbe com.android.settings.deviceinfo.PublicVolumeSettings com.android.settings.applications.InstalledAppDetails diff --git a/tests/robotests/src/com/android/settings/SecuritySettingsTest.java b/tests/robotests/src/com/android/settings/SecuritySettingsTest.java index 51c0d9bc01e..03a8f94df93 100644 --- a/tests/robotests/src/com/android/settings/SecuritySettingsTest.java +++ b/tests/robotests/src/com/android/settings/SecuritySettingsTest.java @@ -16,7 +16,6 @@ package com.android.settings; -import android.content.ContentResolver; import android.content.Context; import android.content.IContentProvider; import android.content.pm.PackageManager; @@ -26,6 +25,7 @@ import android.provider.Settings; import com.android.settings.dashboard.SummaryLoader; import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.shadow.ShadowSecureSettings; import com.android.settingslib.drawer.DashboardCategory; import com.android.settingslib.drawer.Tile; import com.android.settingslib.drawer.TileUtils; @@ -40,7 +40,6 @@ import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; -import java.util.HashMap; import java.util.Map; import static com.google.common.truth.Truth.assertThat; @@ -67,24 +66,6 @@ public class SecuritySettingsTest { private SecuritySettings.SummaryProvider mSummaryProvider; - @Implements(Settings.Secure.class) - public static class ShadowSecureSettings { - - private static final Map mValueMap = new HashMap<>(); - - @Implementation - public static boolean putInt(ContentResolver resolver, String name, int value) { - mValueMap.put(name, value); - return true; - } - - @Implementation - public static int getInt(ContentResolver resolver, String name, int defaultValue) { - Integer value = (Integer) mValueMap.get(name); - return value == null ? defaultValue : value; - } - } - @Implements(com.android.settingslib.drawer.TileUtils.class) public static class ShadowTileUtils { @Implementation diff --git a/tests/robotests/src/com/android/settings/applications/assist/AssistContextPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/assist/AssistContextPreferenceControllerTest.java new file mode 100644 index 00000000000..da8473f1e33 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/assist/AssistContextPreferenceControllerTest.java @@ -0,0 +1,95 @@ +/* + * 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.content.Context; +import android.provider.Settings; +import android.support.v7.preference.PreferenceScreen; +import android.support.v7.preference.TwoStatePreference; + +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.core.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class AssistContextPreferenceControllerTest { + + @Mock + private PreferenceScreen mScreen; + @Mock + private TwoStatePreference mPreference; + @Mock + private AssistContextPreferenceController.SettingObserver mObserver; + private Context mContext; + private AssistContextPreferenceController mController; + private Lifecycle mLifecycle; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mScreen.findPreference(anyString())).thenReturn(mPreference); + mLifecycle = new Lifecycle(); + mContext = RuntimeEnvironment.application; + mController = new AssistContextPreferenceController(mContext, mLifecycle); + ReflectionHelpers.setField(mController, "mSettingObserver", mObserver); + } + + @Test + public void isAvailable_hasAssistant_shouldReturnTrue() { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ASSISTANT, "com.android.settings/assist"); + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void isAvailable_hasNoAssistant_shouldReturnFalse() { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ASSISTANT, ""); + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + public void onResume_shouldUpdatePreference() { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ASSISTANT, "com.android.settings/assist"); + mController.displayPreference(mScreen); + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1); + + mLifecycle.onResume(); + verify(mObserver).register(any(ContentResolver.class), eq(true)); + verify(mPreference).setChecked(true); + } +} diff --git a/tests/robotests/src/com/android/settings/applications/assist/AssistFlashScreenPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/assist/AssistFlashScreenPreferenceControllerTest.java new file mode 100644 index 00000000000..0b2378c4dac --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/assist/AssistFlashScreenPreferenceControllerTest.java @@ -0,0 +1,125 @@ +/* + * 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.ContentResolver; +import android.content.Context; +import android.provider.Settings; +import android.support.v7.preference.PreferenceScreen; +import android.support.v7.preference.TwoStatePreference; + +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.core.lifecycle.Lifecycle; +import com.android.settings.testutils.shadow.ShadowSecureSettings; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class AssistFlashScreenPreferenceControllerTest { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mMockContext; + @Mock + private PreferenceScreen mScreen; + @Mock + private TwoStatePreference mPreference; + @Mock + private AssistFlashScreenPreferenceController.SettingObserver mObserver; + private Context mContext; + private AssistFlashScreenPreferenceController mController; + private Lifecycle mLifecycle; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mScreen.findPreference(anyString())).thenReturn(mPreference); + mLifecycle = new Lifecycle(); + mContext = RuntimeEnvironment.application; + mController = spy(new AssistFlashScreenPreferenceController(mContext, mLifecycle)); + mLifecycle.addObserver(mController); + ReflectionHelpers.setField(mController, "mSettingObserver", mObserver); + } + + @Test + @Config(shadows = {ShadowSecureSettings.class}) + public void isAvailable_hasAssistantAndAllowDisclosure_shouldReturnTrue() { + ReflectionHelpers.setField(mController, "mContext", mMockContext); + ShadowSecureSettings.putString(null, Settings.Secure.ASSISTANT, + "com.android.settings/assist"); + doReturn(true).when(mController).allowDisablingAssistDisclosure(); + + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + @Config(shadows = {ShadowSecureSettings.class}) + public void isAvailable_hasAssistantAndDisallowDisclosure_shouldReturnTrue() { + ReflectionHelpers.setField(mController, "mContext", mMockContext); + ShadowSecureSettings.putString(null, Settings.Secure.ASSISTANT, + "com.android.settings/assist"); + doReturn(false).when(mController).allowDisablingAssistDisclosure(); + + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + public void isAvailable_hasNoAssistant_shouldReturnFalse() { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ASSISTANT, ""); + + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + @Config(shadows = {ShadowSecureSettings.class}) + public void onResume_shouldUpdatePreference() { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ASSISTANT, "com.android.settings/assist"); + doReturn(true).when(mController).isAvailable(); + doReturn(true).when(mController).isPreInstalledAssistant(any(ComponentName.class)); + doReturn(true).when(mController).willShowFlash(any(ComponentName.class)); + + mController.displayPreference(mScreen); + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ASSIST_DISCLOSURE_ENABLED, 1); + + mLifecycle.onResume(); + + verify(mObserver).register(any(ContentResolver.class), eq(true)); + verify(mPreference).setChecked(true); + } +} diff --git a/tests/robotests/src/com/android/settings/applications/assist/DefaultAssistPickerTest.java b/tests/robotests/src/com/android/settings/applications/assist/DefaultAssistPickerTest.java new file mode 100644 index 00000000000..544526abead --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/assist/DefaultAssistPickerTest.java @@ -0,0 +1,99 @@ +/* + * 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.provider.Settings; + +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +import java.util.ArrayList; +import java.util.List; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class DefaultAssistPickerTest { + + private static final ComponentName TEST_ASSIST = + new ComponentName("com.android.settings", "assist"); + + private Context mContext; + private DefaultAssistPicker mPicker; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mPicker = spy(new DefaultAssistPicker()); + mPicker.onAttach(mContext); + doReturn(mContext).when(mPicker).getContext(); + } + + @Test + public void setDefaultAppKey_shouldUpdateDefaultAssist() { + final List assistants = new ArrayList<>(); + assistants.add(new DefaultAssistPicker.Info(TEST_ASSIST)); + ReflectionHelpers.setField(mPicker, "mAvailableAssistants", assistants); + mPicker.setDefaultAppKey(TEST_ASSIST.flattenToString()); + + assertThat(Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.ASSISTANT)) + .isEqualTo(TEST_ASSIST.flattenToString()); + assertThat(mPicker.getDefaultAppKey()) + .isEqualTo(TEST_ASSIST.flattenToString()); + } + + @Test + public void setDefaultAppKey_noAvaialbleAssit_shouldClearDefaultAssist() { + final List assistants = new ArrayList<>(); + ReflectionHelpers.setField(mPicker, "mAvailableAssistants", assistants); + mPicker.setDefaultAppKey(TEST_ASSIST.flattenToString()); + + assertThat(Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.ASSISTANT)) + .isEmpty(); + assertThat(mPicker.getDefaultAppKey()) + .isNull(); + } + + @Test + public void setDefaultAppKeyToNull_shouldClearDefaultAssist() { + final List assistants = new ArrayList<>(); + assistants.add(new DefaultAssistPicker.Info(TEST_ASSIST)); + ReflectionHelpers.setField(mPicker, "mAvailableAssistants", assistants); + mPicker.setDefaultAppKey(null); + + assertThat(Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.ASSISTANT)) + .isEmpty(); + assertThat(mPicker.getDefaultAppKey()) + .isNull(); + } +} diff --git a/tests/robotests/src/com/android/settings/applications/assist/DefaultAssistPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/assist/DefaultAssistPreferenceControllerTest.java new file mode 100644 index 00000000000..1182762ae3a --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/assist/DefaultAssistPreferenceControllerTest.java @@ -0,0 +1,64 @@ +/* + * 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.Settings; + +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.applications.defaultapps.DefaultAppInfo; +import com.android.settings.testutils.shadow.ShadowSecureSettings; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +import static com.google.common.truth.Truth.assertThat; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class DefaultAssistPreferenceControllerTest { + + @Mock + private Context mContext; + private DefaultAssistPreferenceController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mController = new DefaultAssistPreferenceController(mContext); + } + + @Test + public void isAlwaysAvailable() { + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + @Config(shadows = {ShadowSecureSettings.class}) + public void getDefaultAppInfo_hasDefaultAssist_shouldReturnKey() { + final String flattenKey = "com.android.settings/assist"; + ShadowSecureSettings.putString(null, Settings.Secure.ASSISTANT, flattenKey); + DefaultAppInfo appInfo = mController.getDefaultAppInfo(); + + assertThat(appInfo.getKey()).isEqualTo(flattenKey); + } +} diff --git a/tests/robotests/src/com/android/settings/applications/assist/GestureAssistPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/assist/GestureAssistPreferenceControllerTest.java new file mode 100644 index 00000000000..624a01ff28d --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/assist/GestureAssistPreferenceControllerTest.java @@ -0,0 +1,64 @@ +/* + * 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.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.testutils.FakeFeatureFactory; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class GestureAssistPreferenceControllerTest { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mContext; + private FakeFeatureFactory mFeatureFactory; + private GestureAssistPreferenceController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + FakeFeatureFactory.setupForTest(mContext); + mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); + mController = new GestureAssistPreferenceController(mContext); + } + + @Test + public void isAvailable_shouldReturnFeatureProviderValue() { + when(mFeatureFactory.assistGestureFeatureProvider.isSupported(any(Context.class))) + .thenReturn(true); + assertThat(mController.isAvailable()).isTrue(); + + when(mFeatureFactory.assistGestureFeatureProvider.isSupported(any(Context.class))) + .thenReturn(false); + assertThat(mController.isAvailable()).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/applications/assist/ManageAssistTest.java b/tests/robotests/src/com/android/settings/applications/assist/ManageAssistTest.java new file mode 100644 index 00000000000..91d4b518cc9 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/assist/ManageAssistTest.java @@ -0,0 +1,59 @@ +/* + * 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 com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +import static com.google.common.truth.Truth.assertThat; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class ManageAssistTest { + + private ManageAssist mSettings; + + @Before + public void setUp() { + mSettings = new ManageAssist(); + } + + @Test + public void testGetMetricsCategory() { + assertThat(mSettings.getMetricsCategory()) + .isEqualTo(MetricsProto.MetricsEvent.APPLICATIONS_MANAGE_ASSIST); + } + + @Test + public void testGetCategoryKey() { + assertThat(mSettings.getCategoryKey()).isNull(); + } + + @Test + public void testGetPreferenceScreenResId() { + assertThat(mSettings.getPreferenceScreenResId()) + .isEqualTo(R.xml.manage_assist); + } + +} diff --git a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAppInfoTest.java b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAppInfoTest.java index 6c31927dbfe..57668c80fc4 100644 --- a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAppInfoTest.java +++ b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAppInfoTest.java @@ -85,7 +85,7 @@ public class DefaultAppInfoTest { public void initInfoWithComponent_shouldLoadInfo() { when(mComponentName.getPackageName()).thenReturn("com.android.settings"); - mInfo = new DefaultAppInfo(0 /* uid */, mComponentName, null /*summary */); + mInfo = new DefaultAppInfo(0 /* uid */, mComponentName); mInfo.getKey(); verify(mComponentName).flattenToString(); diff --git a/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java b/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java index 899952b2fe4..3c820ec241c 100644 --- a/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java +++ b/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java @@ -36,7 +36,7 @@ public class SearchIndexProviderCodeInspector extends CodeInspector { private static final String TAG = "SearchCodeInspector"; private static final String NOT_IMPLEMENTING_INDEXABLE_ERROR = - "SettingsPreferenceFragment should implement Indexable, but these are not:\n"; + "SettingsPreferenceFragment should implement Indexable, but these do not:\n"; private static final String NOT_CONTAINING_PROVIDER_OBJECT_ERROR = "Indexable should have public field " + Index.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + " but these are not:\n"; diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java new file mode 100644 index 00000000000..5dd54c0087b --- /dev/null +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSecureSettings.java @@ -0,0 +1,60 @@ +/* + * 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.testutils.shadow; + +import android.content.ContentResolver; +import android.provider.Settings; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +import java.util.HashMap; +import java.util.Map; + +@Implements(Settings.Secure.class) +public class ShadowSecureSettings { + + private static final Map mValueMap = new HashMap<>(); + + @Implementation + public static boolean putInt(ContentResolver resolver, String name, int value) { + mValueMap.put(name, value); + return true; + } + + @Implementation + public static boolean putString(ContentResolver resolver, String name, String value) { + mValueMap.put(name, value); + return true; + } + + @Implementation + public static String getString(ContentResolver resolver, String name) { + return (String) mValueMap.get(name); + } + + @Implementation + public static String getStringForUser(ContentResolver resolver, String name, int userHandle) { + return getString(resolver, name); + } + + @Implementation + public static int getInt(ContentResolver resolver, String name, int defaultValue) { + Integer value = (Integer) mValueMap.get(name); + return value == null ? defaultValue : value; + } +}