Deprecate AppListPreference and AppListPrefWithSettings

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

Bug: 35203386
Test: make RunSettingsRoboTests

Change-Id: I0350a06cae7457809fb261e2d8ec99eda80cc50a
This commit is contained in:
Fan Zhang
2017-02-16 15:00:53 -08:00
parent aba2c95ae0
commit dfce62c507
39 changed files with 1787 additions and 946 deletions

View File

@@ -614,7 +614,7 @@
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS" <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.applications.ManageAssist" /> android:value="com.android.settings.applications.assist.ManageAssist" />
</activity> </activity>
<activity android:name="Settings$KeyboardLayoutPickerActivity" <activity android:name="Settings$KeyboardLayoutPickerActivity"

View File

@@ -6959,12 +6959,6 @@
<!-- Title for Default Assist settings [CHAR LIMIT=30] --> <!-- Title for Default Assist settings [CHAR LIMIT=30] -->
<string name="default_assist_title">Assist app</string> <string name="default_assist_title">Assist app</string>
<!-- Summary for No Default Assist settings [CHAR LIMIT=45] -->
<string name="default_assist_none">None</string>
<!-- Title for Choose Assist dialog [CHAR LIMIT=35] -->
<string name="choose_assist_title">Choose Assist app</string>
<!-- [CHAR_LIMIT=45] Title of the security warning dialog for setting an assitant --> <!-- [CHAR_LIMIT=45] Title of the security warning dialog for setting an assitant -->
<string name="assistant_security_warning_title"> <string name="assistant_security_warning_title">
Make <xliff:g id="assistant_app_name">%s</xliff:g> your assistant? Make <xliff:g id="assistant_app_name">%s</xliff:g> your assistant?

View File

@@ -23,7 +23,7 @@
<Preference <Preference
android:key="assist_and_voice_input" android:key="assist_and_voice_input"
android:title="@string/assist_and_voice_input_title" android:title="@string/assist_and_voice_input_title"
android:fragment="com.android.settings.applications.ManageAssist" android:fragment="com.android.settings.applications.assist.ManageAssist"
android:order="-20"/> android:order="-20"/>
<Preference <Preference

View File

@@ -19,10 +19,11 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/assist_and_voice_input_title"> android:title="@string/assist_and_voice_input_title">
<com.android.settings.applications.DefaultAssistPreference <com.android.settings.widget.GearPreference
android:key="default_assist" android:key="default_assist"
android:title="@string/default_assist_title" android:title="@string/default_assist_title"
android:summary="@string/default_assist_none"/> android:summary="@string/app_list_preference_none"
android:fragment="com.android.settings.applications.assist.DefaultAssistPicker"/>
<Preference <Preference
android:key="gesture_assist" android:key="gesture_assist"
@@ -36,7 +37,6 @@
<SwitchPreference <SwitchPreference
android:key="screenshot" android:key="screenshot"
android:dependency="context"
android:title="@string/assist_access_screenshot_title" android:title="@string/assist_access_screenshot_title"
android:summary="@string/assist_access_screenshot_summary"/> android:summary="@string/assist_access_screenshot_summary"/>
@@ -45,8 +45,9 @@
android:title="@string/assist_flash_title" android:title="@string/assist_flash_title"
android:summary="@string/assist_flash_summary"/> android:summary="@string/assist_flash_summary"/>
<com.android.settings.voice.VoiceInputListPreference <com.android.settings.widget.GearPreference
android:key="voice_input_settings" android:key="voice_input_settings"
android:title="@string/voice_input_settings_title"/> android:title="@string/voice_input_settings_title"
android:fragment="com.android.settings.applications.assist.DefaultVoiceInputPicker"/>
</PreferenceScreen> </PreferenceScreen>

View File

