diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 2ba7919a1b5..e3fa0700853 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -97,6 +97,7 @@ + diff --git a/res/values/strings.xml b/res/values/strings.xml index 7331d7265f7..45aadf38d12 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -393,6 +393,8 @@ Your devices Pair new device + + Allow device to pair and connect to bluetooth devices Currently connected diff --git a/res/xml/app_info_settings.xml b/res/xml/app_info_settings.xml index 664210be1c3..99c76b8936f 100644 --- a/res/xml/app_info_settings.xml +++ b/res/xml/app_info_settings.xml @@ -132,6 +132,17 @@ + + + + + + + + android:key="bt_pair_rename_devices" + android:title="@string/bluetooth_device_name" + android:summary="@string/summary_placeholder" /> - - + settings:keywords="@string/keywords_display_brightness_level" + settings:controller="com.android.settings.display.AutoBrightnessPreferenceController"> + + android:fragment="com.android.settings.gestures.GestureSettings" + settings:controller="com.android.settings.gestures.GesturesSettingPreferenceController"/> + android:order="-60" + settings:controller="com.android.settings.backup.BackupSettingsActivityPreferenceController"> @@ -44,14 +46,16 @@ android:title="@string/system_update_settings_list_item_title" android:summary="@string/summary_placeholder" android:icon="@drawable/ic_system_update" - android:order="-30"> + android:order="-30" + settings:controller="com.android.settings.deviceinfo.SystemUpdatePreferenceController"> + android:order="-31" + settings:controller="com.android.settings.deviceinfo.AdditionalSystemUpdatePreferenceController"> diff --git a/src/com/android/settings/PreviewSeekBarPreferenceFragment.java b/src/com/android/settings/PreviewSeekBarPreferenceFragment.java index 544999a3806..f5f3017af79 100644 --- a/src/com/android/settings/PreviewSeekBarPreferenceFragment.java +++ b/src/com/android/settings/PreviewSeekBarPreferenceFragment.java @@ -58,6 +58,7 @@ public abstract class PreviewSeekBarPreferenceFragment extends SettingsPreferenc private DotsPageIndicator mPageIndicator; private TextView mLabel; + private LabeledSeekBar mSeekBar; private View mLarger; private View mSmaller; @@ -110,19 +111,17 @@ public abstract class PreviewSeekBarPreferenceFragment extends SettingsPreferenc // seek bar. final int max = Math.max(1, mEntries.length - 1); - final LabeledSeekBar seekBar = (LabeledSeekBar) content.findViewById(R.id.seek_bar); - seekBar.setLabels(mEntries); - seekBar.setMax(max); - seekBar.setProgress(mInitialIndex); - seekBar.setOnSeekBarChangeListener(new onPreviewSeekBarChangeListener()); + mSeekBar = (LabeledSeekBar) content.findViewById(R.id.seek_bar); + mSeekBar.setLabels(mEntries); + mSeekBar.setMax(max); mSmaller = content.findViewById(R.id.smaller); mSmaller.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - final int progress = seekBar.getProgress(); + final int progress = mSeekBar.getProgress(); if (progress > 0) { - seekBar.setProgress(progress - 1, true); + mSeekBar.setProgress(progress - 1, true); } } }); @@ -131,9 +130,9 @@ public abstract class PreviewSeekBarPreferenceFragment extends SettingsPreferenc mLarger.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - final int progress = seekBar.getProgress(); - if (progress < seekBar.getMax()) { - seekBar.setProgress(progress + 1, true); + final int progress = mSeekBar.getProgress(); + if (progress < mSeekBar.getMax()) { + mSeekBar.setProgress(progress + 1, true); } } }); @@ -141,7 +140,7 @@ public abstract class PreviewSeekBarPreferenceFragment extends SettingsPreferenc if (mEntries.length == 1) { // The larger and smaller buttons will be disabled when we call // setPreviewLayer() later in this method. - seekBar.setEnabled(false); + mSeekBar.setEnabled(false); } final Context context = getContext(); @@ -172,6 +171,21 @@ public abstract class PreviewSeekBarPreferenceFragment extends SettingsPreferenc return root; } + @Override + public void onStart() { + super.onStart(); + // Set SeekBar listener here to avoid onProgressChanged() is called + // during onRestoreInstanceState(). + mSeekBar.setProgress(mCurrentIndex); + mSeekBar.setOnSeekBarChangeListener(new onPreviewSeekBarChangeListener()); + } + + @Override + public void onStop() { + super.onStop(); + mSeekBar.setOnSeekBarChangeListener(null); + } + /** * Creates new configuration based on the current position of the SeekBar. */ diff --git a/src/com/android/settings/applications/AppInfoDashboardFragment.java b/src/com/android/settings/applications/AppInfoDashboardFragment.java index d7d91f3f717..1e24df900fc 100755 --- a/src/com/android/settings/applications/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/AppInfoDashboardFragment.java @@ -45,9 +45,6 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.support.annotation.VisibleForTesting; -import android.support.v7.preference.Preference; -import android.support.v7.preference.PreferenceCategory; -import android.support.v7.preference.PreferenceScreen; import android.text.TextUtils; import android.util.Log; import android.view.Menu; @@ -64,6 +61,8 @@ import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; import com.android.settings.applications.appinfo.AppBatteryPreferenceController; import com.android.settings.applications.appinfo.AppDataUsagePreferenceController; +import com.android.settings.applications.appinfo.AppInstallerInfoPreferenceController; +import com.android.settings.applications.appinfo.AppInstallerPreferenceCategoryController; import com.android.settings.applications.appinfo.AppMemoryPreferenceController; import com.android.settings.applications.appinfo.AppNotificationPreferenceController; import com.android.settings.applications.appinfo.AppOpenByDefaultPreferenceController; @@ -77,9 +76,10 @@ import com.android.settings.applications.appinfo.DefaultPhoneShortcutPreferenceC import com.android.settings.applications.appinfo.DefaultSmsShortcutPreferenceController; import com.android.settings.applications.appinfo.DrawOverlayDetailPreferenceController; import com.android.settings.applications.appinfo.ExternalSourceDetailPreferenceController; +import com.android.settings.applications.appinfo.InstantAppButtonsPreferenceController; +import com.android.settings.applications.appinfo.InstantAppDomainsPreferenceController; import com.android.settings.applications.appinfo.PictureInPictureDetailPreferenceController; import com.android.settings.applications.appinfo.WriteSystemSettingsPreferenceController; -import com.android.settings.applications.instantapps.InstantAppButtonsController; import com.android.settings.applications.manageapplications.ManageApplications; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.dashboard.DashboardFragment; @@ -97,9 +97,9 @@ import com.android.settingslib.core.lifecycle.Lifecycle; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; -import java.util.Set; /** * Dashboard fragment to display application information from Settings. This activity presents @@ -136,10 +136,7 @@ public class AppInfoDashboardFragment extends DashboardFragment private static final int DLG_DISABLE = DLG_BASE + 2; private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3; private static final String KEY_HEADER = "header_view"; - private static final String KEY_INSTANT_APP_BUTTONS = "instant_app_buttons"; private static final String KEY_ACTION_BUTTONS = "action_buttons"; - private static final String KEY_INSTANT_APP_SUPPORTED_LINKS = - "instant_app_launch_supported_domain_urls"; public static final String ARG_PACKAGE_NAME = "package"; public static final String ARG_PACKAGE_UID = "uid"; @@ -172,7 +169,6 @@ public class AppInfoDashboardFragment extends DashboardFragment private boolean mShowUninstalled; private LayoutPreference mHeader; private boolean mUpdatedSysApp = false; - private AppDomainsPreference mInstantAppDomainsPreference; private boolean mDisableAfterUninstall; private List mCallbacks = new ArrayList<>(); @@ -180,7 +176,7 @@ public class AppInfoDashboardFragment extends DashboardFragment @VisibleForTesting ActionButtonPreference mActionButtons; - private InstantAppButtonsController mInstantAppButtonsController; + private InstantAppButtonsPreferenceController mInstantAppButtonPreferenceController; /** * Callback to invoke when app info has been changed. @@ -335,8 +331,6 @@ public class AppInfoDashboardFragment extends DashboardFragment } setHasOptionsMenu(true); - - addDynamicPrefs(); } @Override @@ -381,6 +375,10 @@ public class AppInfoDashboardFragment extends DashboardFragment controllers.add(new AppOpenByDefaultPreferenceController(context, this)); controllers.add(new AppPermissionPreferenceController(context, this, packageName)); controllers.add(new AppVersionPreferenceController(context, this)); + controllers.add(new InstantAppDomainsPreferenceController(context, this)); + final AppInstallerInfoPreferenceController appInstallerInfoPreferenceController = + new AppInstallerInfoPreferenceController(context, this, packageName); + controllers.add(appInstallerInfoPreferenceController); for (AbstractPreferenceController controller : controllers) { mCallbacks.add((Callback) controller); @@ -388,6 +386,9 @@ public class AppInfoDashboardFragment extends DashboardFragment // The following are controllers for preferences that don't need to refresh the preference // state when app state changes. + mInstantAppButtonPreferenceController = + new InstantAppButtonsPreferenceController(context, this, packageName); + controllers.add(mInstantAppButtonPreferenceController); controllers.add(new AppBatteryPreferenceController(context, this, packageName, lifecycle)); controllers.add(new AppMemoryPreferenceController(context, this, lifecycle)); controllers.add(new DefaultHomeShortcutPreferenceController(context, packageName)); @@ -407,6 +408,9 @@ public class AppInfoDashboardFragment extends DashboardFragment controllers.add(new PreferenceCategoryController( context, KEY_ADVANCED_APP_INFO_CATEGORY, advancedAppInfoControllers)); + controllers.add(new AppInstallerPreferenceCategoryController( + context, Arrays.asList(appInstallerInfoPreferenceController))); + return controllers; } @@ -444,8 +448,6 @@ public class AppInfoDashboardFragment extends DashboardFragment .styleActionBar(activity) .bindHeaderButtons(); - mInstantAppDomainsPreference = - (AppDomainsPreference) findPreference(KEY_INSTANT_APP_SUPPORTED_LINKS); } @Override @@ -536,24 +538,6 @@ public class AppInfoDashboardFragment extends DashboardFragment } } - /** - * Utility method to hide and show specific preferences based on whether the app being displayed - * is an Instant App or an installed app. - */ - @VisibleForTesting - void prepareInstantAppPrefs() { - final boolean isInstant = AppUtils.isInstant(mPackageInfo.applicationInfo); - if (isInstant) { - Set handledDomainSet = Utils.getHandledDomains(mPm, mPackageInfo.packageName); - String[] handledDomains = handledDomainSet.toArray(new String[handledDomainSet.size()]); - mInstantAppDomainsPreference.setTitles(handledDomains); - // Dummy values, unused in the implementation - mInstantAppDomainsPreference.setValues(new int[handledDomains.length]); - } else { - getPreferenceScreen().removePreference(mInstantAppDomainsPreference); - } - } - // Utility method to set application label and icon. private void setAppLabelAndIcon(PackageInfo pkgInfo) { final View appSnippet = mHeader.findViewById(R.id.entity_header); @@ -642,7 +626,6 @@ public class AppInfoDashboardFragment extends DashboardFragment checkForceStop(); setAppLabelAndIcon(mPackageInfo); initUninstallButtons(); - prepareInstantAppPrefs(); // Update the preference summaries. Activity context = getActivity(); @@ -676,7 +659,8 @@ public class AppInfoDashboardFragment extends DashboardFragment return true; } - protected AlertDialog createDialog(int id, int errorCode) { + @VisibleForTesting + AlertDialog createDialog(int id, int errorCode) { switch (id) { case DLG_DISABLE: return new AlertDialog.Builder(getActivity()) @@ -722,10 +706,7 @@ public class AppInfoDashboardFragment extends DashboardFragment .setNegativeButton(R.string.dlg_cancel, null) .create(); } - if (mInstantAppButtonsController != null) { - return mInstantAppButtonsController.createDialog(id); - } - return null; + return mInstantAppButtonPreferenceController.createDialog(id); } private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) { @@ -870,57 +851,6 @@ public class AppInfoDashboardFragment extends DashboardFragment || (mUserManager.isSplitSystemUser() && userCount == 2); } - private void addDynamicPrefs() { - if (UserManager.get(getContext()).isManagedProfile()) { - return; - } - addAppInstallerInfoPref(getPreferenceScreen()); - maybeAddInstantAppButtons(); - } - - private void addAppInstallerInfoPref(PreferenceScreen screen) { - String installerPackageName = - AppStoreUtil.getInstallerPackageName(getContext(), mPackageName); - - final CharSequence installerLabel = Utils.getApplicationLabel(getContext(), - installerPackageName); - if (installerLabel == null) { - return; - } - final int detailsStringId = AppUtils.isInstant(mPackageInfo.applicationInfo) - ? R.string.instant_app_details_summary - : R.string.app_install_details_summary; - PreferenceCategory category = new PreferenceCategory(getPrefContext()); - category.setTitle(R.string.app_install_details_group_title); - screen.addPreference(category); - Preference pref = new Preference(getPrefContext()); - pref.setTitle(R.string.app_install_details_title); - pref.setKey("app_info_store"); - pref.setSummary(getString(detailsStringId, installerLabel)); - - Intent intent = - AppStoreUtil.getAppStoreLink(getContext(), installerPackageName, mPackageName); - if (intent != null) { - pref.setIntent(intent); - } else { - pref.setEnabled(false); - } - category.addPreference(pref); - } - - @VisibleForTesting - void maybeAddInstantAppButtons() { - if (AppUtils.isInstant(mPackageInfo.applicationInfo)) { - LayoutPreference buttons = (LayoutPreference) findPreference(KEY_INSTANT_APP_BUTTONS); - mInstantAppButtonsController = mApplicationFeatureProvider - .newInstantAppButtonsController(this, - buttons.findViewById(R.id.instant_app_button_container), - id -> showDialogInner(id, 0)) - .setPackageName(mPackageName) - .show(); - } - } - private void onPackageRemoved() { getActivity().finishActivity(SUB_INFO_FRAGMENT); getActivity().finishAndRemoveTask(); @@ -1041,9 +971,9 @@ public class AppInfoDashboardFragment extends DashboardFragment mFinishing = true; } - private void showDialogInner(int id, int moveErrorCode) { + public void showDialogInner(int id, int moveErrorCode) { DialogFragment newFragment = - AppInfoBase.MyAlertDialogFragment.newInstance(id, moveErrorCode); + MyAlertDialogFragment.newInstance(id, moveErrorCode); newFragment.setTargetFragment(this, 0); newFragment.show(getFragmentManager(), "dialog " + id); } @@ -1094,8 +1024,8 @@ public class AppInfoDashboardFragment extends DashboardFragment public static void startAppInfoFragment(Class fragment, int titleRes, String pkg, int uid, Activity source, int request, int sourceMetricsCategory) { Bundle args = new Bundle(); - args.putString(AppInfoBase.ARG_PACKAGE_NAME, pkg); - args.putInt(AppInfoBase.ARG_PACKAGE_UID, uid); + args.putString(ARG_PACKAGE_NAME, pkg); + args.putInt(ARG_PACKAGE_UID, uid); Intent intent = Utils.onBuildStartFragmentIntent(source, fragment.getName(), args, null, titleRes, null, false, sourceMetricsCategory); @@ -1116,16 +1046,16 @@ public class AppInfoDashboardFragment extends DashboardFragment public Dialog onCreateDialog(Bundle savedInstanceState) { int id = getArguments().getInt(ARG_ID); int errorCode = getArguments().getInt("moveError"); - Dialog dialog = ((AppInfoBase) getTargetFragment()).createDialog(id, errorCode); + Dialog dialog = ((AppInfoDashboardFragment) getTargetFragment()) + .createDialog(id, errorCode); if (dialog == null) { throw new IllegalArgumentException("unknown id " + id); } return dialog; } - public static AppInfoBase.MyAlertDialogFragment newInstance(int id, int errorCode) { - AppInfoBase.MyAlertDialogFragment - dialogFragment = new AppInfoBase.MyAlertDialogFragment(); + public static MyAlertDialogFragment newInstance(int id, int errorCode) { + MyAlertDialogFragment dialogFragment = new MyAlertDialogFragment(); Bundle args = new Bundle(); args.putInt(ARG_ID, id); args.putInt("moveError", errorCode); diff --git a/src/com/android/settings/applications/AppStoreUtil.java b/src/com/android/settings/applications/AppStoreUtil.java index f9b95b0610b..13e551692c6 100644 --- a/src/com/android/settings/applications/AppStoreUtil.java +++ b/src/com/android/settings/applications/AppStoreUtil.java @@ -16,11 +16,9 @@ package com.android.settings.applications; - import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; -import android.net.Uri; import android.util.Log; // This class provides methods that help dealing with app stores. @@ -43,9 +41,6 @@ public class AppStoreUtil { } catch (IllegalArgumentException e) { Log.e(LOG_TAG, "Exception while retrieving the package installer of " + packageName, e); } - if (installerPackageName == null) { - return null; - } return installerPackageName; } diff --git a/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java b/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java new file mode 100644 index 00000000000..2449004b525 --- /dev/null +++ b/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java @@ -0,0 +1,69 @@ +/* + * 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.appinfo; + +import android.content.Context; +import android.content.Intent; +import android.os.UserManager; +import android.support.v7.preference.Preference; + +import com.android.settings.R; +import com.android.settings.Utils; +import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settings.applications.AppStoreUtil; +import com.android.settingslib.applications.AppUtils; + +public class AppInstallerInfoPreferenceController extends AppInfoPreferenceControllerBase { + + private static final String KEY_APP_INSTALLER_INFO = "app_info_store"; + + private final String mPackageName; + private final String mInstallerPackage; + private final CharSequence mInstallerLabel; + + public AppInstallerInfoPreferenceController(Context context, AppInfoDashboardFragment parent, + String packageName) { + super(context, parent, KEY_APP_INSTALLER_INFO); + mPackageName = packageName; + mInstallerPackage = AppStoreUtil.getInstallerPackageName(mContext, mPackageName); + mInstallerLabel = Utils.getApplicationLabel(mContext, mInstallerPackage); + } + + @Override + public int getAvailabilityStatus() { + if (UserManager.get(mContext).isManagedProfile()) { + return DISABLED_FOR_USER; + } + return mInstallerLabel!= null ? AVAILABLE : DISABLED_FOR_USER; + } + + @Override + public void updateState(Preference preference) { + final int detailsStringId = AppUtils.isInstant(mParent.getPackageInfo().applicationInfo) + ? R.string.instant_app_details_summary + : R.string.app_install_details_summary; + preference.setSummary(mContext.getString(detailsStringId, mInstallerLabel)); + + Intent intent = AppStoreUtil.getAppStoreLink(mContext, mInstallerPackage, mPackageName); + if (intent != null) { + preference.setIntent(intent); + } else { + preference.setEnabled(false); + } + } + +} diff --git a/src/com/android/settings/applications/appinfo/AppInstallerPreferenceCategoryController.java b/src/com/android/settings/applications/appinfo/AppInstallerPreferenceCategoryController.java new file mode 100644 index 00000000000..0e6ffe8dcd2 --- /dev/null +++ b/src/com/android/settings/applications/appinfo/AppInstallerPreferenceCategoryController.java @@ -0,0 +1,35 @@ +/* + * 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.appinfo; + +import android.content.Context; + +import com.android.settings.widget.PreferenceCategoryController; +import com.android.settingslib.core.AbstractPreferenceController; + +import java.util.List; + +public class AppInstallerPreferenceCategoryController extends PreferenceCategoryController { + + private static final String KEY_APP_INSTALLER_INFO_CATEGORY = "app_installer"; + + public AppInstallerPreferenceCategoryController(Context context, + List childrenControllers) { + super(context, KEY_APP_INSTALLER_INFO_CATEGORY, childrenControllers); + } + +} diff --git a/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java b/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java new file mode 100644 index 00000000000..e35fa76b85f --- /dev/null +++ b/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java @@ -0,0 +1,75 @@ +/* + * 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.appinfo; + +import android.app.AlertDialog; +import android.content.Context; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settings.applications.ApplicationFeatureProvider; +import com.android.settings.applications.LayoutPreference; +import com.android.settings.applications.instantapps.InstantAppButtonsController; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.applications.AppUtils; + +public class InstantAppButtonsPreferenceController extends BasePreferenceController { + + private static final String KEY_INSTANT_APP_BUTTONS = "instant_app_buttons"; + + private final AppInfoDashboardFragment mParent; + private final String mPackageName; + private InstantAppButtonsController mInstantAppButtonsController; + + public InstantAppButtonsPreferenceController(Context context, AppInfoDashboardFragment parent, + String packageName) { + super(context, KEY_INSTANT_APP_BUTTONS); + mParent = parent; + mPackageName = packageName; + } + + @Override + public int getAvailabilityStatus() { + return AppUtils.isInstant(mParent.getPackageInfo().applicationInfo) + ? AVAILABLE : DISABLED_FOR_USER; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + LayoutPreference buttons = + (LayoutPreference) screen.findPreference(KEY_INSTANT_APP_BUTTONS); + mInstantAppButtonsController = getApplicationFeatureProvider() + .newInstantAppButtonsController(mParent, + buttons.findViewById(R.id.instant_app_button_container), + id -> mParent.showDialogInner(id, 0)) + .setPackageName(mPackageName) + .show(); + } + + public AlertDialog createDialog(int id) { + return mInstantAppButtonsController.createDialog(id); + } + + @VisibleForTesting + ApplicationFeatureProvider getApplicationFeatureProvider() { + return FeatureFactory.getFactory(mContext).getApplicationFeatureProvider(mContext); + } +} diff --git a/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java b/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java new file mode 100644 index 00000000000..1d2229127da --- /dev/null +++ b/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.applications.appinfo; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.support.v7.preference.Preference; + +import com.android.settings.Utils; +import com.android.settings.applications.AppDomainsPreference; +import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settingslib.applications.AppUtils; + +import java.util.Set; + +public class InstantAppDomainsPreferenceController extends AppInfoPreferenceControllerBase { + + private static final String KEY_INSTANT_APP_SUPPORTED_LINKS = + "instant_app_launch_supported_domain_urls"; + + private PackageManager mPackageManager; + + public InstantAppDomainsPreferenceController(Context context, AppInfoDashboardFragment parent) { + super(context, parent, KEY_INSTANT_APP_SUPPORTED_LINKS); + mPackageManager = mContext.getPackageManager(); + } + + @Override + public int getAvailabilityStatus() { + return AppUtils.isInstant(mParent.getPackageInfo().applicationInfo) + ? AVAILABLE : DISABLED_FOR_USER; + } + + @Override + public void updateState(Preference preference) { + final AppDomainsPreference instantAppDomainsPreference = (AppDomainsPreference) preference; + final Set handledDomainSet = + Utils.getHandledDomains(mPackageManager, mParent.getPackageInfo().packageName); + final String[] handledDomains = + handledDomainSet.toArray(new String[handledDomainSet.size()]); + instantAppDomainsPreference.setTitles(handledDomains); + // Dummy values, unused in the implementation + instantAppDomainsPreference.setValues(new int[handledDomains.length]); + } + +} diff --git a/src/com/android/settings/applications/instantapps/InstantAppButtonsController.java b/src/com/android/settings/applications/instantapps/InstantAppButtonsController.java index 28e612c98ff..42474a8b297 100644 --- a/src/com/android/settings/applications/instantapps/InstantAppButtonsController.java +++ b/src/com/android/settings/applications/instantapps/InstantAppButtonsController.java @@ -21,7 +21,6 @@ import android.app.Fragment; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.pm.PackageManager; import android.os.UserHandle; import android.view.View; import android.widget.Button; diff --git a/src/com/android/settings/backup/BackupSettingsActivityPreferenceController.java b/src/com/android/settings/backup/BackupSettingsActivityPreferenceController.java index afc13b47d1c..7a7530cfc8a 100644 --- a/src/com/android/settings/backup/BackupSettingsActivityPreferenceController.java +++ b/src/com/android/settings/backup/BackupSettingsActivityPreferenceController.java @@ -22,31 +22,29 @@ import android.os.UserManager; import android.support.v7.preference.Preference; import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerMixin; import com.android.settingslib.core.AbstractPreferenceController; -public class BackupSettingsActivityPreferenceController extends - AbstractPreferenceController implements PreferenceControllerMixin { +public class BackupSettingsActivityPreferenceController extends BasePreferenceController { + private static final String TAG = "BackupSettingActivityPC"; + private static final String KEY_BACKUP_SETTINGS = "backup_settings"; - private static final String TAG = "BackupSettingActivityPC" ; private final UserManager mUm; private final BackupManager mBackupManager; public BackupSettingsActivityPreferenceController(Context context) { - super(context); + super(context, KEY_BACKUP_SETTINGS); mUm = (UserManager) context.getSystemService(Context.USER_SERVICE); mBackupManager = new BackupManager(context); } @Override - public boolean isAvailable() { - return mUm.isAdminUser(); - } - - @Override - public String getPreferenceKey() { - return KEY_BACKUP_SETTINGS; + public int getAvailabilityStatus() { + return mUm.isAdminUser() + ? AVAILABLE + : DISABLED_UNSUPPORTED; } @Override @@ -57,4 +55,4 @@ public class BackupSettingsActivityPreferenceController extends ? R.string.accessibility_feature_state_on : R.string.accessibility_feature_state_off); } -} +} \ No newline at end of file diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceNamePreferenceController.java b/src/com/android/settings/bluetooth/BluetoothDeviceNamePreferenceController.java index 8b07bcbb510..2d0ce6021aa 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceNamePreferenceController.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceNamePreferenceController.java @@ -29,10 +29,9 @@ import android.text.TextUtils; import android.util.Log; import com.android.settings.R; -import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.BasePreferenceController; import com.android.settingslib.bluetooth.LocalBluetoothAdapter; import com.android.settingslib.bluetooth.LocalBluetoothManager; -import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; @@ -41,8 +40,8 @@ import com.android.settingslib.core.lifecycle.events.OnStop; /** * Controller that shows and updates the bluetooth device name */ -public class BluetoothDeviceNamePreferenceController extends AbstractPreferenceController - implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop { +public class BluetoothDeviceNamePreferenceController extends BasePreferenceController implements + LifecycleObserver, OnStart, OnStop { private static final String TAG = "BluetoothNamePrefCtrl"; public static final String KEY_DEVICE_NAME = "device_name"; @@ -62,12 +61,22 @@ public class BluetoothDeviceNamePreferenceController extends AbstractPreferenceC return; } mLocalAdapter = mLocalManager.getBluetoothAdapter(); - lifecycle.addObserver(this); + + if (lifecycle != null) { + lifecycle.addObserver(this); + } + } + + /** + * Constructor exclusively used for Slice. + */ + public BluetoothDeviceNamePreferenceController(Context context) { + this(context, (Lifecycle) null); } @VisibleForTesting BluetoothDeviceNamePreferenceController(Context context, LocalBluetoothAdapter localAdapter) { - super(context); + super(context, KEY_DEVICE_NAME); mLocalAdapter = localAdapter; } @@ -89,8 +98,8 @@ public class BluetoothDeviceNamePreferenceController extends AbstractPreferenceC } @Override - public boolean isAvailable() { - return mLocalAdapter != null; + public int getAvailabilityStatus() { + return mLocalAdapter != null ? AVAILABLE : DISABLED_UNSUPPORTED; } @Override diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java b/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java index b64da26f1e7..69eefcf2d21 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java @@ -20,6 +20,7 @@ import android.app.Fragment; import android.content.Context; import android.support.annotation.VisibleForTesting; import android.support.v7.preference.Preference; +import android.text.TextUtils; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.core.instrumentation.MetricsFeatureProvider; @@ -30,29 +31,39 @@ import com.android.settingslib.core.lifecycle.Lifecycle; public class BluetoothDeviceRenamePreferenceController extends BluetoothDeviceNamePreferenceController { - public static final String PREF_KEY = "bt_rename_device"; - private final Fragment mFragment; + private String mPrefKey; private MetricsFeatureProvider mMetricsFeatureProvider; - public BluetoothDeviceRenamePreferenceController(Context context, Fragment fragment, - Lifecycle lifecycle) { + public BluetoothDeviceRenamePreferenceController(Context context, String prefKey, + Fragment fragment, Lifecycle lifecycle) { super(context, lifecycle); + mPrefKey = prefKey; mFragment = fragment; mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); } + /** + * Constructor exclusively used for Slice. + */ + public BluetoothDeviceRenamePreferenceController(Context context, String prefKey) { + super(context, (Lifecycle) null); + mPrefKey = prefKey; + mFragment = null; + } + @VisibleForTesting - BluetoothDeviceRenamePreferenceController(Context context, Fragment fragment, + BluetoothDeviceRenamePreferenceController(Context context, String prefKey, Fragment fragment, LocalBluetoothAdapter localAdapter) { super(context, localAdapter); + mPrefKey = prefKey; mFragment = fragment; mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); } @Override public String getPreferenceKey() { - return PREF_KEY; + return mPrefKey; } @Override @@ -62,7 +73,7 @@ public class BluetoothDeviceRenamePreferenceController extends @Override public boolean handlePreferenceTreeClick(Preference preference) { - if (PREF_KEY.equals(preference.getKey())) { + if (TextUtils.equals(mPrefKey, preference.getKey()) && mFragment != null) { mMetricsFeatureProvider.action(mContext, MetricsProto.MetricsEvent.ACTION_BLUETOOTH_RENAME); LocalDeviceNameDialogFragment.newInstance() diff --git a/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceController.java index d1492e46ddd..331907b2259 100644 --- a/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceController.java +++ b/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceController.java @@ -38,6 +38,7 @@ import com.android.settingslib.core.lifecycle.events.OnResume; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; +//TODO(b/69926683): remove this controller in Android P. public class BluetoothMasterSwitchPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin, OnSummaryChangeListener, LifecycleObserver, OnResume, OnPause, OnStart, OnStop { diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDetail.java b/src/com/android/settings/bluetooth/BluetoothPairingDetail.java index a9756a6951b..5e003feda13 100644 --- a/src/com/android/settings/bluetooth/BluetoothPairingDetail.java +++ b/src/com/android/settings/bluetooth/BluetoothPairingDetail.java @@ -46,9 +46,8 @@ public class BluetoothPairingDetail extends DeviceListPreferenceFragment impleme static final String KEY_AVAIL_DEVICES = "available_devices"; @VisibleForTesting static final String KEY_FOOTER_PREF = "footer_preference"; + private static final String KEY_RENAME_DEVICES = "bt_pair_rename_devices"; - @VisibleForTesting - BluetoothDeviceNamePreferenceController mDeviceNamePrefController; @VisibleForTesting BluetoothProgressCategory mAvailableDevicesCategory; @VisibleForTesting @@ -195,10 +194,10 @@ public class BluetoothPairingDetail extends DeviceListPreferenceFragment impleme @Override protected List getPreferenceControllers(Context context) { - List controllers = new ArrayList<>(); - mDeviceNamePrefController = new BluetoothDeviceNamePreferenceController(context, - getLifecycle()); - controllers.add(mDeviceNamePrefController); + final List controllers = new ArrayList<>(); + controllers.add( + new BluetoothDeviceRenamePreferenceController(context, KEY_RENAME_DEVICES, this, + getLifecycle())); return controllers; } diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java index 72d8023983a..3acd4775102 100644 --- a/src/com/android/settings/bluetooth/BluetoothSettings.java +++ b/src/com/android/settings/bluetooth/BluetoothSettings.java @@ -73,6 +73,7 @@ public class BluetoothSettings extends DeviceListPreferenceFragment implements I static final String KEY_PAIRED_DEVICES = "paired_devices"; @VisibleForTesting static final String KEY_FOOTER_PREF = "footer_preference"; + private static final String KEY_RENAME_DEVICES = "bt_rename_device"; @VisibleForTesting PreferenceGroup mPairedDevicesCategory; @@ -369,7 +370,9 @@ public class BluetoothSettings extends DeviceListPreferenceFragment implements I controllers.add(mDeviceNamePrefController); controllers.add(mPairingPrefController); controllers.add(new BluetoothFilesPreferenceController(context)); - controllers.add(new BluetoothDeviceRenamePreferenceController(context, this, lifecycle)); + controllers.add( + new BluetoothDeviceRenamePreferenceController(context, KEY_RENAME_DEVICES, this, + lifecycle)); return controllers; } diff --git a/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java new file mode 100644 index 00000000000..3482ee2d91e --- /dev/null +++ b/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java @@ -0,0 +1,161 @@ +/* + * 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.bluetooth; + +import android.content.Context; +import android.support.v14.preference.SwitchPreference; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settings.core.TogglePreferenceController; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.widget.SwitchWidgetController; +import com.android.settingslib.RestrictedLockUtils; +import com.android.settingslib.bluetooth.LocalBluetoothAdapter; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; + +/** + * PreferenceController to update of bluetooth {@link SwitchPreference}. It will + * + * 1. Invoke the user toggle + * 2. Listen to the update from {@link LocalBluetoothManager} + */ +public class BluetoothSwitchPreferenceController extends TogglePreferenceController + implements LifecycleObserver, OnStart, OnStop { + + public static final String KEY_TOGGLE_BLUETOOTH = "toggle_bluetooth_switch"; + + private LocalBluetoothManager mBluetoothManager; + private SwitchPreference mBtPreference; + private BluetoothEnabler mBluetoothEnabler; + private RestrictionUtils mRestrictionUtils; + @VisibleForTesting + LocalBluetoothAdapter mBluetoothAdapter; + + public BluetoothSwitchPreferenceController(Context context) { + this(context, Utils.getLocalBtManager(context), new RestrictionUtils()); + } + + @VisibleForTesting + public BluetoothSwitchPreferenceController(Context context, + LocalBluetoothManager bluetoothManager, RestrictionUtils restrictionUtils) { + super(context, KEY_TOGGLE_BLUETOOTH); + mBluetoothManager = bluetoothManager; + mRestrictionUtils = restrictionUtils; + + if (mBluetoothManager != null) { + mBluetoothAdapter = mBluetoothManager.getBluetoothAdapter(); + } + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mBtPreference = (SwitchPreference) screen.findPreference(KEY_TOGGLE_BLUETOOTH); + mBluetoothEnabler = new BluetoothEnabler(mContext, + new SwitchController(mBtPreference), + FeatureFactory.getFactory(mContext).getMetricsFeatureProvider(), mBluetoothManager, + MetricsEvent.ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE, + mRestrictionUtils); + } + + @Override + public int getAvailabilityStatus() { + return mBluetoothAdapter != null ? AVAILABLE : DISABLED_UNSUPPORTED; + } + + @Override + public void onStart() { + mBluetoothEnabler.resume(mContext); + } + + @Override + public void onStop() { + mBluetoothEnabler.pause(); + } + + @Override + public boolean isChecked() { + return mBluetoothAdapter != null ? mBluetoothAdapter.isEnabled() : false; + } + + @Override + public void setChecked(boolean isChecked) { + if (mBluetoothAdapter != null) { + mBluetoothAdapter.setBluetoothEnabled(isChecked); + } + } + + /** + * Control the switch inside {@link SwitchPreference} + */ + @VisibleForTesting + class SwitchController extends SwitchWidgetController implements + Preference.OnPreferenceChangeListener { + private SwitchPreference mSwitchPreference; + + public SwitchController(SwitchPreference switchPreference) { + mSwitchPreference = switchPreference; + } + + @Override + public void updateTitle(boolean isChecked) { + } + + @Override + public void startListening() { + mSwitchPreference.setOnPreferenceChangeListener(this); + } + + @Override + public void stopListening() { + mSwitchPreference.setOnPreferenceChangeListener(null); + } + + @Override + public void setChecked(boolean checked) { + mSwitchPreference.setChecked(checked); + } + + @Override + public boolean isChecked() { + return mSwitchPreference.isChecked(); + } + + @Override + public void setEnabled(boolean enabled) { + mSwitchPreference.setEnabled(enabled); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (mListener != null) { + return mListener.onSwitchToggled((Boolean) newValue); + } + return false; + } + + @Override + public void setDisabledByAdmin(RestrictedLockUtils.EnforcedAdmin admin) { + mBtPreference.setEnabled(admin == null); + } + } +} diff --git a/src/com/android/settings/bluetooth/DeviceProfilesSettings.java b/src/com/android/settings/bluetooth/DeviceProfilesSettings.java old mode 100755 new mode 100644 index eae2f2963b4..d13a85f388b --- a/src/com/android/settings/bluetooth/DeviceProfilesSettings.java +++ b/src/com/android/settings/bluetooth/DeviceProfilesSettings.java @@ -172,7 +172,11 @@ public final class DeviceProfilesSettings extends InstrumentedDialogFragment imp mProfileContainer.removeAllViews(); for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) { CheckBox pref = createProfilePreference(profile); - mProfileContainer.addView(pref); + // MAP and PBAP profiles would be added based on permission access + if (!((profile instanceof PbapServerProfile) || + (profile instanceof MapProfile))) { + mProfileContainer.addView(pref); + } if (profile instanceof A2dpProfile) { BluetoothDevice device = mCachedDevice.getDevice(); @@ -191,6 +195,7 @@ public final class DeviceProfilesSettings extends InstrumentedDialogFragment imp } final int pbapPermission = mCachedDevice.getPhonebookPermissionChoice(); + Log.d(TAG, "addPreferencesForProfiles: pbapPermission = " + pbapPermission); // Only provide PBAP cabability if the client device has requested PBAP. if (pbapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN) { final PbapServerProfile psp = mManager.getProfileManager().getPbapProfile(); @@ -200,6 +205,7 @@ public final class DeviceProfilesSettings extends InstrumentedDialogFragment imp final MapProfile mapProfile = mManager.getProfileManager().getMapProfile(); final int mapPermission = mCachedDevice.getMessagePermissionChoice(); + Log.d(TAG, "addPreferencesForProfiles: mapPermission = " + mapPermission); if (mapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN) { CheckBox mapPreference = createProfilePreference(mapProfile); mProfileContainer.addView(mapPreference); @@ -251,15 +257,6 @@ public final class DeviceProfilesSettings extends InstrumentedDialogFragment imp private void onProfileClicked(LocalBluetoothProfile profile, CheckBox profilePref) { BluetoothDevice device = mCachedDevice.getDevice(); - if (KEY_PBAP_SERVER.equals(profilePref.getTag())) { - final int newPermission = mCachedDevice.getPhonebookPermissionChoice() - == CachedBluetoothDevice.ACCESS_ALLOWED ? CachedBluetoothDevice.ACCESS_REJECTED - : CachedBluetoothDevice.ACCESS_ALLOWED; - mCachedDevice.setPhonebookPermissionChoice(newPermission); - profilePref.setChecked(newPermission == CachedBluetoothDevice.ACCESS_ALLOWED); - return; - } - if (!profilePref.isChecked()) { // Recheck it, until the dialog is done. profilePref.setChecked(true); @@ -268,6 +265,12 @@ public final class DeviceProfilesSettings extends InstrumentedDialogFragment imp if (profile instanceof MapProfile) { mCachedDevice.setMessagePermissionChoice(BluetoothDevice.ACCESS_ALLOWED); } + if (profile instanceof PbapServerProfile) { + mCachedDevice.setPhonebookPermissionChoice(BluetoothDevice.ACCESS_ALLOWED); + refreshProfilePreference(profilePref, profile); + // PBAP server is not preffered profile and cannot initiate connection, so return + return; + } if (profile.isPreferred(device)) { // profile is preferred but not connected: disable auto-connect if (profile instanceof PanProfile) { @@ -301,10 +304,17 @@ public final class DeviceProfilesSettings extends InstrumentedDialogFragment imp DialogInterface.OnClickListener disconnectListener = new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { - device.disconnect(profile); - profile.setPreferred(device.getDevice(), false); - if (profile instanceof MapProfile) { - device.setMessagePermissionChoice(BluetoothDevice.ACCESS_REJECTED); + + // Disconnect only when user has selected OK otherwise ignore + if (which == DialogInterface.BUTTON_POSITIVE) { + device.disconnect(profile); + profile.setPreferred(device.getDevice(), false); + if (profile instanceof MapProfile) { + device.setMessagePermissionChoice(BluetoothDevice.ACCESS_REJECTED); + } + if (profile instanceof PbapServerProfile) { + device.setPhonebookPermissionChoice(BluetoothDevice.ACCESS_REJECTED); + } } refreshProfilePreference(findProfile(profile.toString()), profile); } @@ -342,6 +352,19 @@ public final class DeviceProfilesSettings extends InstrumentedDialogFragment imp for (LocalBluetoothProfile profile : mCachedDevice.getRemovedProfiles()) { CheckBox profilePref = findProfile(profile.toString()); if (profilePref != null) { + + if (profile instanceof PbapServerProfile) { + final int pbapPermission = mCachedDevice.getPhonebookPermissionChoice(); + Log.d(TAG, "refreshProfiles: pbapPermission = " + pbapPermission); + if (pbapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN) + continue; + } + if (profile instanceof MapProfile) { + final int mapPermission = mCachedDevice.getMessagePermissionChoice(); + Log.d(TAG, "refreshProfiles: mapPermission = " + mapPermission); + if (mapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN) + continue; + } Log.d(TAG, "Removing " + profile.toString() + " from profile list"); mProfileContainer.removeView(profilePref); } diff --git a/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java index ea93fef0303..a4f6e5cf17d 100644 --- a/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java +++ b/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java @@ -24,6 +24,7 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.bluetooth.BluetoothFilesPreferenceController; import com.android.settings.bluetooth.BluetoothMasterSwitchPreferenceController; +import com.android.settings.bluetooth.BluetoothSwitchPreferenceController; import com.android.settings.bluetooth.Utils; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.deviceinfo.UsbBackend; @@ -83,10 +84,8 @@ public class AdvancedConnectedDeviceDashboardFragment extends DashboardFragment mUsbPrefController = new UsbModePreferenceController(context, new UsbBackend(context)); lifecycle.addObserver(mUsbPrefController); controllers.add(mUsbPrefController); - final BluetoothMasterSwitchPreferenceController bluetoothPreferenceController = - new BluetoothMasterSwitchPreferenceController( - context, Utils.getLocalBtManager(context), this, - (SettingsActivity) getActivity()); + final BluetoothSwitchPreferenceController bluetoothPreferenceController = + new BluetoothSwitchPreferenceController(context); lifecycle.addObserver(bluetoothPreferenceController); controllers.add(bluetoothPreferenceController); diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java index a0b5cb85697..3cccc15782f 100644 --- a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java +++ b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java @@ -19,6 +19,7 @@ import android.support.annotation.VisibleForTesting; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceGroup; import android.support.v7.preference.PreferenceScreen; + import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.bluetooth.BluetoothDeviceUpdater; import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater; @@ -42,26 +43,31 @@ public class ConnectedDeviceGroupController extends AbstractPreferenceController @VisibleForTesting PreferenceGroup mPreferenceGroup; private BluetoothDeviceUpdater mBluetoothDeviceUpdater; + private ConnectedUsbDeviceUpdater mConnectedUsbDeviceUpdater; public ConnectedDeviceGroupController(DashboardFragment fragment, Lifecycle lifecycle) { super(fragment.getContext()); - init(lifecycle, new ConnectedBluetoothDeviceUpdater(fragment, this)); + init(lifecycle, new ConnectedBluetoothDeviceUpdater(fragment, this), + new ConnectedUsbDeviceUpdater(fragment.getContext(), this)); } @VisibleForTesting ConnectedDeviceGroupController(DashboardFragment fragment, Lifecycle lifecycle, - BluetoothDeviceUpdater bluetoothDeviceUpdater) { + BluetoothDeviceUpdater bluetoothDeviceUpdater, + ConnectedUsbDeviceUpdater connectedUsbDeviceUpdater) { super(fragment.getContext()); - init(lifecycle, bluetoothDeviceUpdater); + init(lifecycle, bluetoothDeviceUpdater, connectedUsbDeviceUpdater); } @Override public void onStart() { mBluetoothDeviceUpdater.registerCallback(); + mConnectedUsbDeviceUpdater.registerCallback(); } @Override public void onStop() { + mConnectedUsbDeviceUpdater.unregisterCallback(); mBluetoothDeviceUpdater.unregisterCallback(); } @@ -70,8 +76,10 @@ public class ConnectedDeviceGroupController extends AbstractPreferenceController super.displayPreference(screen); mPreferenceGroup = (PreferenceGroup) screen.findPreference(KEY); mPreferenceGroup.setVisible(false); + mBluetoothDeviceUpdater.setPrefContext(screen.getContext()); mBluetoothDeviceUpdater.forceUpdate(); + mConnectedUsbDeviceUpdater.initUsbPreference(screen.getContext()); } @Override @@ -100,10 +108,12 @@ public class ConnectedDeviceGroupController extends AbstractPreferenceController } } - private void init(Lifecycle lifecycle, BluetoothDeviceUpdater bluetoothDeviceUpdater) { + private void init(Lifecycle lifecycle, BluetoothDeviceUpdater bluetoothDeviceUpdater, + ConnectedUsbDeviceUpdater connectedUsbDeviceUpdater) { if (lifecycle != null) { lifecycle.addObserver(this); } mBluetoothDeviceUpdater = bluetoothDeviceUpdater; + mConnectedUsbDeviceUpdater = connectedUsbDeviceUpdater; } } diff --git a/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdater.java b/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdater.java new file mode 100644 index 00000000000..0468b0f5a35 --- /dev/null +++ b/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdater.java @@ -0,0 +1,92 @@ +/* + * 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.connecteddevice; + +import android.content.Context; +import android.content.Intent; +import android.support.annotation.VisibleForTesting; + +import com.android.settings.R; +import com.android.settings.deviceinfo.UsbBackend; +import com.android.settings.deviceinfo.UsbModeChooserActivity; +import com.android.settings.widget.GearPreference; + +/** + * Controller to maintain connected usb device + */ +public class ConnectedUsbDeviceUpdater { + private Context mContext; + private UsbBackend mUsbBackend; + private DevicePreferenceCallback mDevicePreferenceCallback; + @VisibleForTesting + GearPreference mUsbPreference; + @VisibleForTesting + UsbConnectionBroadcastReceiver mUsbReceiver; + + private UsbConnectionBroadcastReceiver.UsbConnectionListener mUsbConnectionListener = + (connected) -> { + if (connected) { + mUsbPreference.setSummary( + UsbModePreferenceController.getSummary(mUsbBackend.getCurrentMode())); + mDevicePreferenceCallback.onDeviceAdded(mUsbPreference); + } else { + mDevicePreferenceCallback.onDeviceRemoved(mUsbPreference); + } + }; + + public ConnectedUsbDeviceUpdater(Context context, + DevicePreferenceCallback devicePreferenceCallback) { + this(context, devicePreferenceCallback, new UsbBackend(context)); + } + + @VisibleForTesting + ConnectedUsbDeviceUpdater(Context context, DevicePreferenceCallback devicePreferenceCallback, + UsbBackend usbBackend) { + mContext = context; + mDevicePreferenceCallback = devicePreferenceCallback; + mUsbBackend = usbBackend; + mUsbReceiver = new UsbConnectionBroadcastReceiver(context, mUsbConnectionListener); + } + + public void registerCallback() { + // This method could handle multiple register + mUsbReceiver.register(); + } + + public void unregisterCallback() { + mUsbReceiver.unregister(); + } + + public void initUsbPreference(Context context) { + mUsbPreference = new GearPreference(context, null /* AttributeSet */); + mUsbPreference.setTitle(R.string.usb_pref); + mUsbPreference.setIcon(R.drawable.ic_usb); + mUsbPreference.setSelectable(false); + mUsbPreference.setOnGearClickListener((GearPreference p) -> { + final Intent intent = new Intent(mContext, UsbModeChooserActivity.class); + mContext.startActivity(intent); + }); + + forceUpdate(); + } + + private void forceUpdate() { + // Register so we can get the connection state from sticky intent. + //TODO(b/70336520): Use an API to get data instead of sticky intent + mUsbReceiver.register(); + mUsbConnectionListener.onUsbConnectionChanged(mUsbReceiver.isConnected()); + } +} diff --git a/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiver.java b/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiver.java new file mode 100644 index 00000000000..07a76915af4 --- /dev/null +++ b/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiver.java @@ -0,0 +1,76 @@ +/* + * 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.connecteddevice; + + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.usb.UsbManager; + +/** + * Receiver to receive usb update and use {@link UsbConnectionListener} to invoke callback + */ +public class UsbConnectionBroadcastReceiver extends BroadcastReceiver { + private Context mContext; + private UsbConnectionListener mUsbConnectionListener; + private boolean mListeningToUsbEvents; + private boolean mConnected; + + public UsbConnectionBroadcastReceiver(Context context, + UsbConnectionListener usbConnectionListener) { + mContext = context; + mUsbConnectionListener = usbConnectionListener; + } + + @Override + public void onReceive(Context context, Intent intent) { + mConnected = intent != null + && intent.getExtras().getBoolean(UsbManager.USB_CONNECTED); + if (mUsbConnectionListener != null) { + mUsbConnectionListener.onUsbConnectionChanged(mConnected); + } + } + + public void register() { + if (!mListeningToUsbEvents) { + final IntentFilter intentFilter = new IntentFilter(UsbManager.ACTION_USB_STATE); + final Intent intent = mContext.registerReceiver(this, intentFilter); + mConnected = intent != null + && intent.getExtras().getBoolean(UsbManager.USB_CONNECTED); + mListeningToUsbEvents = true; + } + } + + public void unregister() { + if (mListeningToUsbEvents) { + mContext.unregisterReceiver(this); + mListeningToUsbEvents = false; + } + } + + public boolean isConnected() { + return mConnected; + } + + /** + * Interface definition for a callback to be invoked when usb connection is changed. + */ + interface UsbConnectionListener { + void onUsbConnectionChanged(boolean connected); + } +} diff --git a/src/com/android/settings/connecteddevice/UsbModePreferenceController.java b/src/com/android/settings/connecteddevice/UsbModePreferenceController.java index a6cb9be1e7f..869352006c5 100644 --- a/src/com/android/settings/connecteddevice/UsbModePreferenceController.java +++ b/src/com/android/settings/connecteddevice/UsbModePreferenceController.java @@ -15,17 +15,12 @@ */ package com.android.settings.connecteddevice; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.hardware.usb.UsbManager; -import android.support.annotation.VisibleForTesting; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceScreen; -import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.R; +import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.deviceinfo.UsbBackend; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.LifecycleObserver; @@ -44,19 +39,21 @@ public class UsbModePreferenceController extends AbstractPreferenceController public UsbModePreferenceController(Context context, UsbBackend usbBackend) { super(context); mUsbBackend = usbBackend; - mUsbReceiver = new UsbConnectionBroadcastReceiver(); + mUsbReceiver = new UsbConnectionBroadcastReceiver(mContext, (connected) -> { + updateSummary(mUsbPreference); + }); } @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); mUsbPreference = screen.findPreference(KEY_USB_MODE); - updataSummary(mUsbPreference); + updateSummary(mUsbPreference); } @Override public void updateState(Preference preference) { - updataSummary(preference); + updateSummary(preference); } @Override @@ -79,8 +76,7 @@ public class UsbModePreferenceController extends AbstractPreferenceController mUsbReceiver.register(); } - @VisibleForTesting - int getSummary(int mode) { + public static int getSummary(int mode) { switch (mode) { case UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_NONE: return R.string.usb_summary_charging_only; @@ -96,11 +92,11 @@ public class UsbModePreferenceController extends AbstractPreferenceController return 0; } - private void updataSummary(Preference preference) { - updataSummary(preference, mUsbBackend.getCurrentMode()); + private void updateSummary(Preference preference) { + updateSummary(preference, mUsbBackend.getCurrentMode()); } - private void updataSummary(Preference preference, int mode) { + private void updateSummary(Preference preference, int mode) { if (preference != null) { if (mUsbReceiver.isConnected()) { preference.setEnabled(true); @@ -112,40 +108,4 @@ public class UsbModePreferenceController extends AbstractPreferenceController } } - private class UsbConnectionBroadcastReceiver extends BroadcastReceiver { - private boolean mListeningToUsbEvents; - private boolean mConnected; - - @Override - public void onReceive(Context context, Intent intent) { - boolean connected = intent != null - && intent.getExtras().getBoolean(UsbManager.USB_CONNECTED); - if (connected != mConnected) { - mConnected = connected; - updataSummary(mUsbPreference); - } - } - - public void register() { - if (!mListeningToUsbEvents) { - IntentFilter intentFilter = new IntentFilter(UsbManager.ACTION_USB_STATE); - Intent intent = mContext.registerReceiver(this, intentFilter); - mConnected = intent != null - && intent.getExtras().getBoolean(UsbManager.USB_CONNECTED); - mListeningToUsbEvents = true; - } - } - - public void unregister() { - if (mListeningToUsbEvents) { - mContext.unregisterReceiver(this); - mListeningToUsbEvents = false; - } - } - - public boolean isConnected() { - return mConnected; - } - } - } diff --git a/src/com/android/settings/deviceinfo/AdditionalSystemUpdatePreferenceController.java b/src/com/android/settings/deviceinfo/AdditionalSystemUpdatePreferenceController.java index 06bdb3fae81..f91ed4e69a5 100644 --- a/src/com/android/settings/deviceinfo/AdditionalSystemUpdatePreferenceController.java +++ b/src/com/android/settings/deviceinfo/AdditionalSystemUpdatePreferenceController.java @@ -17,26 +17,23 @@ package com.android.settings.deviceinfo; import android.content.Context; +import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerMixin; import com.android.settingslib.core.AbstractPreferenceController; -public class AdditionalSystemUpdatePreferenceController extends - AbstractPreferenceController implements PreferenceControllerMixin { +public class AdditionalSystemUpdatePreferenceController extends BasePreferenceController { private static final String KEY_UPDATE_SETTING = "additional_system_update_settings"; public AdditionalSystemUpdatePreferenceController(Context context) { - super(context); + super(context, KEY_UPDATE_SETTING); } @Override - public boolean isAvailable() { + public int getAvailabilityStatus() { return mContext.getResources().getBoolean( - com.android.settings.R.bool.config_additional_system_update_setting_enable); + com.android.settings.R.bool.config_additional_system_update_setting_enable) + ? AVAILABLE + : DISABLED_UNSUPPORTED; } - - @Override - public String getPreferenceKey() { - return KEY_UPDATE_SETTING; - } -} +} \ No newline at end of file diff --git a/src/com/android/settings/deviceinfo/SystemUpdatePreferenceController.java b/src/com/android/settings/deviceinfo/SystemUpdatePreferenceController.java index d8a64a82113..92c33d86e4b 100644 --- a/src/com/android/settings/deviceinfo/SystemUpdatePreferenceController.java +++ b/src/com/android/settings/deviceinfo/SystemUpdatePreferenceController.java @@ -30,11 +30,9 @@ import android.util.Log; import com.android.settings.R; import com.android.settings.Utils; -import com.android.settings.core.PreferenceControllerMixin; -import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settings.core.BasePreferenceController; -public class SystemUpdatePreferenceController extends AbstractPreferenceController implements - PreferenceControllerMixin { +public class SystemUpdatePreferenceController extends BasePreferenceController { private static final String TAG = "SysUpdatePrefContr"; @@ -42,19 +40,16 @@ public class SystemUpdatePreferenceController extends AbstractPreferenceControll private final UserManager mUm; - public SystemUpdatePreferenceController(Context context, UserManager um) { - super(context); - mUm = um; + public SystemUpdatePreferenceController(Context context) { + super(context, KEY_SYSTEM_UPDATE_SETTINGS); + mUm = UserManager.get(context); } @Override - public boolean isAvailable() { - return mUm.isAdminUser(); - } - - @Override - public String getPreferenceKey() { - return KEY_SYSTEM_UPDATE_SETTINGS; + public int getAvailabilityStatus() { + return mUm.isAdminUser() + ? AVAILABLE + : DISABLED_UNSUPPORTED; } @Override @@ -62,14 +57,14 @@ public class SystemUpdatePreferenceController extends AbstractPreferenceControll super.displayPreference(screen); if (isAvailable()) { Utils.updatePreferenceToSpecificActivityOrRemove(mContext, screen, - KEY_SYSTEM_UPDATE_SETTINGS, + getPreferenceKey(), Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY); } } @Override public boolean handlePreferenceTreeClick(Preference preference) { - if (KEY_SYSTEM_UPDATE_SETTINGS.equals(preference.getKey())) { + if (TextUtils.equals(getPreferenceKey(), preference.getKey())) { CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(CARRIER_CONFIG_SERVICE); PersistableBundle b = configManager.getConfig(); @@ -108,4 +103,4 @@ public class SystemUpdatePreferenceController extends AbstractPreferenceControll mContext.getApplicationContext().sendBroadcast(intent); } } -} +} \ No newline at end of file diff --git a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java index f7a2b9a58a6..d0f4080a4e2 100644 --- a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java +++ b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java @@ -20,16 +20,18 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.os.PowerManager; import android.support.annotation.VisibleForTesting; import com.android.settings.Utils; /** * Use this broadcastReceiver to listen to the battery change, and it will invoke - * {@link OnBatteryChangedListener} if any of the following happens: + * {@link OnBatteryChangedListener} if any of the followings has been changed: * - * 1. Battery level has been changed - * 2. Battery status has been changed + * 1. Battery level(e.g. 100%->99%) + * 2. Battery status(e.g. plugged->unplugged) + * 3. Battery saver(e.g. off->on) */ public class BatteryBroadcastReceiver extends BroadcastReceiver { @@ -58,8 +60,11 @@ public class BatteryBroadcastReceiver extends BroadcastReceiver { } public void register() { - final Intent intent = mContext.registerReceiver(this, - new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); + intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); + + final Intent intent = mContext.registerReceiver(this, intentFilter); updateBatteryStatus(intent, true /* forceUpdate */); } @@ -68,15 +73,18 @@ public class BatteryBroadcastReceiver extends BroadcastReceiver { } private void updateBatteryStatus(Intent intent, boolean forceUpdate) { - if (intent != null && mBatteryListener != null && Intent.ACTION_BATTERY_CHANGED.equals( - intent.getAction())) { - String batteryLevel = Utils.getBatteryPercentage(intent); - String batteryStatus = Utils.getBatteryStatus( - mContext.getResources(), intent); - if (forceUpdate || !batteryLevel.equals(mBatteryLevel) || !batteryStatus.equals( - mBatteryStatus)) { - mBatteryLevel = batteryLevel; - mBatteryStatus = batteryStatus; + if (intent != null && mBatteryListener != null) { + if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { + final String batteryLevel = Utils.getBatteryPercentage(intent); + final String batteryStatus = Utils.getBatteryStatus( + mContext.getResources(), intent); + if (forceUpdate || !batteryLevel.equals(mBatteryLevel) || !batteryStatus.equals( + mBatteryStatus)) { + mBatteryLevel = batteryLevel; + mBatteryStatus = batteryStatus; + mBatteryListener.onBatteryChanged(); + } + } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(intent.getAction())) { mBatteryListener.onBatteryChanged(); } } diff --git a/src/com/android/settings/gestures/GesturesSettingPreferenceController.java b/src/com/android/settings/gestures/GesturesSettingPreferenceController.java index d1b47b21301..819b12861c2 100644 --- a/src/com/android/settings/gestures/GesturesSettingPreferenceController.java +++ b/src/com/android/settings/gestures/GesturesSettingPreferenceController.java @@ -23,27 +23,26 @@ import android.support.v7.preference.Preference; import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.AbstractPreferenceController; import java.util.List; -public class GesturesSettingPreferenceController extends AbstractPreferenceController - implements PreferenceControllerMixin { - - private static final String KEY_GESTURES_SETTINGS = "gesture_settings"; - +public class GesturesSettingPreferenceController extends BasePreferenceController { private final AssistGestureFeatureProvider mFeatureProvider; private List mGestureControllers; + private static final String KEY_GESTURES_SETTINGS = "gesture_settings"; + public GesturesSettingPreferenceController(Context context) { - super(context); + super(context, KEY_GESTURES_SETTINGS); mFeatureProvider = FeatureFactory.getFactory(context).getAssistGestureFeatureProvider(); } @Override - public boolean isAvailable() { + public int getAvailabilityStatus() { if (mGestureControllers == null) { mGestureControllers = GestureSettings.buildPreferenceControllers(mContext, null /* lifecycle */, new AmbientDisplayConfiguration(mContext)); @@ -52,12 +51,9 @@ public class GesturesSettingPreferenceController extends AbstractPreferenceContr for (AbstractPreferenceController controller : mGestureControllers) { isAvailable = isAvailable || controller.isAvailable(); } - return isAvailable; - } - - @Override - public String getPreferenceKey() { - return KEY_GESTURES_SETTINGS; + return isAvailable + ? AVAILABLE + : DISABLED_UNSUPPORTED; } @Override @@ -83,5 +79,4 @@ public class GesturesSettingPreferenceController extends AbstractPreferenceContr } preference.setSummary(summary); } - -} +} \ No newline at end of file diff --git a/src/com/android/settings/search/XmlParserUtils.java b/src/com/android/settings/search/XmlParserUtils.java index b4ffc532f7c..27c5cd36593 100644 --- a/src/com/android/settings/search/XmlParserUtils.java +++ b/src/com/android/settings/search/XmlParserUtils.java @@ -71,6 +71,10 @@ public class XmlParserUtils { return getData(context, attrs, R.styleable.Preference, R.styleable.Preference_keywords); } + public static String getController(Context context, AttributeSet attrs) { + return getData(context, attrs, R.styleable.Preference, R.styleable.Preference_controller); + } + public static int getDataIcon(Context context, AttributeSet attrs) { final TypedArray ta = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Preference); diff --git a/src/com/android/settings/system/SystemDashboardFragment.java b/src/com/android/settings/system/SystemDashboardFragment.java index c01bfcc08ff..323a2d42cd0 100644 --- a/src/com/android/settings/system/SystemDashboardFragment.java +++ b/src/com/android/settings/system/SystemDashboardFragment.java @@ -16,8 +16,12 @@ package com.android.settings.system; import android.content.Context; +import android.os.Bundle; import android.os.UserManager; import android.provider.SearchIndexableResource; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceGroup; +import android.support.v7.preference.PreferenceScreen; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; @@ -40,6 +44,17 @@ public class SystemDashboardFragment extends DashboardFragment { private static final String KEY_RESET = "reset_dashboard"; + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + final PreferenceScreen screen = getPreferenceScreen(); + // We do not want to display an advanced button if only one setting is hidden + if (getVisiblePreferenceCount(screen) == screen.getInitialExpandedChildrenCount() + 1) { + screen.setInitialExpandedChildrenCount(Integer.MAX_VALUE); + } + } + @Override public int getMetricsCategory() { return MetricsProto.MetricsEvent.SETTINGS_SYSTEM_CATEGORY; @@ -67,13 +82,26 @@ public class SystemDashboardFragment extends DashboardFragment { private static List buildPreferenceControllers(Context context) { final List controllers = new ArrayList<>(); - controllers.add(new SystemUpdatePreferenceController(context, UserManager.get(context))); + controllers.add(new SystemUpdatePreferenceController(context)); controllers.add(new AdditionalSystemUpdatePreferenceController(context)); controllers.add(new BackupSettingsActivityPreferenceController(context)); controllers.add(new GesturesSettingPreferenceController(context)); return controllers; } + private int getVisiblePreferenceCount(PreferenceGroup group) { + int visibleCount = 0; + for (int i = 0; i < group.getPreferenceCount(); i++) { + final Preference preference = group.getPreference(i); + if (preference instanceof PreferenceGroup) { + visibleCount += getVisiblePreferenceCount((PreferenceGroup) preference); + } else if (preference.isVisible()) { + visibleCount++; + } + } + return visibleCount; + } + /** * For Search. */ @@ -88,17 +116,18 @@ public class SystemDashboardFragment extends DashboardFragment { } @Override - public List getPreferenceControllers(Context context) { + public List getPreferenceControllers( + Context context) { return buildPreferenceControllers(context); } @Override public List getNonIndexableKeys(Context context) { List keys = super.getNonIndexableKeys(context); - keys.add((new BackupSettingsActivityPreferenceController(context) - .getPreferenceKey())); + keys.add((new BackupSettingsActivityPreferenceController( + context).getPreferenceKey())); keys.add(KEY_RESET); return keys; } }; -} +} \ No newline at end of file diff --git a/tests/robotests/res/xml-mcc999/about_legal.xml b/tests/robotests/res/xml-mcc999/about_legal.xml index 53a2b897391..3e008cb7bad 100644 --- a/tests/robotests/res/xml-mcc999/about_legal.xml +++ b/tests/robotests/res/xml-mcc999/about_legal.xml @@ -30,5 +30,6 @@ + android:title="bears_bears_bears" + settings:controller="mind_flayer"/> \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/applications/AppInfoDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/applications/AppInfoDashboardFragmentTest.java index d710d7c1953..e742549e720 100644 --- a/tests/robotests/src/com/android/settings/applications/AppInfoDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/applications/AppInfoDashboardFragmentTest.java @@ -23,13 +23,10 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.AlertDialog; import android.app.AppOpsManager; -import android.app.Fragment; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -37,16 +34,10 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.UserManager; -import android.support.v7.preference.Preference; -import android.support.v7.preference.PreferenceManager; -import android.support.v7.preference.PreferenceScreen; -import android.view.View; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.TestConfig; -import com.android.settings.applications.instantapps.InstantAppButtonsController; -import com.android.settings.applications.instantapps.InstantAppButtonsController.ShowDialogDelegate; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.widget.ActionButtonPreferenceTest; @@ -246,101 +237,6 @@ public final class AppInfoDashboardFragmentTest { verify(mAppDetail.mActionButtons).setButton2Visible(false); } - @Test - public void instantApps_buttonControllerHandlesDialog() { - InstantAppButtonsController mockController = mock(InstantAppButtonsController.class); - ReflectionHelpers.setField( - mAppDetail, "mInstantAppButtonsController", mockController); - // Make sure first that button controller is not called for supported dialog id - AlertDialog mockDialog = mock(AlertDialog.class); - when(mockController.createDialog(InstantAppButtonsController.DLG_CLEAR_APP)) - .thenReturn(mockDialog); - assertThat(mAppDetail.createDialog(InstantAppButtonsController.DLG_CLEAR_APP, 0)) - .isEqualTo(mockDialog); - verify(mockController).createDialog(InstantAppButtonsController.DLG_CLEAR_APP); - } - - // A helper class for testing the InstantAppButtonsController - it lets us look up the - // preference associated with a key for instant app buttons and get back a mock - // LayoutPreference (to avoid a null pointer exception). - public static class InstalledAppDetailsWithMockInstantButtons extends InstalledAppDetails { - @Mock - private LayoutPreference mInstantButtons; - - public InstalledAppDetailsWithMockInstantButtons() { - super(); - MockitoAnnotations.initMocks(this); - } - - @Override - public Preference findPreference(CharSequence key) { - if (key == "instant_app_buttons") { - return mInstantButtons; - } - return super.findPreference(key); - } - } - - @Test - public void instantApps_instantSpecificButtons() { - // Make this app appear to be instant. - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> true)); - final PackageInfo packageInfo = mock(PackageInfo.class); - - final InstalledAppDetailsWithMockInstantButtons - fragment = new InstalledAppDetailsWithMockInstantButtons(); - ReflectionHelpers.setField(fragment, "mPackageInfo", packageInfo); - ReflectionHelpers.setField(fragment, "mApplicationFeatureProvider", - mFeatureFactory.applicationFeatureProvider); - - final InstantAppButtonsController buttonsController = - mock(InstantAppButtonsController.class); - when(buttonsController.setPackageName(nullable(String.class))) - .thenReturn(buttonsController); - when(mFeatureFactory.applicationFeatureProvider.newInstantAppButtonsController( - nullable(Fragment.class), nullable(View.class), nullable(ShowDialogDelegate.class))) - .thenReturn(buttonsController); - - fragment.maybeAddInstantAppButtons(); - verify(buttonsController).setPackageName(nullable(String.class)); - verify(buttonsController).show(); - } - - @Test - public void instantApps_removeCorrectPref() { - PreferenceScreen mockPreferenceScreen = mock(PreferenceScreen.class); - PreferenceManager mockPreferenceManager = mock(PreferenceManager.class); - AppDomainsPreference mockAppDomainsPref = mock(AppDomainsPreference.class); - PackageInfo mockPackageInfo = mock(PackageInfo.class); - PackageManager mockPackageManager = mock(PackageManager.class); - ReflectionHelpers.setField( - mAppDetail, "mInstantAppDomainsPreference", mockAppDomainsPref); - ReflectionHelpers.setField( - mAppDetail, "mPreferenceManager", mockPreferenceManager); - ReflectionHelpers.setField( - mAppDetail, "mPackageInfo", mockPackageInfo); - ReflectionHelpers.setField( - mAppDetail, "mPm", mockPackageManager); - when(mockPreferenceManager.getPreferenceScreen()).thenReturn(mockPreferenceScreen); - - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> false)); - mAppDetail.prepareInstantAppPrefs(); - - // For the non instant case we remove the app domain pref, and leave the launch pref - verify(mockPreferenceScreen).removePreference(mockAppDomainsPref); - - // For the instant app case we remove the launch preff, and leave the app domain pref - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> true)); - - mAppDetail.prepareInstantAppPrefs(); - // Will be 1 still due to above call - verify(mockPreferenceScreen, times(1)) - .removePreference(mockAppDomainsPref); - } - @Test public void onActivityResult_uninstalledUpdates_shouldInvalidateOptionsMenu() { doReturn(true).when(mAppDetail).refreshUi(); diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java new file mode 100644 index 00000000000..ffbc8f55954 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java @@ -0,0 +1,147 @@ +/* + * 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.appinfo; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.UserManager; +import android.support.v7.preference.Preference; + +import com.android.settings.TestConfig; +import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +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; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class AppInstallerInfoPreferenceControllerTest { + + @Mock + private UserManager mUserManager; + @Mock + private PackageManager mPackageManager; + @Mock + private ApplicationInfo mAppInfo; + @Mock + private AppInfoDashboardFragment mFragment; + @Mock + private Preference mPreference; + + private Context mContext; + private AppInstallerInfoPreferenceController mController; + + @Before + public void setUp() throws PackageManager.NameNotFoundException { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + final String installerPackage = "Installer1"; + when(mPackageManager.getInstallerPackageName(anyString())).thenReturn(installerPackage); + when(mPackageManager.getApplicationInfo(eq(installerPackage), anyInt())) + .thenReturn(mAppInfo); + mController = new AppInstallerInfoPreferenceController(mContext, mFragment, "Package1"); + } + + @Test + public void getAvailabilityStatus_managedProfile_shouldReturnDisabled() { + when(mUserManager.isManagedProfile()).thenReturn(true); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER); + } + + @Test + public void getAvailabilityStatus_noAppLabel_shouldReturnDisabled() { + when(mUserManager.isManagedProfile()).thenReturn(false); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER); + } + + @Test + public void getAvailabilityStatus_hasAppLabel_shouldReturnAvailable() { + when(mUserManager.isManagedProfile()).thenReturn(false); + when(mAppInfo.loadLabel(mPackageManager)).thenReturn("Label1"); + mController = new AppInstallerInfoPreferenceController(mContext, mFragment, "Package1"); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE); + } + + @Test + public void updateState_shouldSetSummary() { + final PackageInfo packageInfo = mock(PackageInfo.class); + packageInfo.applicationInfo = mAppInfo; + when(mFragment.getPackageInfo()).thenReturn(packageInfo); + + mController.updateState(mPreference); + + verify(mPreference).setSummary(any()); + } + + @Test + public void updateState_noAppStoreLink_shouldDisablePreference() { + final PackageInfo packageInfo = mock(PackageInfo.class); + packageInfo.applicationInfo = mAppInfo; + when(mFragment.getPackageInfo()).thenReturn(packageInfo); + when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(null); + + mController.updateState(mPreference); + + verify(mPreference).setEnabled(false); + } + + @Test + public void updateState_hasAppStoreLink_shouldSetPreferenceIntent() { + final PackageInfo packageInfo = mock(PackageInfo.class); + packageInfo.applicationInfo = mAppInfo; + when(mFragment.getPackageInfo()).thenReturn(packageInfo); + final ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.activityInfo = new ActivityInfo(); + resolveInfo.activityInfo.packageName = "Pkg1"; + resolveInfo.activityInfo.name = "Name1"; + when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo); + + mController.updateState(mPreference); + + verify(mPreference, never()).setEnabled(false); + verify(mPreference).setIntent(any(Intent.class)); + } + +} diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java new file mode 100644 index 00000000000..121659538a4 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java @@ -0,0 +1,133 @@ +/* + * 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.appinfo; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.AlertDialog; +import android.app.Fragment; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.support.v7.preference.PreferenceScreen; +import android.view.View; + +import com.android.settings.TestConfig; +import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settings.applications.LayoutPreference; +import com.android.settings.applications.instantapps.InstantAppButtonsController; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.applications.AppUtils; +import com.android.settingslib.applications.instantapps.InstantAppDataProvider; + +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; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class InstantAppButtonsPreferenceControllerTest { + + @Mock + private PackageManager mPackageManager; + @Mock + private ApplicationInfo mAppInfo; + @Mock + private AppInfoDashboardFragment mFragment; + + private Context mContext; + private InstantAppButtonsPreferenceController mController; + private FakeFeatureFactory mFeatureFactory; + + @Before + public void setUp() throws PackageManager.NameNotFoundException { + MockitoAnnotations.initMocks(this); + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mContext = spy(RuntimeEnvironment.application); + final PackageInfo packageInfo = mock(PackageInfo.class); + packageInfo.applicationInfo = mAppInfo; + when(mFragment.getPackageInfo()).thenReturn(packageInfo); + mController = + spy(new InstantAppButtonsPreferenceController(mContext, mFragment, "Package1")); + } + + @Test + public void getAvailabilityStatus_notInstantApp_shouldReturnDisabled() { + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> false)); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER); + } + + @Test + public void getAvailabilityStatus_isInstantApp_shouldReturnAvailable() { + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> true)); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE); + } + + @Test + public void displayPreference_shouldSetPreferenceTitle() { + final PreferenceScreen screen = mock(PreferenceScreen.class); + final LayoutPreference preference = mock(LayoutPreference.class); + when(screen.findPreference(mController.getPreferenceKey())).thenReturn(preference); + when(mController.getApplicationFeatureProvider()) + .thenReturn(mFeatureFactory.applicationFeatureProvider); + final InstantAppButtonsController buttonsController = + mock(InstantAppButtonsController.class); + when(buttonsController.setPackageName(nullable(String.class))) + .thenReturn(buttonsController); + when(mFeatureFactory.applicationFeatureProvider.newInstantAppButtonsController( + nullable(Fragment.class), nullable(View.class), + nullable(InstantAppButtonsController.ShowDialogDelegate.class))) + .thenReturn(buttonsController); + + mController.displayPreference(screen); + + verify(buttonsController).setPackageName(nullable(String.class)); + verify(buttonsController).show(); + } + + @Test + public void createDialog_shouldReturnDialogFromButtonController() { + final InstantAppButtonsController buttonsController = + mock(InstantAppButtonsController.class); + ReflectionHelpers.setField( + mController, "mInstantAppButtonsController", buttonsController); + final AlertDialog mockDialog = mock(AlertDialog.class); + when(buttonsController.createDialog(InstantAppButtonsController.DLG_CLEAR_APP)) + .thenReturn(mockDialog); + + assertThat(mController.createDialog(InstantAppButtonsController.DLG_CLEAR_APP)) + .isEqualTo(mockDialog); + } + +} diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceControllerTest.java new file mode 100644 index 00000000000..f1776e88e7d --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceControllerTest.java @@ -0,0 +1,115 @@ +/* + * 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.appinfo; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.IntentFilterVerificationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.util.ArraySet; + +import com.android.settings.TestConfig; +import com.android.settings.applications.AppDomainsPreference; +import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.applications.AppUtils; +import com.android.settingslib.applications.instantapps.InstantAppDataProvider; + +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 java.util.ArrayList; +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class InstantAppDomainsPreferenceControllerTest { + + @Mock + private PackageManager mPackageManager; + @Mock + private ApplicationInfo mAppInfo; + @Mock + private AppInfoDashboardFragment mFragment; + @Mock + private AppDomainsPreference mPreference; + + private Context mContext; + private InstantAppDomainsPreferenceController mController; + + @Before + public void setUp() throws PackageManager.NameNotFoundException { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + final PackageInfo packageInfo = mock(PackageInfo.class); + packageInfo.applicationInfo = mAppInfo; + packageInfo.packageName = "Package1"; + when(mFragment.getPackageInfo()).thenReturn(packageInfo); + mController = new InstantAppDomainsPreferenceController(mContext, mFragment); + } + + @Test + public void getAvailabilityStatus_notInstantApp_shouldReturnDisabled() { + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> false)); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER); + } + + @Test + public void getAvailabilityStatus_isInstantApp_shouldReturnAvailable() { + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> true)); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE); + } + + @Test + public void updateState_shouldSetPreferenceTitle() { + final String[] domain = { "Domain1" }; + final ArraySet domains = new ArraySet<>(); + domains.add(domain[0]); + final List infoList = new ArrayList<>(); + final IntentFilterVerificationInfo info = + new IntentFilterVerificationInfo("Package1", domains); + infoList.add(info); + + when(mPackageManager.getIntentFilterVerifications("Package1")).thenReturn(infoList); + + mController.updateState(mPreference); + + verify(mPreference).setTitles(domain); + } + +} diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceControllerTest.java index cde95cd5b16..62a0d42177d 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceControllerTest.java @@ -47,6 +47,7 @@ import org.robolectric.annotation.Config; public class BluetoothDeviceRenamePreferenceControllerTest { private static final String DEVICE_NAME = "Nightshade"; + private static final String PREF_KEY = "bt_rename_devices"; @Mock private LocalBluetoothAdapter mLocalAdapter; @@ -66,10 +67,10 @@ public class BluetoothDeviceRenamePreferenceControllerTest { mContext = spy(RuntimeEnvironment.application); mPreference = new Preference(mContext); - mPreference.setKey(BluetoothDeviceRenamePreferenceController.PREF_KEY); + mPreference.setKey(PREF_KEY); mController = new BluetoothDeviceRenamePreferenceController( - mContext, mFragment, mLocalAdapter); + mContext, PREF_KEY, mFragment, mLocalAdapter); } @Test diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceControllerTest.java new file mode 100644 index 00000000000..aa9d26624e4 --- /dev/null +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceControllerTest.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.bluetooth; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.support.v14.preference.SwitchPreference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.TestConfig; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.bluetooth.LocalBluetoothAdapter; +import com.android.settingslib.bluetooth.LocalBluetoothManager; + +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; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class BluetoothSwitchPreferenceControllerTest { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private LocalBluetoothManager mBluetoothManager; + @Mock + private PreferenceScreen mScreen; + @Mock + private SwitchPreference mPreference; + @Mock + private RestrictionUtils mRestrictionUtils; + @Mock + private LocalBluetoothAdapter mLocalBluetoothAdapter; + + private Context mContext; + private BluetoothSwitchPreferenceController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application.getApplicationContext()); + FakeFeatureFactory.setupForTest(); + + mController = new BluetoothSwitchPreferenceController( + mContext, mBluetoothManager, mRestrictionUtils); + when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference); + when(mPreference.getKey()).thenReturn(mController.getPreferenceKey()); + } + + @Test + public void testGetAvailabilityStatus_adapterNull_returnDisabled() { + mController.mBluetoothAdapter = null; + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.DISABLED_UNSUPPORTED); + } + + @Test + public void testGetAvailabilityStatus_adapterExisted_returnAvailable() { + mController.mBluetoothAdapter = mLocalBluetoothAdapter; + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.AVAILABLE); + } + + @Test + public void testOnStart_shouldRegisterPreferenceChangeListener() { + mController.displayPreference(mScreen); + mController.onStart(); + + verify(mPreference).setOnPreferenceChangeListener( + any(BluetoothSwitchPreferenceController.SwitchController.class)); + } + + @Test + public void testOnStop_shouldRegisterPreferenceChangeListener() { + mController.displayPreference(mScreen); + mController.onStart(); + + mController.onStop(); + + verify(mPreference).setOnPreferenceChangeListener(null); + } + + @Test + public void testIsChecked_adapterNull_returnFalse() { + mController.mBluetoothAdapter = null; + + assertThat(mController.isChecked()).isFalse(); + } + + @Test + public void testIsChecked_adapterExisted_returnFromAdapter() { + mController.mBluetoothAdapter = mLocalBluetoothAdapter; + doReturn(true).when(mLocalBluetoothAdapter).isEnabled(); + + assertThat(mController.isChecked()).isTrue(); + } + + @Test + public void testSetChecked_adapterExisted() { + mController.mBluetoothAdapter = mLocalBluetoothAdapter; + + mController.setChecked(true); + + verify(mLocalBluetoothAdapter).setBluetoothEnabled(true); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java index f9efc0bf64d..aa5eb67342f 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java @@ -37,6 +37,7 @@ import com.android.settingslib.core.lifecycle.Lifecycle; 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; @@ -45,13 +46,17 @@ import org.robolectric.annotation.Config; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class ConnectedDeviceGroupControllerTest { + private static final String PREFERENCE_KEY_1 = "pref_key_1"; + @Mock private DashboardFragment mDashboardFragment; @Mock private ConnectedBluetoothDeviceUpdater mConnectedBluetoothDeviceUpdater; @Mock - private PreferenceScreen mPreferenceScreen; + private ConnectedUsbDeviceUpdater mConnectedUsbDeviceUpdater; @Mock + private PreferenceScreen mPreferenceScreen; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) private PreferenceManager mPreferenceManager; private PreferenceGroup mPreferenceGroup; @@ -66,30 +71,33 @@ public class ConnectedDeviceGroupControllerTest { mContext = RuntimeEnvironment.application; mPreference = new Preference(mContext); + mPreference.setKey(PREFERENCE_KEY_1); mLifecycle = new Lifecycle(() -> mLifecycle); mPreferenceGroup = spy(new PreferenceScreen(mContext, null)); doReturn(mPreferenceManager).when(mPreferenceGroup).getPreferenceManager(); doReturn(mContext).when(mDashboardFragment).getContext(); mConnectedDeviceGroupController = new ConnectedDeviceGroupController(mDashboardFragment, - mLifecycle, mConnectedBluetoothDeviceUpdater); + mLifecycle, mConnectedBluetoothDeviceUpdater, mConnectedUsbDeviceUpdater); mConnectedDeviceGroupController.mPreferenceGroup = mPreferenceGroup; } @Test - public void testOnDeviceAdded_firstAdd_becomeVisible() { + public void testOnDeviceAdded_firstAdd_becomeVisibleAndPreferenceAdded() { mConnectedDeviceGroupController.onDeviceAdded(mPreference); assertThat(mPreferenceGroup.isVisible()).isTrue(); + assertThat(mPreferenceGroup.findPreference(PREFERENCE_KEY_1)).isEqualTo(mPreference); } @Test - public void testOnDeviceRemoved_lastRemove_becomeInvisible() { + public void testOnDeviceRemoved_lastRemove_becomeInvisibleAndPreferenceRemoved() { mPreferenceGroup.addPreference(mPreference); mConnectedDeviceGroupController.onDeviceRemoved(mPreference); assertThat(mPreferenceGroup.isVisible()).isFalse(); + assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0); } @Test @@ -117,9 +125,11 @@ public class ConnectedDeviceGroupControllerTest { // register the callback in onStart() mLifecycle.handleLifecycleEvent(android.arch.lifecycle.Lifecycle.Event.ON_START); verify(mConnectedBluetoothDeviceUpdater).registerCallback(); + verify(mConnectedUsbDeviceUpdater).registerCallback(); // unregister the callback in onStop() mLifecycle.handleLifecycleEvent(android.arch.lifecycle.Lifecycle.Event.ON_STOP); verify(mConnectedBluetoothDeviceUpdater).unregisterCallback(); + verify(mConnectedUsbDeviceUpdater).unregisterCallback(); } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdaterTest.java new file mode 100644 index 00000000000..16cd3a7a94f --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedUsbDeviceUpdaterTest.java @@ -0,0 +1,89 @@ +/* + * 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.connecteddevice; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + +import android.content.Context; + +import com.android.settings.R; +import com.android.settings.TestConfig; +import com.android.settings.deviceinfo.UsbBackend; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +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; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class ConnectedUsbDeviceUpdaterTest { + private Context mContext; + private ConnectedUsbDeviceUpdater mDeviceUpdater; + + @Mock + private UsbConnectionBroadcastReceiver mUsbReceiver; + @Mock + private DevicePreferenceCallback mDevicePreferenceCallback; + @Mock + private UsbBackend mUsbBackend; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = RuntimeEnvironment.application; + mDeviceUpdater = new ConnectedUsbDeviceUpdater(mContext, mDevicePreferenceCallback, + mUsbBackend); + mDeviceUpdater.mUsbReceiver = mUsbReceiver; + } + + @Test + public void testInitUsbPreference_preferenceInit() { + mDeviceUpdater.initUsbPreference(mContext); + + assertThat(mDeviceUpdater.mUsbPreference.getTitle()).isEqualTo("USB"); + assertThat(mDeviceUpdater.mUsbPreference.getIcon()).isEqualTo(mContext.getDrawable( + R.drawable.ic_usb)); + assertThat(mDeviceUpdater.mUsbPreference.isSelectable()).isFalse(); + } + + @Test + public void testInitUsbPreference_usbConnected_preferenceAdded() { + doReturn(true).when(mUsbReceiver).isConnected(); + + mDeviceUpdater.initUsbPreference(mContext); + + verify(mDevicePreferenceCallback).onDeviceAdded(mDeviceUpdater.mUsbPreference); + } + + @Test + public void testInitUsbPreference_usbDisconnected_preferenceRemoved() { + doReturn(false).when(mUsbReceiver).isConnected(); + + mDeviceUpdater.initUsbPreference(mContext); + + verify(mDevicePreferenceCallback).onDeviceRemoved(mDeviceUpdater.mUsbPreference); + } + +} \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiverTest.java new file mode 100644 index 00000000000..06bd5b7834d --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/UsbConnectionBroadcastReceiverTest.java @@ -0,0 +1,126 @@ +/* + * 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.connecteddevice; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.hardware.usb.UsbManager; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +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.shadows.ShadowApplication; + +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class UsbConnectionBroadcastReceiverTest { + private Context mContext; + private UsbConnectionBroadcastReceiver mReceiver; + private ShadowApplication mShadowApplication; + + @Mock + private UsbConnectionBroadcastReceiver.UsbConnectionListener mListener; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mShadowApplication = ShadowApplication.getInstance(); + mContext = RuntimeEnvironment.application; + mReceiver = new UsbConnectionBroadcastReceiver(mContext, mListener); + } + + @Test + public void testOnReceive_usbConnected_invokeCallback() { + final Intent intent = new Intent(); + intent.putExtra(UsbManager.USB_CONNECTED, true); + + mReceiver.onReceive(mContext, intent); + + verify(mListener).onUsbConnectionChanged(true); + } + + @Test + public void testOnReceive_usbDisconnected_invokeCallback() { + final Intent intent = new Intent(); + intent.putExtra(UsbManager.USB_CONNECTED, false); + + mReceiver.onReceive(mContext, intent); + + verify(mListener).onUsbConnectionChanged(false); + } + + @Test + public void testRegister_invokeMethodTwice_registerOnce() { + mReceiver.register(); + mReceiver.register(); + + final List receivers = mShadowApplication.getReceiversForIntent( + new Intent(UsbManager.ACTION_USB_STATE)); + assertHasOneUsbConnectionBroadcastReceiver(receivers); + } + + @Test + public void testUnregister_invokeMethodTwice_unregisterOnce() { + mReceiver.register(); + mReceiver.unregister(); + mReceiver.unregister(); + + final List receivers = mShadowApplication.getReceiversForIntent( + new Intent(UsbManager.ACTION_USB_STATE)); + assertHasNoUsbConnectionBroadcastReceiver(receivers); + } + + private void assertHasOneUsbConnectionBroadcastReceiver(List receivers) { + boolean hasReceiver = false; + for (final BroadcastReceiver receiver : receivers) { + if (receiver instanceof UsbConnectionBroadcastReceiver) { + // If hasReceiver is true, then we're at the second copy of it so fail. + assertWithMessage( + "Only one instance of UsbConnectionBroadcastReceiver should be " + + "registered").that( + hasReceiver).isFalse(); + hasReceiver = true; + } + } + assertThat(hasReceiver).isTrue(); + } + + private void assertHasNoUsbConnectionBroadcastReceiver(List receivers) { + for (final BroadcastReceiver receiver : receivers) { + assertThat(receiver instanceof UsbConnectionBroadcastReceiver).isFalse(); + } + } +} \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/core/XmlControllerAttributeTest.java b/tests/robotests/src/com/android/settings/core/XmlControllerAttributeTest.java new file mode 100644 index 00000000000..ed4e815c6e4 --- /dev/null +++ b/tests/robotests/src/com/android/settings/core/XmlControllerAttributeTest.java @@ -0,0 +1,275 @@ +package com.android.settings.core; + +import static com.google.common.truth.Truth.assertWithMessage; + +import android.content.Context; +import android.content.res.XmlResourceParser; +import android.provider.SearchIndexableResource; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.Xml; + +import com.android.settings.R; +import com.android.settings.TestConfig; +import com.android.settings.search.DatabaseIndexingUtils; +import com.android.settings.search.Indexable; +import com.android.settings.search.SearchIndexableResources; +import com.android.settings.search.XmlParserUtils; +import com.android.settings.security.SecuritySettings; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.xmlpull.v1.XmlPullParser; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class XmlControllerAttributeTest { + + // List of classes that are too hard to mock in order to retrieve xml information. + private final List illegalClasses = new ArrayList<>( + Arrays.asList( + SecuritySettings.class + )); + + // List of XML that could be retrieved from the illegalClasses list. + private final List whitelistXml = new ArrayList<>( + Arrays.asList( + R.xml.security_settings_misc, + R.xml.security_settings_lockscreen_profile, + R.xml.security_settings_lockscreen, + R.xml.security_settings_chooser, + R.xml.security_settings_pattern_profile, + R.xml.security_settings_pin_profile, + R.xml.security_settings_password_profile, + R.xml.security_settings_pattern, + R.xml.security_settings_pin, + R.xml.security_settings_password, + R.xml.security_settings, + R.xml.security_settings_status + )); + + private static final String NO_VALID_CONSTRUCTOR_ERROR = + "Controllers added in XML need a constructor following either:" + + "\n\tClassName(Context)\n\tClassName(Context, String)" + + "\nThese controllers are missing a valid constructor:\n"; + + private static final String NOT_BASE_PREF_CONTROLLER_ERROR = + "Controllers added in XML need to extend com.android.settings.core" + + ".BasePreferenceController\nThese controllers do not:\n"; + + private static final String BAD_CLASSNAME_ERROR = + "The following controllers set in the XML did not have valid class names:\n"; + + private static final String BAD_CONSTRUCTOR_ERROR = + "The constructor provided by the following classes were insufficient to instantiate " + + "the object. It could be due to being an interface, abstract, or an " + + "IllegalAccessException. Please fix the following classes:\n"; + + Context mContext; + + private Set mProviderClassesCopy; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mProviderClassesCopy = new HashSet<>(SearchIndexableResources.providerValues()); + } + + @After + public void cleanUp() { + SearchIndexableResources.providerValues().clear(); + SearchIndexableResources.providerValues().addAll(mProviderClassesCopy); + } + + @Test + public void testAllIndexableXML_onlyValidBasePreferenceControllersAdded() { + Set xmlSet = getIndexableXml(); + xmlSet.addAll(whitelistXml); + + List xmlControllers = new ArrayList<>(); + Set invalidConstructors = new HashSet<>(); + Set invalidClassHierarchy = new HashSet<>(); + Set badClassNameControllers = new HashSet<>(); + Set badConstructorControllers = new HashSet<>(); + + for (int resId : xmlSet) { + xmlControllers.addAll(getXmlControllers(resId)); + } + + for (String controllerClassName : xmlControllers) { + Class clazz = getClassFromClassName(controllerClassName); + + if (clazz == null) { + badClassNameControllers.add(controllerClassName); + continue; + } + + Constructor constructor = getConstructorFromClass(clazz); + + if (constructor == null) { + invalidConstructors.add(controllerClassName); + continue; + } + + Object controller = getObjectFromConstructor(constructor); + if (controller == null) { + badConstructorControllers.add(controllerClassName); + continue; + } + + if (!(controller instanceof BasePreferenceController)) { + invalidClassHierarchy.add(controllerClassName); + } + } + + final String invalidConstructorError = buildErrorMessage(NO_VALID_CONSTRUCTOR_ERROR, + invalidConstructors); + final String invalidClassHierarchyError = buildErrorMessage(NOT_BASE_PREF_CONTROLLER_ERROR, + invalidClassHierarchy); + final String badClassNameError = buildErrorMessage(BAD_CLASSNAME_ERROR, + badClassNameControllers); + final String badConstructorError = buildErrorMessage(BAD_CONSTRUCTOR_ERROR, + badConstructorControllers); + + assertWithMessage(invalidConstructorError).that(invalidConstructors).isEmpty(); + assertWithMessage(invalidClassHierarchyError).that(invalidClassHierarchy).isEmpty(); + assertWithMessage(badClassNameError).that(badClassNameControllers).isEmpty(); + assertWithMessage(badConstructorError).that(badConstructorControllers).isEmpty(); + } + + private Set getIndexableXml() { + Set xmlResSet = new HashSet(); + + Collection indexableClasses = SearchIndexableResources.providerValues(); + indexableClasses.removeAll(illegalClasses); + + for (Class clazz : indexableClasses) { + + Indexable.SearchIndexProvider provider = DatabaseIndexingUtils.getSearchIndexProvider( + clazz); + + if (provider == null) { + continue; + } + + List resources = provider.getXmlResourcesToIndex(mContext, + true); + + if (resources == null) { + continue; + } + + for (SearchIndexableResource resource : resources) { + // Add '0's anyway. It won't break the test. + xmlResSet.add(resource.xmlResId); + } + } + return xmlResSet; + } + + private List getXmlControllers(int xmlResId) { + List xmlControllers = new ArrayList<>(); + + XmlResourceParser parser; + try { + parser = mContext.getResources().getXml(xmlResId); + + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.START_TAG) { + // Parse next until start tag is found + } + + final int outerDepth = parser.getDepth(); + final AttributeSet attrs = Xml.asAttributeSet(parser); + String controllerClassName; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + controllerClassName = XmlParserUtils.getController(mContext, attrs); + // If controller is not indexed, then it is not compatible with + if (!TextUtils.isEmpty(controllerClassName)) { + xmlControllers.add(controllerClassName); + } + } + } catch (Exception e) { + // Assume an issue with robolectric resources + } + return xmlControllers; + } + + private String buildErrorMessage(String errorSummary, Set errorClasses) { + final StringBuilder error = new StringBuilder(errorSummary); + for (String c : errorClasses) { + error.append(c).append("\n"); + } + return error.toString(); + } + + private Class getClassFromClassName(String className) { + Class clazz = null; + try { + clazz = Class.forName(className); + } catch (ClassNotFoundException e) { + } + return clazz; + } + + private Constructor getConstructorFromClass(Class clazz) { + Constructor constructor = null; + try { + constructor = clazz.getConstructor(Context.class); + } catch (NoSuchMethodException e) { + } + + if (constructor != null) { + return constructor; + } + + try { + constructor = clazz.getConstructor(Context.class, String.class); + } catch (NoSuchMethodException e) { + } + + return constructor; + } + + private Object getObjectFromConstructor(Constructor constructor) { + Object controller = null; + + try { + controller = constructor.newInstance(mContext); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + IllegalArgumentException e) { + } + + if (controller != null) { + return controller; + } + + try { + controller = constructor.newInstance(mContext, "key"); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + IllegalArgumentException e) { + } + + return controller; + } +} diff --git a/tests/robotests/src/com/android/settings/deviceinfo/SystemUpdatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/SystemUpdatePreferenceControllerTest.java index 05670e28faa..1fd543042c1 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/SystemUpdatePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/SystemUpdatePreferenceControllerTest.java @@ -57,7 +57,9 @@ public class SystemUpdatePreferenceControllerTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mController = new SystemUpdatePreferenceController(mContext, mUserManager); + + when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + mController = new SystemUpdatePreferenceController(mContext); mPreference = new Preference(RuntimeEnvironment.application); mPreference.setKey(mController.getPreferenceKey()); when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference); @@ -82,7 +84,7 @@ public class SystemUpdatePreferenceControllerTest { mController.updateNonIndexableKeys(keys); - assertThat(keys.size()).isEqualTo(1); + assertThat(keys).hasSize(1); } @Test @@ -94,8 +96,8 @@ public class SystemUpdatePreferenceControllerTest { @Test public void updateState_shouldSetToAndroidVersion() { - mController = new SystemUpdatePreferenceController( - RuntimeEnvironment.application, mUserManager); + mController = new SystemUpdatePreferenceController(RuntimeEnvironment.application); + mController.updateState(mPreference); assertThat(mPreference.getSummary()) diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java index a163a43989b..c75dbf496d7 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java @@ -18,6 +18,7 @@ package com.android.settings.fuelgauge; import android.content.Context; import android.content.Intent; import android.os.BatteryManager; +import android.os.PowerManager; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; @@ -82,6 +83,14 @@ public class BatteryBroadcastReceiverTest { verify(mBatteryListener).onBatteryChanged(); } + @Test + public void testOnReceive_powerSaveModeChanged_listenerInvoked() { + mBatteryBroadcastReceiver.onReceive(mContext, + new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)); + + verify(mBatteryListener).onBatteryChanged(); + } + @Test public void testOnReceive_batteryDataNotChanged_listenerNotInvoked() { final String batteryLevel = Utils.getBatteryPercentage(mChargingIntent); diff --git a/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java b/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java index 6050b32396f..2bec503afe5 100644 --- a/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java +++ b/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java @@ -128,6 +128,16 @@ public class XmlParserUtilTest { assertThat(key).isNull(); } + @Test + @Config(qualifiers = "mcc999") + public void testControllerAttribute_returnsValidData() { + XmlResourceParser parser = getChildByType(R.xml.about_legal, "Preference"); + final AttributeSet attrs = Xml.asAttributeSet(parser); + + String controller = XmlParserUtils.getController(mContext, attrs); + assertThat(controller).isEqualTo("mind_flayer"); + } + @Test public void testDataSummaryInvalid_ReturnsNull() { XmlResourceParser parser = getParentPrimedParser(R.xml.display_settings);