@@ -1,356 +0,0 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings;
import android.app.AlertDialog;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/**
* Extends ListPreference to allow us to show the icons for a given list of applications. We do this
* because the names of applications are very similar and the user may not be able to determine what
* app they are selecting without an icon.
*
* @deprecated Selecting app from a list should be done in full UI. Use DefaultAppPickerFragment
* instead.
*/
@Deprecated
public class AppListPreference extends CustomListPreference {
public static final String ITEM_NONE_VALUE = "";
protected final boolean mForWork;
protected final int mUserId;
private boolean mSavesState = true;
private Drawable[] mEntryDrawables;
private boolean mShowItemNone = false;
private CharSequence[] mSummaries;
private int mSystemAppIndex = -1;
public class AppArrayAdapter extends ArrayAdapter<CharSequence> {
private Drawable[] mImageDrawables = null;
private int mSelectedIndex = 0;
public AppArrayAdapter(Context context, int textViewResourceId,
CharSequence[] objects, Drawable[] imageDrawables, int selectedIndex) {
super(context, textViewResourceId, objects);
mSelectedIndex = selectedIndex;
mImageDrawables = imageDrawables;
}
@Override
public boolean isEnabled(int position) {
return mSummaries == null || mSummaries[position] == null;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(getContext());
View view = inflater.inflate(R.layout.app_preference_item, parent, false);
TextView textView = (TextView) view.findViewById(android.R.id.title);
textView.setText(getItem(position));
if (position == mSelectedIndex && position == mSystemAppIndex) {
view.findViewById(R.id.system_default_label).setVisibility(View.VISIBLE);
} else if (position == mSelectedIndex) {
view.findViewById(R.id.default_label).setVisibility(View.VISIBLE);
} else if (position == mSystemAppIndex) {
view.findViewById(R.id.system_label).setVisibility(View.VISIBLE);
}
ImageView imageView = (ImageView) view.findViewById(android.R.id.icon);
imageView.setImageDrawable(mImageDrawables[position]);
// Summaries are describing why a item is disabled, so anything with a summary
// is not enabled.
boolean enabled = mSummaries == null || mSummaries[position] == null;
view.setEnabled(enabled);
if (!enabled) {
TextView summary = (TextView) view.findViewById(android.R.id.summary);
summary.setText(mSummaries[position]);
summary.setVisibility(View.VISIBLE);
}
return view;
}
}
public AppListPreference(Context context, AttributeSet attrs, int defStyle, int defAttrs) {
super(context, attrs, defStyle, defAttrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WorkPreference, 0, 0);
mForWork = a.getBoolean(R.styleable.WorkPreference_forWork, false);
final UserHandle managedProfile = Utils.getManagedProfile(UserManager.get(context));
mUserId = mForWork && managedProfile != null ? managedProfile.getIdentifier()
: UserHandle.myUserId();
}
public AppListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WorkPreference, 0, 0);
mForWork = a.getBoolean(R.styleable.WorkPreference_forWork, false);
final UserHandle managedProfile = Utils.getManagedProfile(UserManager.get(context));
mUserId = mForWork && managedProfile != null ? managedProfile.getIdentifier()
: UserHandle.myUserId();
}
public void setSavesState(boolean savesState) {
mSavesState = savesState;
}
public void setShowItemNone(boolean showItemNone) {
mShowItemNone = showItemNone;
}
public void setPackageNames(CharSequence[] packageNames, CharSequence defaultPackageName) {
setPackageNames(packageNames, defaultPackageName, null);
}
public void setPackageNames(CharSequence[] packageNames, CharSequence defaultPackageName,
CharSequence systemPackageName) {
// Look up all package names in PackageManager. Skip ones we can't find.
PackageManager pm = getContext().getPackageManager();
final int entryCount = packageNames.length + (mShowItemNone ? 1 : 0);
List<CharSequence> applicationNames = new ArrayList<>(entryCount);
List<CharSequence> validatedPackageNames = new ArrayList<>(entryCount);
List<Drawable> entryDrawables = new ArrayList<>(entryCount);
int selectedIndex = -1;
mSystemAppIndex = -1;
for (int i = 0; i < packageNames.length; i++) {
try {
ApplicationInfo appInfo = pm.getApplicationInfoAsUser(packageNames[i].toString(), 0,
mUserId);
applicationNames.add(appInfo.loadLabel(pm));
validatedPackageNames.add(appInfo.packageName);
entryDrawables.add(appInfo.loadIcon(pm));
if (defaultPackageName != null &&
appInfo.packageName.contentEquals(defaultPackageName)) {
selectedIndex = i;
}
if (appInfo.packageName != null && systemPackageName != null &&
appInfo.packageName.contentEquals(systemPackageName)) {
mSystemAppIndex = i;
}
} catch (NameNotFoundException e) {
// Skip unknown packages.
}
}
if (mShowItemNone) {
applicationNames.add(
getContext().getResources().getText(R.string.app_list_preference_none));
validatedPackageNames.add(ITEM_NONE_VALUE);
entryDrawables.add(getContext().getDrawable(R.drawable.ic_remove_circle));
}
setEntries(applicationNames.toArray(new CharSequence[applicationNames.size()]));
setEntryValues(
validatedPackageNames.toArray(new CharSequence[validatedPackageNames.size()]));
mEntryDrawables = entryDrawables.toArray(new Drawable[entryDrawables.size()]);
if (selectedIndex != -1) {
setValueIndex(selectedIndex);
} else {
setValue(null);
}
}
public void setComponentNames(ComponentName[] componentNames, ComponentName defaultCN) {
setComponentNames(componentNames, defaultCN, null);
}
public void setComponentNames(ComponentName[] componentNames, ComponentName defaultCN,
CharSequence[] summaries) {
mSummaries = summaries;
// Look up all package names in PackageManager. Skip ones we can't find.
PackageManager pm = getContext().getPackageManager();
final int entryCount = componentNames.length + (mShowItemNone ? 1 : 0);
List<CharSequence> applicationNames = new ArrayList<>(entryCount);
List<CharSequence> validatedComponentNames = new ArrayList<>(entryCount);
List<Drawable> entryDrawables = new ArrayList<>(entryCount);
int selectedIndex = -1;
for (int i = 0; i < componentNames.length; i++) {
try {
ActivityInfo activityInfo = AppGlobals.getPackageManager().getActivityInfo(
componentNames[i], 0, mUserId);
if (activityInfo != null) {
applicationNames.add(activityInfo.loadLabel(pm));
validatedComponentNames.add(componentNames[i].flattenToString());
entryDrawables.add(activityInfo.loadIcon(pm));
} else {
ApplicationInfo appInfo = pm.getApplicationInfoAsUser(
componentNames[i].getPackageName().toString(), 0, mUserId);
applicationNames.add(appInfo.loadLabel(pm));
validatedComponentNames.add(componentNames[i].flattenToString());
entryDrawables.add(appInfo.loadIcon(pm));
}
if (defaultCN != null && componentNames[i].equals(defaultCN)) {
selectedIndex = i;
}
} catch (RemoteException|NameNotFoundException e) {
// Skip unknown packages.
}
}
if (mShowItemNone) {
applicationNames.add(
getContext().getResources().getText(R.string.app_list_preference_none));
validatedComponentNames.add(ITEM_NONE_VALUE);
entryDrawables.add(getContext().getDrawable(R.drawable.ic_remove_circle));
}
setEntries(applicationNames.toArray(new CharSequence[applicationNames.size()]));
setEntryValues(
validatedComponentNames.toArray(new CharSequence[validatedComponentNames.size()]));
mEntryDrawables = entryDrawables.toArray(new Drawable[entryDrawables.size()]);
if (selectedIndex != -1) {
setValueIndex(selectedIndex);
} else {
setValue(null);
}
}
protected ListAdapter createListAdapter() {
final String selectedValue = getValue();
final boolean selectedNone = selectedValue == null ||
(mShowItemNone && selectedValue.contentEquals(ITEM_NONE_VALUE));
int selectedIndex = selectedNone ? -1 : findIndexOfValue(selectedValue);
return new AppArrayAdapter(getContext(),
R.layout.app_preference_item, getEntries(), mEntryDrawables, selectedIndex);
}
@Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
DialogInterface.OnClickListener listener) {
builder.setAdapter(createListAdapter(), listener);
}
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
if (mSavesState) {
return new SavedState(getEntryValues(), getValue(), mSummaries, mShowItemNone, superState);
} else {
return superState;
}
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (mSavesState || state instanceof SavedState) {
SavedState savedState = (SavedState) state;
mShowItemNone = savedState.showItemNone;
setPackageNames(savedState.entryValues, savedState.value);
mSummaries = savedState.summaries;
super.onRestoreInstanceState(savedState.superState);
} else {
super.onRestoreInstanceState(state);
}
}
/**
* Sets app label as summary if there is only 1 app applicable to this preference.
*/
protected void setSoleAppLabelAsSummary() {
final CharSequence soleLauncherLabel = getSoleAppLabel();
if (!TextUtils.isEmpty(soleLauncherLabel)) {
setSummary(soleLauncherLabel);
}
}
/**
* Returns app label if there is only 1 app applicable to this preference.
*/
protected CharSequence getSoleAppLabel() {
// Intentionally left empty so subclasses can override with necessary logic.
return null;
}
private static class SavedState implements Parcelable {
public final CharSequence[] entryValues;
public final CharSequence value;
public final boolean showItemNone;
public final Parcelable superState;
public final CharSequence[] summaries;
public SavedState(CharSequence[] entryValues, CharSequence value, CharSequence[] summaries,
boolean showItemNone, Parcelable superState) {
this.entryValues = entryValues;
this.value = value;
this.showItemNone = showItemNone;
this.superState = superState;
this.summaries = summaries;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeCharSequenceArray(entryValues);
dest.writeCharSequence(value);
dest.writeInt(showItemNone ? 1 : 0);
dest.writeParcelable(superState, flags);
dest.writeCharSequenceArray(summaries);
}
public static Creator<SavedState> CREATOR = new Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel source) {
CharSequence[] entryValues = source.readCharSequenceArray();
CharSequence value = source.readCharSequence();
boolean showItemNone = source.readInt() != 0;
Parcelable superState = source.readParcelable(getClass().getClassLoader());
CharSequence[] summaries = source.readCharSequenceArray();
return new SavedState(entryValues, value, summaries, showItemNone, superState);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.assist;
import android.content.Context;
import android.net.Uri;
import android.os.UserHandle;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.support.v7.preference.TwoStatePreference;
import com.android.internal.app.AssistUtils;
import com.android.settings.core.PreferenceController;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.core.lifecycle.LifecycleObserver;
import com.android.settings.core.lifecycle.events.OnPause;
import com.android.settings.core.lifecycle.events.OnResume;
import java.util.Arrays;
import java.util.List;
public class AssistContextPreferenceController extends PreferenceController
implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
private static final String KEY_CONTEXT = "context";
private final AssistUtils mAssistUtils;
private final SettingObserver mSettingObserver;
private Preference mPreference;
private PreferenceScreen mScreen;
public AssistContextPreferenceController(Context context, Lifecycle lifecycle) {
super(context);
mAssistUtils = new AssistUtils(context);
mSettingObserver = new SettingObserver();
if (lifecycle != null) {
lifecycle.addObserver(this);
}
}
@Override
public boolean isAvailable() {
return mAssistUtils.getAssistComponentForUser(UserHandle.myUserId()) != null;
}
@Override
public String getPreferenceKey() {
return KEY_CONTEXT;
}
@Override
public void displayPreference(PreferenceScreen screen) {
mScreen = screen;
mPreference = screen.findPreference(getPreferenceKey());
super.displayPreference(screen);
}
@Override
public void onResume() {
mSettingObserver.register(mContext.getContentResolver(), true);
updatePreference();
}
@Override
public void updateState(Preference preference) {
updatePreference();
}
@Override
public void onPause() {
mSettingObserver.register(mContext.getContentResolver(), false);
}
private void updatePreference() {
if (mPreference == null || !(mPreference instanceof TwoStatePreference)) {
return;
}
if (isAvailable()) {
if (mScreen.findPreference(getPreferenceKey()) == null) {
// add it if it's not on scree
mScreen.addPreference(mPreference);
}
} else {
mScreen.removePreference(mPreference);
}
((TwoStatePreference) mPreference).setChecked(isChecked(mContext));
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ASSIST_STRUCTURE_ENABLED,
(boolean) newValue ? 1 : 0);
return true;
}
static boolean isChecked(Context context) {
return Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1) != 0;
}
class SettingObserver extends AssistSettingObserver {
private final Uri URI =
Settings.Secure.getUriFor(Settings.Secure.ASSIST_STRUCTURE_ENABLED);
@Override
protected List<Uri> getSettingUris() {
return Arrays.asList(URI);
}
@Override
public void onSettingChange() {
updatePreference();
}
}
}

View File

@@ -0,0 +1,158 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.assist;
import android.content.ComponentName;
import android.content.Context;
import android.net.Uri;
import android.os.UserHandle;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.support.v7.preference.TwoStatePreference;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
import com.android.settings.core.PreferenceController;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.core.lifecycle.LifecycleObserver;
import com.android.settings.core.lifecycle.events.OnPause;
import com.android.settings.core.lifecycle.events.OnResume;
import java.util.Arrays;
import java.util.List;
public class AssistFlashScreenPreferenceController extends PreferenceController
implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
private static final String KEY_FLASH = "flash";
private final AssistUtils mAssistUtils;
private final SettingObserver mSettingObserver;
private PreferenceScreen mScreen;
private Preference mPreference;
public AssistFlashScreenPreferenceController(Context context, Lifecycle lifecycle) {
super(context);
mAssistUtils = new AssistUtils(context);
mSettingObserver = new SettingObserver();
if (lifecycle != null) {
lifecycle.addObserver(this);
}
}
@Override
public boolean isAvailable() {
return getCurrentAssist() != null && allowDisablingAssistDisclosure();
}
@Override
public String getPreferenceKey() {
return KEY_FLASH;
}
@Override
public void displayPreference(PreferenceScreen screen) {
mScreen = screen;
mPreference = screen.findPreference(getPreferenceKey());
super.displayPreference(screen);
}
@Override
public void onResume() {
mSettingObserver.register(mContext.getContentResolver(), true);
updatePreference();
}
@Override
public void updateState(Preference preference) {
updatePreference();
}
@Override
public void onPause() {
mSettingObserver.register(mContext.getContentResolver(), false);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
(boolean) newValue ? 1 : 0);
return true;
}
private void updatePreference() {
if (mPreference == null || !(mPreference instanceof TwoStatePreference)) {
return;
}
if (isAvailable()) {
if (mScreen.findPreference(getPreferenceKey()) == null) {
// add it if it's not on scree
mScreen.addPreference(mPreference);
}
} else {
mScreen.removePreference(mPreference);
}
ComponentName assistant = getCurrentAssist();
boolean isContextChecked = AssistContextPreferenceController.isChecked(mContext);
mPreference.setEnabled(isContextChecked && isPreInstalledAssistant(assistant));
((TwoStatePreference) mPreference).setChecked(willShowFlash(assistant));
}
@VisibleForTesting
boolean willShowFlash(ComponentName assistant) {
return AssistUtils.shouldDisclose(mContext, assistant);
}
@VisibleForTesting
boolean isPreInstalledAssistant(ComponentName assistant) {
return AssistUtils.isPreinstalledAssistant(mContext, assistant);
}
@VisibleForTesting
boolean allowDisablingAssistDisclosure() {
return AssistUtils.allowDisablingAssistDisclosure(mContext);
}
private ComponentName getCurrentAssist() {
return mAssistUtils.getAssistComponentForUser(UserHandle.myUserId());
}
class SettingObserver extends AssistSettingObserver {
private final Uri URI =
Settings.Secure.getUriFor(Settings.Secure.ASSIST_DISCLOSURE_ENABLED);
private final Uri CONTEXT_URI =
Settings.Secure.getUriFor(Settings.Secure.ASSIST_STRUCTURE_ENABLED);
@Override
protected List<Uri> getSettingUris() {
return Arrays.asList(URI, CONTEXT_URI);
}
@Override
public void onSettingChange() {
updatePreference();
}
}
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.assist;
import android.content.Context;
import android.net.Uri;
import android.os.UserHandle;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.support.v7.preference.TwoStatePreference;
import com.android.internal.app.AssistUtils;
import com.android.settings.core.PreferenceController;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.core.lifecycle.LifecycleObserver;
import com.android.settings.core.lifecycle.events.OnPause;
import com.android.settings.core.lifecycle.events.OnResume;
import java.util.Arrays;
import java.util.List;
public class AssistScreenshotPreferenceController extends PreferenceController
implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
private static final String KEY_SCREENSHOT = "screenshot";
private final AssistUtils mAssistUtils;
private final SettingObserver mSettingObserver;
private PreferenceScreen mScreen;
private Preference mPreference;
public AssistScreenshotPreferenceController(Context context, Lifecycle lifecycle) {
super(context);
mAssistUtils = new AssistUtils(context);
mSettingObserver = new SettingObserver();
if (lifecycle != null) {
lifecycle.addObserver(this);
}
}
@Override
public boolean isAvailable() {
return mAssistUtils.getAssistComponentForUser(UserHandle.myUserId()) != null;
}
@Override
public void displayPreference(PreferenceScreen screen) {
mScreen = screen;
mPreference = screen.findPreference(getPreferenceKey());
super.displayPreference(screen);
}
@Override
public String getPreferenceKey() {
return KEY_SCREENSHOT;
}
@Override
public void onResume() {
mSettingObserver.register(mContext.getContentResolver(), true);
updatePreference();
}
@Override
public void updateState(Preference preference) {
updatePreference();
}
@Override
public void onPause() {
mSettingObserver.register(mContext.getContentResolver(), false);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
(boolean) newValue ? 1 : 0);
return true;
}
private void updatePreference() {
if (mPreference == null || !(mPreference instanceof TwoStatePreference)) {
return;
}
if (isAvailable()) {
if (mScreen.findPreference(getPreferenceKey()) == null) {
// add it if it's not on scree
mScreen.addPreference(mPreference);
}
} else {
mScreen.removePreference(mPreference);
}
final boolean checked = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ASSIST_SCREENSHOT_ENABLED, 1) != 0;
((TwoStatePreference) mPreference).setChecked(checked);
final boolean contextChecked = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1) != 0;
mPreference.setEnabled(contextChecked);
}
class SettingObserver extends AssistSettingObserver {
private final Uri URI =
Settings.Secure.getUriFor(Settings.Secure.ASSIST_SCREENSHOT_ENABLED);
private final Uri CONTEXT_URI =
Settings.Secure.getUriFor(Settings.Secure.ASSIST_STRUCTURE_ENABLED);
@Override
protected List<Uri> getSettingUris() {
return Arrays.asList(URI, CONTEXT_URI);
}
@Override
public void onSettingChange() {
updatePreference();
}
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.assist;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
import java.util.List;
public abstract class AssistSettingObserver extends ContentObserver {
private final Uri ASSIST_URI =
Settings.Secure.getUriFor(Settings.Secure.ASSISTANT);
public AssistSettingObserver() {
super(new Handler());
}
public void register(ContentResolver cr, boolean register) {
if (register) {
cr.registerContentObserver(ASSIST_URI, false, this);
final List<Uri> settingUri = getSettingUris();
if (settingUri != null) {
for (Uri uri : settingUri)
cr.registerContentObserver(uri, false, this);
}
} else {
cr.unregisterContentObserver(this);
}
}
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
boolean shouldUpdatePreference = false;
final List<Uri> settingUri = getSettingUris();
if (ASSIST_URI.equals(uri) || (settingUri != null && settingUri.contains(uri))) {
shouldUpdatePreference = true;
}
if (shouldUpdatePreference) {
onSettingChange();
}
}
protected abstract List<Uri> getSettingUris();
public abstract void onSettingChange();
}

View File

@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * 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.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.os.UserHandle;
import android.provider.Settings; import android.provider.Settings;
import android.service.voice.VoiceInteractionService; import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionServiceInfo; import android.service.voice.VoiceInteractionServiceInfo;
import android.speech.RecognitionService; import android.speech.RecognitionService;
import android.util.AttributeSet; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.android.internal.app.AssistUtils; 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.R;
import com.android.settings.applications.defaultapps.DefaultAppInfo;
import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class DefaultAssistPreference extends AppListPreferenceWithSettings { public class DefaultAssistPicker extends DefaultAppPickerFragment {
private static final String TAG = DefaultAssistPreference.class.getSimpleName();
private static final String TAG = "DefaultAssistPicker";
private static final Intent ASSIST_SERVICE_PROBE =
new Intent(VoiceInteractionService.SERVICE_INTERFACE);
private static final Intent ASSIST_ACTIVITY_PROBE =
new Intent(Intent.ACTION_ASSIST);
private final List<Info> mAvailableAssistants = new ArrayList<>(); private final List<Info> mAvailableAssistants = new ArrayList<>();
private final AssistUtils mAssistUtils; private AssistUtils mAssistUtils;
public DefaultAssistPreference(Context context, AttributeSet attrs) { @Override
super(context, attrs); public int getMetricsCategory() {
setShowItemNone(true); return MetricsProto.MetricsEvent.DEFAULT_ASSIST_PICKER;
setDialogTitle(R.string.choose_assist_title); }
@Override
protected boolean shouldShowItemNone() {
return true;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
mAssistUtils = new AssistUtils(context); mAssistUtils = new AssistUtils(context);
} }
@Override @Override
protected boolean persistString(String value) { protected List<DefaultAppInfo> getCandidates() {
final Info info = findAssistantByPackageName(value); mAvailableAssistants.clear();
addAssistServices();
addAssistActivities();
final List<String> packages = new ArrayList<>();
final List<DefaultAppInfo> candidates = new ArrayList<>();
for (Info info : mAvailableAssistants) {
final String packageName = info.component.getPackageName();
if (packages.contains(packageName)) {
// A service appears before an activity thus overrides it if from the same package.
continue;
}
packages.add(packageName);
candidates.add(new DefaultAppInfo(mUserId, info.component));
}
return candidates;
}
@Override
protected String getDefaultAppKey() {
final ComponentName cn = getCurrentAssist();
if (cn != null) {
return new DefaultAppInfo(mUserId, cn).getKey();
}
return null;
}
@Override
protected String getConfirmationMessage(DefaultAppInfo appInfo) {
if (appInfo == null) {
return null;
}
return getContext().getString(R.string.assistant_security_warning,
appInfo.loadLabel(mPm.getPackageManager()));
}
@Override
protected boolean setDefaultAppKey(String key) {
if (TextUtils.isEmpty(key)) {
setAssistNone();
return true;
}
ComponentName cn = ComponentName.unflattenFromString(key);
final Info info = findAssistantByPackageName(cn.getPackageName());
if (info == null) { if (info == null) {
setAssistNone(); setAssistNone();
return true; return true;
@@ -67,83 +123,15 @@ public class DefaultAssistPreference extends AppListPreferenceWithSettings {
return true; return true;
} }
private void setAssistNone() { public ComponentName getCurrentAssist() {
Settings.Secure.putString(getContext().getContentResolver(), return mAssistUtils.getAssistComponentForUser(mUserId);
Settings.Secure.ASSISTANT, ITEM_NONE_VALUE);
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 serviceRecognizerName = new ComponentName(
serviceInfo.component.getPackageName(),
serviceInfo.voiceInteractionServiceInfo.getRecognitionService())
.flattenToShortString();
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.ASSISTANT, serviceComponentName);
Settings.Secure.putString(getContext().getContentResolver(),
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) {
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.ASSISTANT, activityInfo.component.flattenToShortString());
Settings.Secure.putString(getContext().getContentResolver(),
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(
new Intent(RecognitionService.SERVICE_INTERFACE),
PackageManager.GET_META_DATA);
if (resolveInfo == null || resolveInfo.serviceInfo == null) {
Log.w(TAG, "Unable to resolve default voice recognition service.");
return "";
}
return new ComponentName(resolveInfo.serviceInfo.packageName,
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() { private void addAssistServices() {
PackageManager pm = getContext().getPackageManager(); final PackageManager pm = mPm.getPackageManager();
final List<ResolveInfo> services = pm.queryIntentServices(
List<ResolveInfo> services = pm.queryIntentServices( ASSIST_SERVICE_PROBE, PackageManager.GET_META_DATA);
new Intent(VoiceInteractionService.SERVICE_INTERFACE), for (ResolveInfo resolveInfo : services) {
PackageManager.GET_META_DATA);
for (int i = 0; i < services.size(); ++i) {
ResolveInfo resolveInfo = services.get(i);
VoiceInteractionServiceInfo voiceInteractionServiceInfo = VoiceInteractionServiceInfo voiceInteractionServiceInfo =
new VoiceInteractionServiceInfo(pm, resolveInfo.serviceInfo); new VoiceInteractionServiceInfo(pm, resolveInfo.serviceInfo);
if (!voiceInteractionServiceInfo.getSupportsAssist()) { if (!voiceInteractionServiceInfo.getSupportsAssist()) {
@@ -158,44 +146,73 @@ public class DefaultAssistPreference extends AppListPreferenceWithSettings {
} }
private void addAssistActivities() { private void addAssistActivities() {
PackageManager pm = getContext().getPackageManager(); final PackageManager pm = mPm.getPackageManager();
final List<ResolveInfo> activities = pm.queryIntentActivities(
List<ResolveInfo> activities = pm.queryIntentActivities( ASSIST_ACTIVITY_PROBE, PackageManager.MATCH_DEFAULT_ONLY);
new Intent(Intent.ACTION_ASSIST), for (ResolveInfo resolveInfo : activities) {
PackageManager.MATCH_DEFAULT_ONLY);
for (int i = 0; i < activities.size(); ++i) {
ResolveInfo resolveInfo = activities.get(i);
mAvailableAssistants.add(new Info( mAvailableAssistants.add(new Info(
new ComponentName(resolveInfo.activityInfo.packageName, new ComponentName(resolveInfo.activityInfo.packageName,
resolveInfo.activityInfo.name))); resolveInfo.activityInfo.name)));
} }
} }
public ComponentName getCurrentAssist() { private Info findAssistantByPackageName(String packageName) {
return mAssistUtils.getAssistComponentForUser(UserHandle.myUserId()); for (Info info : mAvailableAssistants) {
if (TextUtils.equals(info.component.getPackageName(), packageName)) {
return info;
}
}
return null;
} }
public void refreshAssistApps() { private void setAssistNone() {
mAvailableAssistants.clear(); Settings.Secure.putString(getContext().getContentResolver(),
addAssistServices(); Settings.Secure.ASSISTANT, "");
addAssistActivities(); Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_INTERACTION_SERVICE, "");
List<String> packages = new ArrayList<>(); Settings.Secure.putString(getContext().getContentResolver(),
for (int i = 0; i < mAvailableAssistants.size(); ++i) { Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer());
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(); private void setAssistService(Info serviceInfo) {
setPackageNames(packages.toArray(new String[packages.size()]), final String serviceComponentName = serviceInfo.component.
currentAssist == null ? null : currentAssist.getPackageName()); flattenToShortString();
final String serviceRecognizerName = new ComponentName(
serviceInfo.component.getPackageName(),
serviceInfo.voiceInteractionServiceInfo.getRecognitionService())
.flattenToShortString();
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.ASSISTANT, serviceComponentName);
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_INTERACTION_SERVICE, serviceComponentName);
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_RECOGNITION_SERVICE, serviceRecognizerName);
} }
private static class Info { private void setAssistActivity(Info activityInfo) {
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.ASSISTANT, activityInfo.component.flattenToShortString());
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_INTERACTION_SERVICE, "");
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer());
}
private String getDefaultRecognizer() {
final ResolveInfo resolveInfo = mPm.getPackageManager().resolveService(
new Intent(RecognitionService.SERVICE_INTERFACE),
PackageManager.GET_META_DATA);
if (resolveInfo == null || resolveInfo.serviceInfo == null) {
Log.w(TAG, "Unable to resolve default voice recognition service.");
return "";
}
return new ComponentName(resolveInfo.serviceInfo.packageName,
resolveInfo.serviceInfo.name).flattenToShortString();
}
static class Info {
public final ComponentName component; public final ComponentName component;
public final VoiceInteractionServiceInfo voiceInteractionServiceInfo; public final VoiceInteractionServiceInfo voiceInteractionServiceInfo;

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.assist;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionServiceInfo;
import com.android.internal.app.AssistUtils;
import com.android.settings.applications.defaultapps.DefaultAppInfo;
import com.android.settings.applications.defaultapps.DefaultAppPreferenceController;
import java.util.List;
public class DefaultAssistPreferenceController extends DefaultAppPreferenceController {
private static final String KEY_DEFAULT_ASSIST = "default_assist";
private AssistUtils mAssistUtils;
public DefaultAssistPreferenceController(Context context) {
super(context);
mAssistUtils = new AssistUtils(context);
}
@Override
protected Intent getSettingIntent(DefaultAppInfo info) {
final ComponentName cn = mAssistUtils.getAssistComponentForUser(mUserId);
if (cn == null) {
return null;
}
final Intent probe = new Intent(VoiceInteractionService.SERVICE_INTERFACE)
.setPackage(cn.getPackageName());
final PackageManager pm = mPackageManager.getPackageManager();
final List<ResolveInfo> services = pm.queryIntentServices(probe, PackageManager
.GET_META_DATA);
if (services == null || services.isEmpty()) {
return null;
}
final ResolveInfo resolveInfo = services.get(0);
final VoiceInteractionServiceInfo voiceInfo =
new VoiceInteractionServiceInfo(pm, resolveInfo.serviceInfo);
if (!voiceInfo.getSupportsAssist()) {
return null;
}
final String activity = voiceInfo.getSettingsActivity();
return new Intent(Intent.ACTION_MAIN)
.setComponent(new ComponentName(cn.getPackageName(), activity));
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public String getPreferenceKey() {
return KEY_DEFAULT_ASSIST;
}
@Override
protected DefaultAppInfo getDefaultAppInfo() {
final ComponentName cn = mAssistUtils.getAssistComponentForUser(mUserId);
if (cn == null) {
return null;
}
return new DefaultAppInfo(mUserId, cn);
}
}

View File

@@ -0,0 +1,162 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.assist;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.provider.Settings;
import android.text.TextUtils;
import com.android.internal.app.AssistUtils;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.applications.defaultapps.DefaultAppInfo;
import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
import java.util.ArrayList;
import java.util.List;
public class DefaultVoiceInputPicker extends DefaultAppPickerFragment {
private VoiceInputHelper mHelper;
private AssistUtils mAssistUtils;
private String mAssistRestrict;
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.DEFAULT_VOICE_INPUT_PICKER;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
mAssistUtils = new AssistUtils(context);
mHelper = new VoiceInputHelper(context);
mHelper.buildUi();
final ComponentName assist = getCurrentAssist();
if (isCurrentAssistVoiceService(assist, getCurrentService(mHelper))) {
mAssistRestrict = assist.flattenToShortString();
}
}
@Override
protected List<VoiceInputDefaultAppInfo> getCandidates() {
final List<VoiceInputDefaultAppInfo> candidates = new ArrayList<>();
boolean hasEnabled = true;
for (VoiceInputHelper.InteractionInfo info : mHelper.mAvailableInteractionInfos) {
final boolean enabled = TextUtils.equals(info.key, mAssistRestrict);
hasEnabled |= enabled;
candidates.add(new VoiceInputDefaultAppInfo(mUserId, info, enabled));
}
final boolean assistIsService = !hasEnabled;
for (VoiceInputHelper.RecognizerInfo info : mHelper.mAvailableRecognizerInfos) {
final boolean enabled = !assistIsService;
candidates.add(new VoiceInputDefaultAppInfo(mUserId, info, enabled));
}
return candidates;
}
@Override
protected String getDefaultAppKey() {
final ComponentName currentService = getCurrentService(mHelper);
if (currentService == null) {
return null;
}
return currentService.flattenToShortString();
}
@Override
protected boolean setDefaultAppKey(String value) {
for (VoiceInputHelper.InteractionInfo info : mHelper.mAvailableInteractionInfos) {
if (TextUtils.equals(value, info.key)) {
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_INTERACTION_SERVICE, value);
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_RECOGNITION_SERVICE,
new ComponentName(info.service.packageName,
info.serviceInfo.getRecognitionService())
.flattenToShortString());
return true;
}
}
for (VoiceInputHelper.RecognizerInfo info : mHelper.mAvailableRecognizerInfos) {
if (TextUtils.equals(value, info.key)) {
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_INTERACTION_SERVICE, "");
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_RECOGNITION_SERVICE, value);
return true;
}
}
return true;
}
public static ComponentName getCurrentService(VoiceInputHelper helper) {
if (helper.mCurrentVoiceInteraction != null) {
return helper.mCurrentVoiceInteraction;
} else if (helper.mCurrentRecognizer != null) {
return helper.mCurrentRecognizer;
} else {
return null;
}
}
private ComponentName getCurrentAssist() {
return mAssistUtils.getAssistComponentForUser(mUserId);
}
public static boolean isCurrentAssistVoiceService(ComponentName currentAssist,
ComponentName currentVoiceService) {
return currentAssist == null && currentVoiceService == null ||
currentAssist != null && currentAssist.equals(currentVoiceService);
}
public static class VoiceInputDefaultAppInfo extends DefaultAppInfo {
public VoiceInputHelper.BaseInfo mInfo;
public VoiceInputDefaultAppInfo(int userId, VoiceInputHelper.BaseInfo info,
boolean enabled) {
super(userId, info.componentName, null /* summary */, enabled);
mInfo = info;
}
@Override
public String getKey() {
return mInfo.key;
}
@Override
public CharSequence loadLabel(PackageManager pm) {
if (mInfo instanceof VoiceInputHelper.InteractionInfo) {
return mInfo.appLabel;
} else {
return mInfo.label;
}
}
public Intent getSettingIntent() {
if (mInfo.settings == null) {
return null;
}
return new Intent(Intent.ACTION_MAIN).setComponent(mInfo.settings);
}
}
}

View File

@@ -0,0 +1,165 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.assist;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils;
import com.android.internal.app.AssistUtils;
import com.android.settings.applications.defaultapps.DefaultAppInfo;
import com.android.settings.applications.defaultapps.DefaultAppPreferenceController;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.core.lifecycle.LifecycleObserver;
import com.android.settings.core.lifecycle.events.OnPause;
import com.android.settings.core.lifecycle.events.OnResume;
import java.util.List;
public class DefaultVoiceInputPreferenceController extends DefaultAppPreferenceController
implements LifecycleObserver, OnResume, OnPause {
private static final String KEY_VOICE_INPUT = "voice_input_settings";
private VoiceInputHelper mHelper;
private AssistUtils mAssistUtils;
private PreferenceScreen mScreen;
private Preference mPreference;
private SettingObserver mSettingObserver;
public DefaultVoiceInputPreferenceController(Context context, Lifecycle lifecycle) {
super(context);
mSettingObserver = new SettingObserver();
mAssistUtils = new AssistUtils(context);
mHelper = new VoiceInputHelper(context);
mHelper.buildUi();
if (lifecycle != null) {
lifecycle.addObserver(this);
}
}
@Override
public boolean isAvailable() {
// If current assist is also voice service, don't show voice preference.
final ComponentName currentVoiceService =
DefaultVoiceInputPicker.getCurrentService(mHelper);
final ComponentName currentAssist =
mAssistUtils.getAssistComponentForUser(mUserId);
return !DefaultVoiceInputPicker.isCurrentAssistVoiceService(
currentAssist, currentVoiceService);
}
@Override
public String getPreferenceKey() {
return KEY_VOICE_INPUT;
}
@Override
public void displayPreference(PreferenceScreen screen) {
mScreen = screen;
mPreference = screen.findPreference(getPreferenceKey());
super.displayPreference(screen);
}
@Override
public void onResume() {
mSettingObserver.register(mContext.getContentResolver(), true);
updatePreference();
}
@Override
public void updateState(Preference preference) {
super.updateState(mPreference);
updatePreference();
}
@Override
public void onPause() {
mSettingObserver.register(mContext.getContentResolver(), false);
}
@Override
protected DefaultAppInfo getDefaultAppInfo() {
final String defaultKey = getDefaultAppKey();
if (defaultKey == null) {
return null;
}
for (VoiceInputHelper.InteractionInfo info : mHelper.mAvailableInteractionInfos) {
if (TextUtils.equals(defaultKey, info.key)) {
return new DefaultVoiceInputPicker.VoiceInputDefaultAppInfo(
mUserId, info, true /* enabled */);
}
}
for (VoiceInputHelper.RecognizerInfo info : mHelper.mAvailableRecognizerInfos) {
if (TextUtils.equals(defaultKey, info.key)) {
return new DefaultVoiceInputPicker.VoiceInputDefaultAppInfo(
mUserId, info, true /* enabled */);
}
}
return null;
}
@Override
protected Intent getSettingIntent(DefaultAppInfo info) {
final DefaultAppInfo appInfo = getDefaultAppInfo();
if (appInfo == null
|| !(appInfo instanceof DefaultVoiceInputPicker.VoiceInputDefaultAppInfo)) {
return null;
}
return ((DefaultVoiceInputPicker.VoiceInputDefaultAppInfo) appInfo).getSettingIntent();
}
private void updatePreference() {
if (mPreference == null) {
return;
}
mHelper.buildUi();
if (isAvailable()) {
if (mScreen.findPreference(getPreferenceKey()) == null) {
// add it if it's not on scree
mScreen.addPreference(mPreference);
}
} else {
mScreen.removePreference(mPreference);
}
}
private String getDefaultAppKey() {
final ComponentName currentService = DefaultVoiceInputPicker.getCurrentService(mHelper);
if (currentService == null) {
return null;
}
return currentService.flattenToShortString();
}
class SettingObserver extends AssistSettingObserver {
@Override
protected List<Uri> getSettingUris() {
return null;
}
@Override
public void onSettingChange() {
updatePreference();
}
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.assist;
import android.content.Context;
import android.provider.SearchIndexableResource;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.core.PreferenceController;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Settings screen to manage everything about assist.
*/
public class ManageAssist extends DashboardFragment {
private static final String TAG = "ManageAssist";
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.manage_assist;
}
@Override
protected List<PreferenceController> getPreferenceControllers(Context context) {
final Lifecycle lifecycle = getLifecycle();
final List<PreferenceController> controllers = new ArrayList<>();
controllers.add(new DefaultAssistPreferenceController(context));
controllers.add(new GestureAssistPreferenceController(context));
controllers.add(new AssistContextPreferenceController(context, lifecycle));
controllers.add(new AssistScreenshotPreferenceController(context, lifecycle));
controllers.add(new AssistFlashScreenPreferenceController(context, lifecycle));
controllers.add(new DefaultVoiceInputPreferenceController(context, lifecycle));
return controllers;
}
@Override
public int getMetricsCategory() {
return MetricsEvent.APPLICATIONS_MANAGE_ASSIST;
}
@Override
public void onResume() {
super.onResume();
mFooterPreferenceMixin.createFooterPreference()
.setTitle(R.string.assist_footer);
}
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
boolean enabled) {
final SearchIndexableResource sir = new SearchIndexableResource(context);
sir.xmlResId = R.xml.manage_assist;
return Arrays.asList(sir);
}
@Override
public List<String> getNonIndexableKeys(Context context) {
List<String> result = new ArrayList<>();
new DefaultAssistPreferenceController(context).updateNonIndexableKeys(result);
new GestureAssistPreferenceController(context).updateNonIndexableKeys(result);
new AssistContextPreferenceController(context, null)
.updateNonIndexableKeys(result);
new AssistScreenshotPreferenceController(context, null)
.updateNonIndexableKeys(result);
new AssistFlashScreenPreferenceController(context, null)
.updateNonIndexableKeys(result);
new DefaultVoiceInputPreferenceController(context, null)
.updateNonIndexableKeys(result);
return result;
}
};
}

View File

@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.settings.voice; package com.android.settings.applications.assist;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
@@ -71,7 +71,7 @@ public final class VoiceInputHelper {
@Override @Override
public int compareTo(Object another) { public int compareTo(Object another) {
return labelStr.compareTo(((BaseInfo)another).labelStr); return labelStr.compareTo(((BaseInfo) another).labelStr);
} }
} }
@@ -107,10 +107,6 @@ public final class VoiceInputHelper {
PackageManager.GET_META_DATA); PackageManager.GET_META_DATA);
} }
public boolean hasItems() {
return mAvailableVoiceInteractions.size() > 0 || mAvailableRecognition.size() > 0;
}
public void buildUi() { public void buildUi() {
// Get the currently selected interactor from the secure setting. // Get the currently selected interactor from the secure setting.
String currentSetting = Settings.Secure.getString( String currentSetting = Settings.Secure.getString(
@@ -178,7 +174,7 @@ public final class VoiceInputHelper {
AttributeSet attrs = Xml.asAttributeSet(parser); AttributeSet attrs = Xml.asAttributeSet(parser);
int type; int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.START_TAG) { && type != XmlPullParser.START_TAG) {
} }

View File

@@ -37,13 +37,23 @@ public class DefaultAppInfo {
public final String summary; public final String summary;
// Description for why this item is disabled, if null, the item is enabled. // Description for why this item is disabled, if null, the item is enabled.
public final String disabledDescription; 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) { 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; packageItemInfo = null;
userId = uid; userId = uid;
componentName = cn; componentName = cn;
this.summary = summary; this.summary = summary;
this.disabledDescription = null; this.disabledDescription = null;
this.enabled = enabled;
} }
public DefaultAppInfo(PackageItemInfo info, String description) { public DefaultAppInfo(PackageItemInfo info, String description) {
@@ -52,6 +62,7 @@ public class DefaultAppInfo {
componentName = null; componentName = null;
summary = null; summary = null;
this.disabledDescription = description; this.disabledDescription = description;
enabled = true;
} }
public DefaultAppInfo(PackageItemInfo info) { public DefaultAppInfo(PackageItemInfo info) {

View File

@@ -97,7 +97,7 @@ public abstract class DefaultAppPickerFragment extends InstrumentedPreferenceFra
@VisibleForTesting @VisibleForTesting
public void updateCandidates() { public void updateCandidates() {
mCandidates.clear(); mCandidates.clear();
final List<DefaultAppInfo> candidateList = getCandidates(); final List<? extends DefaultAppInfo> candidateList = getCandidates();
if (candidateList != null) { if (candidateList != null) {
for (DefaultAppInfo info : candidateList) { for (DefaultAppInfo info : candidateList) {
mCandidates.put(info.getKey(), info); mCandidates.put(info.getKey(), info);
@@ -134,6 +134,7 @@ public abstract class DefaultAppPickerFragment extends InstrumentedPreferenceFra
pref.setEnabled(false); pref.setEnabled(false);
pref.setSummary(app.getValue().disabledDescription); pref.setSummary(app.getValue().disabledDescription);
} }
pref.setEnabled(info.enabled);
pref.setOnClickListener(this); pref.setOnClickListener(this);
screen.addPreference(pref); screen.addPreference(pref);
} }
@@ -200,14 +201,15 @@ public abstract class DefaultAppPickerFragment extends InstrumentedPreferenceFra
return null; return null;
} }
protected abstract List<DefaultAppInfo> getCandidates(); protected abstract List<? extends DefaultAppInfo> getCandidates();
protected abstract String getDefaultAppKey(); protected abstract String getDefaultAppKey();
protected abstract boolean setDefaultAppKey(String key); protected abstract boolean setDefaultAppKey(String key);
// Called after the user tries to select an item. // Called after the user tries to select an item.
protected void onSelectionPerformed(boolean success) {} protected void onSelectionPerformed(boolean success) {
}
protected String getConfirmationMessage(DefaultAppInfo appInfo) { protected String getConfirmationMessage(DefaultAppInfo appInfo) {
return null; return null;

View File

@@ -54,7 +54,7 @@ public class DefaultAutoFillPicker extends DefaultAppPickerFragment {
.queryIntentServices(AUTO_FILL_PROBE, PackageManager.GET_META_DATA); .queryIntentServices(AUTO_FILL_PROBE, PackageManager.GET_META_DATA);
for (ResolveInfo info : resolveInfos) { for (ResolveInfo info : resolveInfos) {
candidates.add(new DefaultAppInfo(mUserId, new ComponentName( candidates.add(new DefaultAppInfo(mUserId, new ComponentName(
info.serviceInfo.packageName, info.serviceInfo.name), null /* summary */)); info.serviceInfo.packageName, info.serviceInfo.name)));
} }
return candidates; return candidates;
} }

View File

@@ -55,7 +55,7 @@ public class DefaultAutoFillPreferenceController extends DefaultAppPreferenceCon
DefaultAutoFillPicker.SETTING); DefaultAutoFillPicker.SETTING);
if (!TextUtils.isEmpty(flattenComponent)) { if (!TextUtils.isEmpty(flattenComponent)) {
DefaultAppInfo appInfo = new DefaultAppInfo( DefaultAppInfo appInfo = new DefaultAppInfo(
mUserId, ComponentName.unflattenFromString(flattenComponent), null /*summary*/); mUserId, ComponentName.unflattenFromString(flattenComponent));
return appInfo; return appInfo;
} }
return null; return null;

View File

@@ -75,7 +75,7 @@ public class DefaultHomePreferenceController extends DefaultAppPreferenceControl
final ArrayList<ResolveInfo> homeActivities = new ArrayList<>(); final ArrayList<ResolveInfo> homeActivities = new ArrayList<>();
final ComponentName currentDefaultHome = mPackageManager.getHomeActivities(homeActivities); final ComponentName currentDefaultHome = mPackageManager.getHomeActivities(homeActivities);
return new DefaultAppInfo(mUserId, currentDefaultHome, null /* summary */); return new DefaultAppInfo(mUserId, currentDefaultHome);
} }
private String getOnlyAppLabel() { private String getOnlyAppLabel() {

View File

@@ -76,7 +76,7 @@ public class DefaultNotificationAssistantPicker extends DefaultAppPickerFragment
} }
candidates.add(new DefaultAppInfo( candidates.add(new DefaultAppInfo(
mUserId, new ComponentName(info.packageName, info.name), null /* summary */)); mUserId, new ComponentName(info.packageName, info.name)));
} }
return candidates; return candidates;
} }

View File

@@ -47,7 +47,7 @@ public class DefaultSmsPreferenceController extends DefaultAppPreferenceControll
protected DefaultAppInfo getDefaultAppInfo() { protected DefaultAppInfo getDefaultAppInfo() {
final ComponentName app = SmsApplication.getDefaultSmsApplication(mContext, true); final ComponentName app = SmsApplication.getDefaultSmsApplication(mContext, true);
if (app != null) { if (app != null) {
return new DefaultAppInfo(mUserId, app, null /* summary */); return new DefaultAppInfo(mUserId, app);
} }
return null; return null;
} }

View File

@@ -54,7 +54,7 @@ import com.android.settings.applications.AppAndNotificationDashboardFragment;
import com.android.settings.applications.DrawOverlayDetails; import com.android.settings.applications.DrawOverlayDetails;
import com.android.settings.applications.InstalledAppDetails; import com.android.settings.applications.InstalledAppDetails;
import com.android.settings.applications.ManageApplications; 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.ManageDomainUrls;
import com.android.settings.applications.NotificationApps; import com.android.settings.applications.NotificationApps;
import com.android.settings.applications.PictureInPictureSettings; import com.android.settings.applications.PictureInPictureSettings;

View File

@@ -37,6 +37,7 @@ import com.android.settings.accounts.UserAndAccountDashboardFragment;
import com.android.settings.applications.AdvancedAppSettings; import com.android.settings.applications.AdvancedAppSettings;
import com.android.settings.applications.AppAndNotificationDashboardFragment; import com.android.settings.applications.AppAndNotificationDashboardFragment;
import com.android.settings.applications.SpecialAccessSettings; import com.android.settings.applications.SpecialAccessSettings;
import com.android.settings.applications.assist.ManageAssist;
import com.android.settings.backup.BackupSettingsFragment; import com.android.settings.backup.BackupSettingsFragment;
import com.android.settings.bluetooth.BluetoothSettings; import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
@@ -133,6 +134,7 @@ public final class SearchIndexableResources {
addIndex(BatterySaverSettings.class, addIndex(BatterySaverSettings.class,
R.xml.battery_saver_settings, R.drawable.ic_settings_battery); R.xml.battery_saver_settings, R.drawable.ic_settings_battery);
addIndex(AdvancedAppSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_applications); 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, addIndex(SpecialAccessSettings.class,
R.xml.special_access, R.drawable.ic_settings_applications); R.xml.special_access, R.drawable.ic_settings_applications);
addIndex(UserSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_multiuser); addIndex(UserSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_multiuser);

View File

@@ -1,158 +0,0 @@
package com.android.settings.voice;
import android.content.ComponentName;
import android.content.Context;
import android.provider.Settings;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import com.android.settings.AppListPreferenceWithSettings;
import com.android.settings.R;
import java.util.ArrayList;
import java.util.List;
public class VoiceInputListPreference extends AppListPreferenceWithSettings {
private VoiceInputHelper mHelper;
// The assist component name to restrict available voice inputs.
private ComponentName mAssistRestrict;
private final List<Integer> mAvailableIndexes = new ArrayList<>();
public VoiceInputListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setDialogTitle(R.string.choose_voice_input_title);
}
@Override
protected ListAdapter createListAdapter() {
return new CustomAdapter(getContext(), getEntries());
}
@Override
protected boolean persistString(String value) {
for (int i = 0; i < mHelper.mAvailableInteractionInfos.size(); ++i) {
VoiceInputHelper.InteractionInfo info = mHelper.mAvailableInteractionInfos.get(i);
if (info.key.equals(value)) {
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_INTERACTION_SERVICE, value);
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_RECOGNITION_SERVICE,
new ComponentName(info.service.packageName,
info.serviceInfo.getRecognitionService())
.flattenToShortString());
setSummary(getEntry());
setSettingsComponent(info.settings);
return true;
}
}
for (int i = 0; i < mHelper.mAvailableRecognizerInfos.size(); ++i) {
VoiceInputHelper.RecognizerInfo info = mHelper.mAvailableRecognizerInfos.get(i);
if (info.key.equals(value)) {
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_INTERACTION_SERVICE, "");
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.VOICE_RECOGNITION_SERVICE, value);
setSummary(getEntry());
setSettingsComponent(info.settings);
return true;
}
}
setSettingsComponent(null);
return true;
}
@Override
public void setPackageNames(CharSequence[] packageNames, CharSequence defaultPackageName) {
// Skip since all entries are created from |mHelper|.
}
public void setAssistRestrict(ComponentName assistRestrict) {
mAssistRestrict = assistRestrict;
}
public void refreshVoiceInputs() {
mHelper = new VoiceInputHelper(getContext());
mHelper.buildUi();
final String assistKey =
mAssistRestrict == null ? "" : mAssistRestrict.flattenToShortString();
mAvailableIndexes.clear();
List<CharSequence> entries = new ArrayList<>();
List<CharSequence> values = new ArrayList<>();
for (int i = 0; i < mHelper.mAvailableInteractionInfos.size(); ++i) {
VoiceInputHelper.InteractionInfo info = mHelper.mAvailableInteractionInfos.get(i);
entries.add(info.appLabel);
values.add(info.key);
if (info.key.contentEquals(assistKey)) {
mAvailableIndexes.add(i);
}
}
final boolean assitIsService = !mAvailableIndexes.isEmpty();
final int serviceCount = entries.size();
for (int i = 0; i < mHelper.mAvailableRecognizerInfos.size(); ++i) {
VoiceInputHelper.RecognizerInfo info = mHelper.mAvailableRecognizerInfos.get(i);
entries.add(info.label);
values.add(info.key);
if (!assitIsService) {
mAvailableIndexes.add(serviceCount + i);
}
}
setEntries(entries.toArray(new CharSequence[entries.size()]));
setEntryValues(values.toArray(new CharSequence[values.size()]));
if (mHelper.mCurrentVoiceInteraction != null) {
setValue(mHelper.mCurrentVoiceInteraction.flattenToShortString());
} else if (mHelper.mCurrentRecognizer != null) {
setValue(mHelper.mCurrentRecognizer.flattenToShortString());
} else {
setValue(null);
}
}
public ComponentName getCurrentService() {
if (mHelper.mCurrentVoiceInteraction != null) {
return mHelper.mCurrentVoiceInteraction;
} else if (mHelper.mCurrentRecognizer != null) {
return mHelper.mCurrentRecognizer;
} else {
return null;
}
}
private class CustomAdapter extends ArrayAdapter<CharSequence> {
public CustomAdapter(Context context, CharSequence[] objects) {
super(context, com.android.internal.R.layout.select_dialog_singlechoice_material,
android.R.id.text1, objects);
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
@Override
public boolean isEnabled(int position) {
return mAvailableIndexes.contains(position);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
view.setEnabled(isEnabled(position));
return view;
}
}
}

View File

@@ -65,7 +65,7 @@ com.android.settings.fuelgauge.BatteryHistoryDetail
com.android.settings.fuelgauge.PowerUsageSummary com.android.settings.fuelgauge.PowerUsageSummary
com.android.settings.applications.RunningServices com.android.settings.applications.RunningServices
com.android.settings.wifi.p2p.WifiP2pSettings 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.applications.ConfirmConvertToFbe
com.android.settings.deviceinfo.PublicVolumeSettings com.android.settings.deviceinfo.PublicVolumeSettings
com.android.settings.applications.InstalledAppDetails com.android.settings.applications.InstalledAppDetails

View File

@@ -16,7 +16,6 @@
package com.android.settings; package com.android.settings;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.IContentProvider; import android.content.IContentProvider;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@@ -26,6 +25,7 @@ import android.provider.Settings;
import com.android.settings.dashboard.SummaryLoader; import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowSecureSettings;
import com.android.settingslib.drawer.DashboardCategory; import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile; import com.android.settingslib.drawer.Tile;
import com.android.settingslib.drawer.TileUtils; import com.android.settingslib.drawer.TileUtils;
@@ -40,7 +40,6 @@ import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements; import org.robolectric.annotation.Implements;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
@@ -67,24 +66,6 @@ public class SecuritySettingsTest {
private SecuritySettings.SummaryProvider mSummaryProvider; private SecuritySettings.SummaryProvider mSummaryProvider;
@Implements(Settings.Secure.class)
public static class ShadowSecureSettings {
private static final Map<String, Object> 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) @Implements(com.android.settingslib.drawer.TileUtils.class)
public static class ShadowTileUtils { public static class ShadowTileUtils {
@Implementation @Implementation

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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<DefaultAssistPicker.Info> 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<DefaultAssistPicker.Info> 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<DefaultAssistPicker.Info> 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();
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -85,7 +85,7 @@ public class DefaultAppInfoTest {
public void initInfoWithComponent_shouldLoadInfo() { public void initInfoWithComponent_shouldLoadInfo() {
when(mComponentName.getPackageName()).thenReturn("com.android.settings"); when(mComponentName.getPackageName()).thenReturn("com.android.settings");
mInfo = new DefaultAppInfo(0 /* uid */, mComponentName, null /*summary */); mInfo = new DefaultAppInfo(0 /* uid */, mComponentName);
mInfo.getKey(); mInfo.getKey();
verify(mComponentName).flattenToString(); verify(mComponentName).flattenToString();

View File

@@ -36,7 +36,7 @@ public class SearchIndexProviderCodeInspector extends CodeInspector {
private static final String TAG = "SearchCodeInspector"; private static final String TAG = "SearchCodeInspector";
private static final String NOT_IMPLEMENTING_INDEXABLE_ERROR = 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 = private static final String NOT_CONTAINING_PROVIDER_OBJECT_ERROR =
"Indexable should have public field " + Index.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER "Indexable should have public field " + Index.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER
+ " but these are not:\n"; + " but these are not:\n";

View File

@@ -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<String, Object> 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;
}
}