Snap for 5189750 from abcfa26bf6 to qt-release

Change-Id: I170cbec6d76999499b14011158b65e906d30322c
This commit is contained in:
android-build-team Robot
2018-12-19 04:00:56 +00:00
80 changed files with 2033 additions and 1027 deletions

View File

@@ -14,6 +14,7 @@ include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := Settings
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PRODUCT_MODULE := true
LOCAL_PRIVILEGED_MODULE := true
LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.settings
LOCAL_MODULE_TAGS := optional

View File

@@ -717,8 +717,7 @@
android:name="Settings$ZenModeSettingsActivity"
android:label="@string/zen_mode_settings_title"
android:icon="@drawable/ic_notifications"
android:exported="true"
android:parentActivityName="Settings">
android:exported="true">
<intent-filter android:priority="1">
<action android:name="android.settings.ZEN_MODE_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
@@ -875,8 +874,7 @@
android:name="Settings$NightDisplaySettingsActivity"
android:label="@string/night_display_title"
android:enabled="@*android:bool/config_nightDisplayAvailable"
android:icon="@drawable/ic_settings_night_display"
android:parentActivityName="Settings">
android:icon="@drawable/ic_settings_night_display">
<intent-filter android:priority="32">
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.settings.SHORTCUT" />
@@ -1626,7 +1624,7 @@
android:icon="@drawable/ic_settings_security">
<intent-filter android:priority="1">
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.settings.suggested.category.LOCK_SCREEN" />
<category android:name="com.android.settings.suggested.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.title"
android:resource="@string/suggested_lock_settings_title" />
@@ -2821,10 +2819,8 @@
android:name=".Settings$AppAndNotificationDashboardActivity"
android:label="@string/app_and_notification_dashboard_title"
android:icon="@drawable/ic_homepage_apps"
android:parentActivityName="Settings">
<intent-filter>
<action android:name="com.android.settings.action.SETTINGS"/>
</intent-filter>
android:parentActivityName="Settings"
android:exported="true">
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.applications.AppAndNotificationDashboardFragment"/>
</activity>

View File

@@ -46,6 +46,7 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Settings_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Settings_intermediates)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/Settings)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST

3
OWNERS
View File

@@ -7,8 +7,11 @@ asargent@google.com
dehboxturtle@google.com
dhnishi@google.com
dling@google.com
edgarwang@google.com
jackqdyulei@google.com
mfritze@google.com
rafftsai@google.com
tmfang@google.com
zhfan@google.com
# Emergency approvers in case the above are not available

View File

@@ -43,8 +43,5 @@
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<include layout="@layout/wifi_dpp_fragment_footer"
android:gravity="center|bottom"/>
</LinearLayout>

View File

@@ -39,6 +39,6 @@
style="@style/SuwGlifButton.Primary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/suw_next_button_label" />
android:text="@string/wizard_next" />
</com.google.android.setupdesign.view.ButtonBarLayout>

View File

@@ -39,6 +39,6 @@
style="@style/SuwGlifButton.Primary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/suw_next_button_label" />
android:text="@string/wizard_next" />
</com.google.android.setupdesign.view.ButtonBarLayout>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 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.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<androidx.slice.widget.SliceView
android:id="@+id/slice_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>

View File

@@ -27,7 +27,7 @@
android:layout_height="wrap_content"
android:onClick="onNavigateBack"
android:visibility="gone"
android:text="@string/suw_back_button_label" />
android:text="@string/wizard_back" />
<Space
android:layout_width="0dp"
@@ -41,6 +41,6 @@
android:layout_height="wrap_content"
android:onClick="onNavigateNext"
android:visibility="gone"
android:text="@string/suw_next_button_label" />
android:text="@string/wizard_next" />
</LinearLayout>

View File

@@ -22,12 +22,6 @@
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_wifi_signal_4"
android:layout_gravity="center"/>
<LinearLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"

View File

@@ -17,18 +17,42 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/EntityHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_centerHorizontal="true"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<TextView android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<ImageView
android:id="@+id/header_icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:scaleType="fitCenter"/>
<TextView android:id="@+id/description"
android:layout_width="wrap_content"
<TextView
android:id="@+id/title"
style="@style/TextAppearance.EntityHeaderTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
android:gravity="center_horizontal"
android:textAlignment="center"
android:layout_marginTop="8dp"
android:paddingStart="32dp"
android:paddingEnd="32dp"/>
<TextView
android:id="@+id/description"
style="@style/TextAppearance.EntityHeaderSummary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="false"
android:gravity="center_horizontal"
android:textAlignment="center"
android:layout_marginTop="2dp"
android:paddingStart="32dp"
android:paddingEnd="32dp"/>
</LinearLayout>

View File

@@ -52,6 +52,8 @@
<attr name="apnPreferenceStyle" format="reference" />
<attr name="slicePreferenceStyle" format="reference" />
<attr name="footerPreferenceStyle" format="reference" />
<declare-styleable name="FixedLineSummaryPreference">

View File

@@ -165,4 +165,7 @@
<!-- Email address for the homepage contextual cards feedback -->
<string name="config_contextual_card_feedback_email" translatable="false"></string>
<!-- Uri that represents extra bluetooth settings -->
<string name="config_bluetooth_device_settings_uri" translatable="false">content://com.google.android.gms.nearby.fastpair/settings_slice?addr=<xliff:g id="mac_address">%1$s</xliff:g></string>
</resources>

View File

@@ -827,8 +827,15 @@
<string name="location_settings_title">Location</string>
<!-- Used in the location settings to control turning on/off the feature entirely -->
<string name="location_settings_master_switch_title">Use location</string>
<!-- Summary for Location settings, explaining a few important settings under it [CHAR LIMIT=NONE]-->
<string name="location_settings_summary">Scanning, location history</string>
<!-- Summary for Location settings when location is off [CHAR LIMIT=NONE] -->
<string name="location_settings_summary_location_off">Off</string>
<!-- Summary for Location settings when location is on, explaining how many apps have location permission [CHAR LIMIT=NONE]-->
<plurals name="location_settings_summary_location_on">
<item quantity="one">On - <xliff:g id="count">%1$d</xliff:g> app can access location</item>
<item quantity="other">On - <xliff:g id="count">%1$d</xliff:g> apps can access location</item>
</plurals>
<!-- Location settings, loading the number of apps which have location permission [CHAR LIMIT=30] -->
<string name="location_settings_loading_app_permission_stats">Loading\u2026</string>
<!-- Main Settings screen setting option title for the item to take you to the accounts screen [CHAR LIMIT=22] -->
<string name="account_settings_title">Accounts</string>
@@ -3630,15 +3637,28 @@
<string name="managed_profile_location_switch_title">Location for work profile</string>
<!-- [CHAR LIMIT=30] Location settings screen. It's a link that directs the user to a page that
shows the location permission setting for each installed app -->
<string name="location_app_level_permissions">App-level permissions</string>
<!-- [CHAR LIMIT=42] Location settings screen, sub category for recent location requests -->
<string name="location_category_recent_location_requests">Recent location requests</string>
<!-- Location settings screen, displayed when there're more than three recent location requests -->
<string name="location_recent_location_requests_see_all">See all</string>
<string name="location_app_level_permissions">App permission</string>
<!-- Summary for app permission on Location settings page when location is off [CHAR LIMIT=NONE] -->
<string name="location_app_permission_summary_location_off">Location is off</string>
<!-- Summary for Location settings when location is on, explaining how many apps have location permission [CHAR LIMIT=NONE]-->
<plurals name="location_app_permission_summary_location_on">
<item quantity="one">
<xliff:g id="background_location_app_count">%1$d</xliff:g>
of
<xliff:g id="total_location_app_count">%2$d</xliff:g>
app has unlimited access</item>
<item quantity="other">
<xliff:g id="background_location_app_count">%1$d</xliff:g>
of
<xliff:g id="total_location_app_count">%2$d</xliff:g>
apps have unlimited access</item>
</plurals>
<!-- [CHAR LIMIT=50] Location settings screen, sub category for recent location access -->
<string name="location_category_recent_location_access">Recent location access</string>
<!-- [CHAR LIMIT=30] Location settings screen, button to bring the user to view the details of recent location access -->
<string name="location_recent_location_access_view_details">View details</string>
<!-- Location settings screen, displayed when there's no recent app accessing location -->
<string name="location_no_recent_apps">No apps have requested location recently</string>
<!-- [CHAR LIMIT=30] Location settings screen, sub category for location services -->
<string name="location_category_location_services">Location services</string>
<!-- [CHAR LIMIT=30] Location settings screen, recent location requests high battery use-->
<string name="location_high_battery_use">High battery use</string>
<!-- [CHAR LIMIT=30] Location settings screen, recent location requests low battery use-->
@@ -6746,7 +6766,7 @@
<string name="wizard_back">Back</string>
<!-- Wizard next button label [CHAR LIMIT=25] -->
<string name="wizard_next">Next</string>
<!-- Wizard next button label [CHAR LIMIT=25] -->
<!-- Wizard finish button label [CHAR LIMIT=25] -->
<string name="wizard_finish">Finish</string>
<!-- An option in a photo selection dialog, if there is no photo yet [CHAR LIMIT=50] -->
@@ -6943,6 +6963,8 @@
<string name="keywords_sim_status">network, mobile network state, service state, signal strength, mobile network type, roaming, iccid</string>
<string name="keywords_model_and_hardware">serial number, hardware version</string>
<string name="keywords_android_version">android security patch level, baseband version, kernel version</string>
<!--Search keywords for financial apps sms access settings -->
<string name="keywords_financial_apps_sms_access">financial app, sms, permission</string>
<!-- Search keyword for Device Theme Settings [CHAR LIMIT=NONE] -->
<string name="keywords_systemui_theme">dark theme</string>
@@ -8833,6 +8855,10 @@
<!-- Summary of number of apps currently can write system settings [CHAR LIMIT=60] -->
<string name="write_settings_summary"><xliff:g id="count" example="10">%1$d</xliff:g> of <xliff:g id="count" example="10">%2$d</xliff:g> apps allowed to modify system settings</string>
<!-- Settings title in main settings screen for financial apps sms access [CHAR LIMIT=60] -->
<string name="financial_apps_sms_access_title">Financial Apps Sms Access</string>
<!-- Preference key for financial apps sms access screen -->
<string name="financial_sms_root_screen_key" translatable="false">financial_sms_root_screen_key</string>
<!-- Label for showing apps that can install other apps [CHAR LIMIT=45] -->
<string name="filter_install_sources_apps">Can install other apps</string>
<!-- Label for showing apps that can write system settings [CHAR LIMIT=45] -->

View File

@@ -489,4 +489,8 @@
<item name="android:textAllCaps">false</item>
</style>
<style name="Widget.SliceView.Settings">
<item name="titleSize">@*android:dimen/text_size_subhead_material</item>
</style>
</resources>

View File

@@ -21,6 +21,7 @@
<style name="PreferenceTheme" parent="@style/PreferenceThemeOverlay.SettingsBase">
<item name="apnPreferenceStyle">@style/ApnPreference</item>
<item name="slicePreferenceStyle">@style/SlicePreference</item>
<item name="seekBarPreferenceStyle">@style/SettingsSeekBarPreference</item>
<item name="twoStateButtonPreferenceStyle">@style/TwoStateButtonPreference</item>
</style>
@@ -34,6 +35,10 @@
<item name="android:layout">@layout/apn_preference_layout</item>
</style>
<style name="SlicePreference" parent="@style/Preference.Material">
<item name="android:layout">@layout/slice_preference_layout</item>
</style>
<style name="SettingsSeekBarPreference" parent="@style/Preference.Material">
<item name="android:layout">@layout/preference_widget_seekbar_settings</item>
</style>

View File

@@ -32,9 +32,13 @@
<item name="fingerprint_layout_theme">@style/FingerprintLayoutTheme</item>
<item name="face_layout_theme">@style/FaceLayoutTheme</item>
<item name="ic_menu_moreoverflow">@*android:drawable/ic_menu_moreoverflow_holo_dark</item>
<!-- For wifi icon -->
<item name="wifi_signal">@drawable/wifi_signal</item>
<item name="wifi_signal_color">?android:attr/colorAccent</item>
<item name="wifi_friction">@drawable/wifi_friction</item>
<item name="frictionIconColor">?android:colorControlNormal</item>
<item name="side_margin">@dimen/settings_side_margin</item>
<item name="suwListItemIconColor">?android:attr/colorAccent</item>
@@ -58,6 +62,9 @@
<!-- TODO(118444000): Remove colorPrimary and colorPrimaryVariant -->
<item name="colorPrimary">@*android:color/primary_device_default_settings_light</item>
<item name="colorPrimaryVariant">@android:color/white</item>
<!-- For slice view in settings -->
<item name="sliceViewStyle">@style/Widget.SliceView.Settings</item>
</style>
<!-- Variant of the settings theme with no action bar. -->
@@ -71,9 +78,6 @@
<item name="android:actionBarStyle">@style/Widget.ActionBar.SubSettings</item>
<item name="switchBarTheme">@style/ThemeOverlay.SwitchBar.Settings</item>
<!-- Friction icon color for wifi preferences -->
<item name="frictionIconColor">?android:colorControlNormal</item>
</style>
<style name="Theme.SubSettings" parent="Theme.SubSettings.Base"/>

View File

@@ -26,7 +26,14 @@
settings:allowDividerBelow="true"/>
<com.android.settingslib.widget.ActionButtonsPreference
android:key="action_buttons" />
android:key="action_buttons"
settings:allowDividerBelow="true"/>
<com.android.settings.slices.SlicePreference
android:key="bt_device_slice"
settings:controller="com.android.settings.slices.SlicePreferenceController"
settings:allowDividerBelow="true"
settings:allowDividerAbove="true"/>
<PreferenceCategory
android:key="bluetooth_profiles"/>

View File

@@ -9,16 +9,14 @@
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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/location_category_recent_location_requests"
android:key="recent_location_requests_see_all">
<PreferenceCategory
android:key="all_recent_location_requests"/>
</PreferenceScreen>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="@string/financial_sms_root_screen_key"
android:title="@string/financial_apps_sms_access_title"
settings:controller="com.android.settings.applications.specialaccess.financialapps.FinancialAppsController" />

View File

@@ -20,21 +20,14 @@
android:title="@string/location_settings_title"
settings:keywords="@string/keywords_location">
<PreferenceCategory
android:key="recent_location_requests"
android:title="@string/location_category_recent_location_requests"/>
<Preference
android:key="recent_location_requests_see_all_button"
android:title="@string/location_recent_location_requests_see_all"
android:icon="@drawable/ic_chevron_right_24dp"
android:selectable="true"
android:fragment="com.android.settings.location.RecentLocationRequestSeeAllFragment"
settings:searchable="false"/>
<com.android.settingslib.widget.LayoutPreference
android:key="apps_dashboard"
android:layout="@layout/app_entities_header"
settings:allowDividerBelow="true" />
<PreferenceCategory
android:key="location_advanced_settings"
settings:initialExpandedChildrenCount="1">
settings:initialExpandedChildrenCount="0">
<!-- This preference category gets removed if new_recent_location_ui is disabled -->
<Preference
@@ -60,8 +53,7 @@
android:selectable="true" />
<PreferenceCategory
android:key="location_services"
android:title="@string/location_category_location_services"/>
android:key="location_services" />
</PreferenceCategory>
<PreferenceCategory

View File

@@ -130,4 +130,9 @@
android:value="com.android.settings.Settings$ChangeWifiStateActivity" />
</Preference>
<Preference
android:key="financial_apps_sms_access"
android:title="@string/financial_apps_sms_access_title"
android:fragment="com.android.settings.applications.specialaccess.financialapps.FinancialAppsSmsAccess"
settings:keywords="@string/keywords_financial_apps_sms_access" />
</PreferenceScreen>

View File

@@ -93,10 +93,11 @@
<Preference
android:key="top_level_location"
android:title="@string/location_settings_title"
android:summary="@string/location_settings_summary"
android:summary="@string/location_settings_loading_app_permission_stats"
android:icon="@drawable/ic_homepage_location"
android:order="-50"
android:fragment="com.android.settings.location.LocationSettings"/>
android:fragment="com.android.settings.location.LocationSettings"
settings:controller="com.android.settings.location.TopLevelLocationPreferenceController"/>
<Preference
android:key="top_level_security"
@@ -149,4 +150,4 @@
android:order="100"
settings:controller="com.android.settings.support.SupportPreferenceController"/>
</PreferenceScreen>
</PreferenceScreen>

View File

@@ -32,7 +32,6 @@ import com.android.settings.display.TapToWakePreferenceController;
import com.android.settings.display.ThemePreferenceController;
import com.android.settings.display.TimeoutPreferenceController;
import com.android.settings.display.VrDisplayPreferenceController;
import com.android.settings.display.WallpaperPreferenceController;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
import com.android.settingslib.core.AbstractPreferenceController;

View File

@@ -66,7 +66,9 @@ import com.android.settings.password.ConfirmLockPattern;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.google.android.setupcompat.TemplateLayout;
import com.google.android.setupdesign.template.ButtonFooterMixin;
import com.google.android.setupcompat.item.FooterButton;
import com.google.android.setupcompat.item.FooterButton.ButtonType;
import com.google.android.setupcompat.template.ButtonFooterMixin;
import java.util.List;
@@ -96,7 +98,7 @@ public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutL
private View mContentView;
@VisibleForTesting
Button mInitiateButton;
FooterButton mInitiateButton;
private View mExternalStorageContainer;
@VisibleForTesting
CheckBox mExternalStorage;
@@ -416,12 +418,15 @@ public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutL
final TemplateLayout layout = mContentView.findViewById(R.id.setup_wizard_layout);
final ButtonFooterMixin buttonFooterMixin = layout.getMixin(ButtonFooterMixin.class);
buttonFooterMixin.removeAllViews();
buttonFooterMixin.addSpace();
buttonFooterMixin.addSpace();
mInitiateButton = buttonFooterMixin.addButton(R.string.master_clear_button_text,
R.style.SuwGlifButton_Primary);
mInitiateButton.setOnClickListener(mInitiateListener);
buttonFooterMixin.setPrimaryButton(
new FooterButton(
getActivity(),
R.string.master_clear_button_text,
mInitiateListener,
ButtonType.OTHER,
R.style.SuwGlifButton_Primary)
);
mInitiateButton = buttonFooterMixin.getPrimaryButton();
}
private void getContentDescription(View v, StringBuffer description) {

View File

@@ -45,7 +45,9 @@ import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.google.android.setupcompat.TemplateLayout;
import com.google.android.setupdesign.template.ButtonFooterMixin;
import com.google.android.setupcompat.item.FooterButton;
import com.google.android.setupcompat.item.FooterButton.ButtonType;
import com.google.android.setupcompat.template.ButtonFooterMixin;
/**
* Confirm and execute a reset of the device to a clean "just out of the box"
@@ -153,11 +155,14 @@ public class MasterClearConfirm extends InstrumentedFragment {
final TemplateLayout layout = mContentView.findViewById(R.id.setup_wizard_layout);
final ButtonFooterMixin buttonFooterMixin = layout.getMixin(ButtonFooterMixin.class);
buttonFooterMixin.removeAllViews();
buttonFooterMixin.addSpace();
buttonFooterMixin.addSpace();
buttonFooterMixin.addButton(R.string.master_clear_button_text,
R.style.SuwGlifButton_Primary).setOnClickListener(mFinalClickListener);
buttonFooterMixin.setPrimaryButton(
new FooterButton(
getActivity(),
R.string.master_clear_button_text,
mFinalClickListener,
ButtonType.OTHER,
R.style.SuwGlifButton_Primary)
);
}
private void setUpActionBarAndTitle() {

View File

@@ -0,0 +1,158 @@
/*
* Copyright (C) 2018 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.specialaccess.financialapps;
import static android.Manifest.permission.SMS_FINANCIAL_TRANSACTIONS;
import static android.Manifest.permission.READ_SMS;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.SwitchPreference;
import java.util.ArrayList;
import java.util.List;
public class FinancialAppsController extends BasePreferenceController
implements ApplicationsState.Callbacks {
private final static String TAG = FinancialAppsController.class.getSimpleName();
@VisibleForTesting
PreferenceScreen mRoot;
public FinancialAppsController(Context context, String key) {
super(context, key);
}
@AvailabilityStatus
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mRoot = screen;
}
@Override
public void updateState(Preference preference) {
updateList();
}
private void updateList() {
mRoot.removeAll();
final PackageManager packageManager = mContext.getPackageManager();
final AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
final List<PackageInfo> installedPackages =
packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS);
final int numPackages = installedPackages.size();
for (int i = 0; i < numPackages; i++) {
final PackageInfo installedPackage = installedPackages.get(i);
if (installedPackage.requestedPermissions == null) {
continue;
}
final int targetSdk = installedPackage.applicationInfo.targetSdkVersion;
final String pkgName = installedPackage.packageName;
if ((targetSdk >= Build.VERSION_CODES.Q
&& ArrayUtils.contains(installedPackage.requestedPermissions,
SMS_FINANCIAL_TRANSACTIONS))
|| (targetSdk < Build.VERSION_CODES.Q
&& ArrayUtils.contains(installedPackage.requestedPermissions,
READ_SMS))) {
final SwitchPreference pref = new SwitchPreference(mRoot.getContext());
pref.setTitle(installedPackage.applicationInfo.loadLabel(packageManager));
pref.setKey(pkgName);
pref.setChecked(
appOpsManager.checkOp(
targetSdk >= Build.VERSION_CODES.Q
? AppOpsManager.OP_SMS_FINANCIAL_TRANSACTIONS
: AppOpsManager.OP_READ_SMS,
installedPackage.applicationInfo.uid,
pkgName) == AppOpsManager.MODE_ALLOWED);
pref.setOnPreferenceChangeListener((preference, newValue) -> {
final int uid;
try {
uid = packageManager.getPackageInfo(preference.getKey(), 0)
.applicationInfo.uid;
} catch (NameNotFoundException e) {
Log.e(TAG, "onPreferenceChange: Failed to get uid for "
+ preference.getKey());
return false;
}
appOpsManager.setMode(
targetSdk >= Build.VERSION_CODES.Q
? AppOpsManager.OP_SMS_FINANCIAL_TRANSACTIONS
: AppOpsManager.OP_READ_SMS,
uid,
pkgName,
(Boolean) newValue ? AppOpsManager.MODE_ALLOWED
: AppOpsManager.MODE_IGNORED);
return true;
});
mRoot.addPreference(pref);
}
}
}
@Override
public void onRunningStateChanged(boolean running) {}
@Override
public void onPackageListChanged() {
updateList();
}
@Override
public void onRebuildComplete(ArrayList<AppEntry> apps) {}
@Override
public void onPackageIconChanged() {}
@Override
public void onPackageSizeChanged(String packageName) {}
@Override
public void onAllSizesComputed() {}
@Override
public void onLauncherInfoChanged() {}
@Override
public void onLoadEntriesCompleted() {}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2018 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.specialaccess.financialapps;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.provider.SearchIndexableResource;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
import java.util.List;
@SearchIndexable
public class FinancialAppsSmsAccess extends DashboardFragment {
private final static String TAG = FinancialAppsSmsAccess.class.getSimpleName();
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.financial_app_sms_access;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.SETTINGS_FINANCIAL_APPS_SMS_ACCESS;
}
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
boolean enabled) {
final ArrayList<SearchIndexableResource> result = new ArrayList<>();
final SearchIndexableResource sir = new SearchIndexableResource(context);
sir.xmlResId = R.xml.financial_app_sms_access;
result.add(sir);
return result;
}
};
}

View File

@@ -67,8 +67,11 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity
*/
public static final int RESULT_TIMEOUT = RESULT_FIRST_USER + 2;
public static final int CONFIRM_REQUEST = 1;
public static final int ENROLLING = 2;
public static final int CHOOSE_LOCK_GENERIC_REQUEST = 1;
public static final int BIOMETRIC_FIND_SENSOR_REQUEST = 2;
public static final int LEARN_MORE_REQUEST = 3;
public static final int CONFIRM_REQUEST = 4;
public static final int ENROLLING = 5;
protected boolean mLaunchedConfirmLock;
protected byte[] mToken;
@@ -105,10 +108,6 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity
initViews();
}
protected boolean shouldLaunchConfirmLock() {
return mToken == null && !mLaunchedConfirmLock;
}
protected void initViews() {
getWindow().setStatusBarColor(Color.TRANSPARENT);
Button nextButton = getNextButton();

View File

@@ -37,10 +37,6 @@ import com.google.android.setupdesign.span.LinkSpan;
public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
implements LinkSpan.OnClickListener {
public static final int CHOOSE_LOCK_GENERIC_REQUEST = 1;
public static final int BIOMETRIC_FIND_SENSOR_REQUEST = 2;
public static final int LEARN_MORE_REQUEST = 3;
private UserManager mUserManager;
private boolean mHasPassword;
private boolean mBiometricUnlockDisabledByAdmin;
@@ -109,6 +105,11 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
*/
protected abstract Intent getEnrollingIntent();
/**
* @return the title to be shown on the ConfirmLock screen.
*/
protected abstract int getConfirmLockTitleResId();
/**
* @param span
*/
@@ -133,6 +134,13 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
mUserManager = UserManager.get(this);
updatePasswordQuality();
if (!mHasPassword) {
// No password registered, launch into enrollment wizard.
launchChooseLock();
} else {
launchConfirmLock(getConfirmLockTitleResId(), getChallenge());
}
}
@Override
@@ -157,13 +165,8 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
@Override
protected void onNextButtonClick() {
if (!mHasPassword) {
// No biometrics registered, launch into enrollment wizard.
launchChooseLock();
} else {
// Lock thingy is already set up, launch directly into find sensor step from wizard.
launchFindSensor(null);
}
// Lock thingy is already set up, launch directly to the next page
launchNextEnrollingActivity(mToken);
}
private void launchChooseLock() {
@@ -181,7 +184,7 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
startActivityForResult(intent, CHOOSE_LOCK_GENERIC_REQUEST);
}
private void launchFindSensor(byte[] token) {
private void launchNextEnrollingActivity(byte[] token) {
Intent intent = getEnrollingIntent();
if (token != null) {
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
@@ -199,9 +202,9 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
final boolean isResultFinished = resultCode == RESULT_FINISHED;
final int result = isResultFinished ? RESULT_OK : RESULT_SKIP;
if (requestCode == BIOMETRIC_FIND_SENSOR_REQUEST) {
if (isResultFinished || resultCode == RESULT_SKIP) {
final int result = isResultFinished ? RESULT_OK : RESULT_SKIP;
setResult(result, data);
finish();
return;
@@ -209,10 +212,21 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
} else if (requestCode == CHOOSE_LOCK_GENERIC_REQUEST) {
if (isResultFinished) {
updatePasswordQuality();
byte[] token = data.getByteArrayExtra(
mToken = data.getByteArrayExtra(
ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
launchFindSensor(token);
overridePendingTransition(R.anim.suw_slide_next_in, R.anim.suw_slide_next_out);
return;
} else {
setResult(result, data);
finish();
}
} else if (requestCode == CONFIRM_REQUEST) {
if (resultCode == RESULT_OK && data != null) {
mToken = data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
overridePendingTransition(R.anim.suw_slide_next_in, R.anim.suw_slide_next_out);
} else {
setResult(result, data);
finish();
}
} else if (requestCode == LEARN_MORE_REQUEST) {
overridePendingTransition(R.anim.suw_slide_back_in, R.anim.suw_slide_back_out);

View File

@@ -48,13 +48,6 @@ public abstract class BiometricsEnrollEnrolling extends BiometricEnrollBase
*/
protected abstract boolean shouldStartAutomatically();
/**
* @return true if enrollment should finish when onStop is called.
*/
protected boolean shouldFinishOnStop() {
return true;
}
@Override
protected void onStart() {
super.onStart();
@@ -70,7 +63,7 @@ public abstract class BiometricsEnrollEnrolling extends BiometricEnrollBase
mSidecar.setListener(null);
}
if (shouldFinishOnStop() && !isChangingConfigurations()) {
if (!isChangingConfigurations()) {
if (mSidecar != null) {
mSidecar.cancelEnrollment();
getSupportFragmentManager()

View File

@@ -44,7 +44,6 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling {
private TextView mErrorText;
private Interpolator mLinearOutSlowInInterpolator;
private boolean mShouldFinishOnStop = true;
private FaceEnrollPreviewFragment mPreviewFragment;
private FaceFeatureProvider.Listener mListener = new FaceFeatureProvider.Listener() {
@@ -92,13 +91,7 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling {
Button skipButton = findViewById(R.id.skip_button);
skipButton.setOnClickListener(this);
if (shouldLaunchConfirmLock()) {
launchConfirmLock(R.string.security_settings_face_preference_title,
Utils.getFaceManagerOrNull(this).generateChallenge());
mShouldFinishOnStop = false;
} else {
startEnrollment();
}
startEnrollment();
}
@Override
@@ -129,11 +122,6 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling {
return false;
}
@Override
protected boolean shouldFinishOnStop() {
return mShouldFinishOnStop;
}
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.FACE_ENROLL_ENROLLING;
@@ -178,23 +166,6 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling {
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CONFIRM_REQUEST) {
if (resultCode == RESULT_OK && data != null) {
mShouldFinishOnStop = true;
mToken = data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
overridePendingTransition(R.anim.suw_slide_next_in, R.anim.suw_slide_next_out);
getIntent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
startEnrollment();
} else {
finish();
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
private void showErrorDialog(CharSequence msg, int msgId) {
BiometricErrorDialog dialog = FaceErrorDialog.newInstance(msg, msgId);
dialog.show(getSupportFragmentManager(), FaceErrorDialog.class.getName());

View File

@@ -102,6 +102,7 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
@Override
protected long getChallenge() {
mFaceManager = Utils.getFaceManagerOrNull(this);
if (mFaceManager == null) {
return 0;
}
@@ -118,6 +119,11 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
return new Intent(this, FaceEnrollEnrolling.class);
}
@Override
protected int getConfirmLockTitleResId() {
return R.string.security_settings_face_preference_title;
}
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.FACE_ENROLL_INTRO;

View File

@@ -51,12 +51,9 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase {
setHeaderText(R.string.security_settings_fingerprint_enroll_find_sensor_title);
if (shouldLaunchConfirmLock()) {
launchConfirmLock(R.string.security_settings_fingerprint_preference_title,
Utils.getFingerprintManagerOrNull(this).preEnroll());
} else if (mToken != null) {
startLookingForFingerprint(); // already confirmed, so start looking for fingerprint
}
startLookingForFingerprint(); // already confirmed, so start looking for fingerprint
View animationView = findViewById(R.id.fingerprint_sensor_location_animation);
if (animationView instanceof FingerprintFindSensorAnimation) {
mAnimation = (FingerprintFindSensorAnimation) animationView;

View File

@@ -106,6 +106,7 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
@Override
protected long getChallenge() {
mFingerprintManager = Utils.getFingerprintManagerOrNull(this);
if (mFingerprintManager == null) {
return 0;
}
@@ -122,6 +123,11 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
return new Intent(this, FingerprintEnrollFindSensor.class);
}
@Override
protected int getConfirmLockTitleResId() {
return R.string.security_settings_fingerprint_preference_title;
}
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.FINGERPRINT_ENROLL_INTRO;

View File

@@ -21,12 +21,16 @@ import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.os.Bundle;
import android.util.FeatureFlagUtils;
import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.core.FeatureFlags;
import com.android.settings.dashboard.RestrictedDashboardFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.slices.SlicePreferenceController;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -98,6 +102,13 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
mManager = getLocalBluetoothManager(context);
mCachedDevice = getCachedDevice(mDeviceAddress);
super.onAttach(context);
if (FeatureFlagUtils.isEnabled(context, FeatureFlags.SLICE_INJECTION)) {
final BluetoothFeatureProvider featureProvider = FeatureFactory.getFactory(context)
.getBluetoothFeatureProvider(context);
use(SlicePreferenceController.class).setSliceUri(
featureProvider.getBluetoothDeviceSettingsUri(mDeviceAddress));
}
}
@Override

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2018 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.net.Uri;
/**
* Provider for bluetooth related feature
*/
public interface BluetoothFeatureProvider {
/**
* Get the {@link Uri} that represents extra settings for a specific bluetooth device
* @param macAddress Bluetooth mac address
* @return {@link Uri} for extra settings
*/
Uri getBluetoothDeviceSettingsUri(String macAddress);
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2018 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.net.Uri;
import com.android.settings.R;
/**
* Impl of {@link BluetoothFeatureProvider}
*/
public class BluetoothFeatureProviderImpl implements BluetoothFeatureProvider {
private Context mContext;
public BluetoothFeatureProviderImpl(Context context) {
mContext = context;
}
@Override
public Uri getBluetoothDeviceSettingsUri(String macAddress) {
final String uriString = mContext.getString(R.string.config_bluetooth_device_settings_uri,
macAddress);
return Uri.parse(uriString);
}
}

View File

@@ -27,4 +27,5 @@ public class FeatureFlags {
public static final String WIFI_MAC_RANDOMIZATION = "settings_wifi_mac_randomization";
public static final String NETWORK_INTERNET_V2 = "settings_network_and_internet_v2";
public static final String WIFI_SHARING = "settings_wifi_sharing";
public static final String SLICE_INJECTION = "settings_slice_injection";
}

View File

@@ -295,7 +295,13 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
if (!controller.isAvailable()) {
continue;
}
final String key = controller.getPreferenceKey();
if (TextUtils.isEmpty(key)) {
Log.d(TAG, String.format("Preference key is %s in Controller %s",
key, controller.getClass().getSimpleName()));
continue;
}
final Preference preference = screen.findPreference(key);
if (preference == null) {

View File

@@ -35,6 +35,7 @@ import androidx.loader.content.Loader;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
import java.util.ArrayList;
import java.util.List;
@@ -56,7 +57,9 @@ import java.util.stream.Collectors;
* get the page refreshed.
*/
public class ContextualCardManager implements ContextualCardLoader.CardContentLoaderListener,
ContextualCardUpdateListener {
ContextualCardUpdateListener, LifecycleObserver, OnSaveInstanceState {
private static final String KEY_CONTEXTUAL_CARDS = "key_contextual_cards";
private static final String TAG = "ContextualCardManager";
@@ -68,6 +71,9 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
final List<ContextualCard> mContextualCards;
@VisibleForTesting
long mStartTime;
boolean mIsFirstLaunch;
@VisibleForTesting
List<String> mSavedCards;
private final Context mContext;
private final ControllerRendererPool mControllerRendererPool;
@@ -76,12 +82,20 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
private ContextualCardUpdateListener mListener;
public ContextualCardManager(Context context, Lifecycle lifecycle) {
public ContextualCardManager(Context context, Lifecycle lifecycle, Bundle savedInstanceState) {
mContext = context;
mLifecycle = lifecycle;
mContextualCards = new ArrayList<>();
mLifecycleObservers = new ArrayList<>();
mControllerRendererPool = new ControllerRendererPool();
mLifecycle.addObserver(this);
if (savedInstanceState == null) {
mIsFirstLaunch = true;
mSavedCards = null;
} else {
mSavedCards = savedInstanceState.getStringArrayList(KEY_CONTEXTUAL_CARDS);
}
//for data provided by Settings
for (@ContextualCard.CardType int cardType : SETTINGS_CARDS) {
setupController(cardType);
@@ -172,13 +186,34 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
@Override
public void onFinishCardLoading(List<ContextualCard> cards) {
final long loadTime = System.currentTimeMillis() - mStartTime;
final List<ContextualCard> cardsToKeep = getCardsToKeep(cards);
//navigate back to the homepage or after card dismissal
if (!mIsFirstLaunch) {
onContextualCardUpdated(cardsToKeep.stream()
.collect(groupingBy(ContextualCard::getCardType)));
return;
}
//only log homepage display upon a fresh launch
if (loadTime <= ContextualCardLoader.CARD_CONTENT_LOADER_TIMEOUT_MS) {
onContextualCardUpdated(
cards.stream().collect(groupingBy(ContextualCard::getCardType)));
onContextualCardUpdated(cards.stream()
.collect(groupingBy(ContextualCard::getCardType)));
}
final long totalTime = System.currentTimeMillis() - mStartTime;
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider()
.logHomepageDisplay(mContext, totalTime);
mIsFirstLaunch = false;
}
@Override
public void onSaveInstanceState(Bundle outState) {
final ArrayList<String> cards = mContextualCards.stream()
.map(ContextualCard::getName)
.collect(Collectors.toCollection(ArrayList::new));
outState.putStringArrayList(KEY_CONTEXTUAL_CARDS, cards);
}
public ControllerRendererPool getControllerRendererPool() {
@@ -189,6 +224,22 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
mListener = listener;
}
private List<ContextualCard> getCardsToKeep(List<ContextualCard> cards) {
if (mSavedCards != null) {
//screen rotate
final List<ContextualCard> cardsToKeep = cards.stream()
.filter(card -> mSavedCards.contains(card.getName()))
.collect(Collectors.toList());
mSavedCards = null;
return cardsToKeep;
} else {
//navigate back to the homepage or after dismissing a card
return cards.stream()
.filter(card -> mContextualCards.contains(card))
.collect(Collectors.toList());
}
}
static class CardContentLoaderCallbacks implements
LoaderManager.LoaderCallbacks<List<ContextualCard>> {

View File

@@ -42,7 +42,8 @@ public class ContextualCardsFragment extends InstrumentedFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContextualCardManager = new ContextualCardManager(getContext(), getSettingsLifecycle());
mContextualCardManager = new ContextualCardManager(getContext(), getSettingsLifecycle(),
savedInstanceState);
}
@Override

View File

@@ -1,18 +1,38 @@
package com.android.settings.location;
import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import android.content.Context;
import android.location.LocationManager;
import android.permission.RuntimePermissionPresenter;
import android.provider.Settings;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicInteger;
public class AppLocationPermissionPreferenceController extends
AbstractPreferenceController implements PreferenceControllerMixin {
LocationBasePreferenceController implements PreferenceControllerMixin {
private static final String KEY_APP_LEVEL_PERMISSIONS = "app_level_permissions";
/** Total number of apps that has location permission. */
private int mNumTotal = -1;
/** Total number of apps that has background location permission. */
private int mNumBackground = -1;
private final LocationManager mLocationManager;
private Preference mPreference;
public AppLocationPermissionPreferenceController(Context context) {
super(context);
public AppLocationPermissionPreferenceController(Context context, Lifecycle lifecycle) {
super(context, lifecycle);
mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
}
@Override
@@ -25,4 +45,53 @@ public class AppLocationPermissionPreferenceController extends
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED, 1) == 1;
}
@Override
public CharSequence getSummary() {
if (mLocationManager.isLocationEnabled()) {
if (mNumTotal == -1 || mNumBackground == -1) {
return mContext.getString(R.string.location_settings_loading_app_permission_stats);
}
return mContext.getResources().getQuantityString(
R.plurals.location_app_permission_summary_location_on, mNumBackground,
mNumBackground, mNumTotal);
} else {
return mContext.getString(R.string.location_app_permission_summary_location_off);
}
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
mPreference = preference;
final AtomicInteger loadingInProgress = new AtomicInteger(2);
refreshSummary(preference);
// Bail out if location has been disabled.
if (!mLocationManager.isLocationEnabled()) {
return;
}
RuntimePermissionPresenter.getInstance(mContext).countPermissionApps(
Arrays.asList(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION), false, false,
(numApps) -> {
mNumTotal = numApps;
if (loadingInProgress.decrementAndGet() == 0) {
refreshSummary(preference);
}
}, null);
RuntimePermissionPresenter.getInstance(mContext).countPermissionApps(
Collections.singletonList(ACCESS_BACKGROUND_LOCATION), true, false,
(numApps) -> {
mNumBackground = numApps;
if (loadingInProgress.decrementAndGet() == 0) {
refreshSummary(preference);
}
}, null);
}
@Override
public void onLocationModeChanged(int mode, boolean restricted) {
// 'null' is checked inside updateState(), so no need to check here.
updateState(mPreference);
}
}

View File

@@ -35,15 +35,15 @@ import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
/**
* A class that listens to location settings change and modifies location settings
* settings.
*/
public class LocationEnabler implements LifecycleObserver, OnResume, OnPause {
public class LocationEnabler implements LifecycleObserver, OnStart, OnStop {
private static final String TAG = "LocationEnabler";
@VisibleForTesting
@@ -73,7 +73,7 @@ public class LocationEnabler implements LifecycleObserver, OnResume, OnPause {
}
@Override
public void onResume() {
public void onStart() {
if (mReceiver == null) {
mReceiver = new BroadcastReceiver() {
@Override
@@ -90,12 +90,8 @@ public class LocationEnabler implements LifecycleObserver, OnResume, OnPause {
}
@Override
public void onPause() {
try {
mContext.unregisterReceiver(mReceiver);
} catch (RuntimeException e) {
// Ignore exceptions caused by race condition
}
public void onStop() {
mContext.unregisterReceiver(mReceiver);
}
void refreshLocationMode() {

View File

@@ -35,7 +35,7 @@ import com.android.settings.search.Indexable;
import com.android.settings.widget.SwitchBar;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.location.RecentLocationApps;
import com.android.settingslib.location.RecentLocationAccesses;
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
@@ -52,7 +52,7 @@ import java.util.List;
* <li>In switch bar: location master switch. Used to toggle location on and off.
* </li>
* </ul>
* <li>Recent location requests: automatically populated by {@link RecentLocationApps}</li>
* <li>Recent location requests: automatically populated by {@link RecentLocationAccesses}</li>
* <li>Location services: multi-app settings provided from outside the Android framework. Each
* is injected by a system-partition app via the {@link SettingInjectorService} API.</li>
* </ul>
@@ -122,13 +122,11 @@ public class LocationSettings extends DashboardFragment {
private static List<AbstractPreferenceController> buildPreferenceControllers(
Context context, LocationSettings fragment, Lifecycle lifecycle) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new AppLocationPermissionPreferenceController(context));
controllers.add(new AppLocationPermissionPreferenceController(context, lifecycle));
controllers.add(new LocationForWorkPreferenceController(context, lifecycle));
controllers.add(
new RecentLocationRequestPreferenceController(context, fragment, lifecycle));
controllers.add(new RecentLocationAccessPreferenceController(context));
controllers.add(new LocationScanningPreferenceController(context));
controllers.add(
new LocationServicePreferenceController(context, fragment, lifecycle));
controllers.add(new LocationServicePreferenceController(context, fragment, lifecycle));
controllers.add(new LocationFooterPreferenceController(context, lifecycle));
return controllers;
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.settings.location;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.view.View;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.location.RecentLocationAccesses;
import com.android.settingslib.widget.AppEntitiesHeaderController;
import com.android.settingslib.widget.LayoutPreference;
import java.util.List;
public class RecentLocationAccessPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin {
/** Key for the recent location apps dashboard */
private static final String KEY_APPS_DASHBOARD = "apps_dashboard";
private final RecentLocationAccesses mRecentLocationAccesses;
private AppEntitiesHeaderController mController;
private static final int MAXIMUM_APP_COUNT = 3;
public RecentLocationAccessPreferenceController(Context context) {
this(context, new RecentLocationAccesses(context));
}
@VisibleForTesting
RecentLocationAccessPreferenceController(Context context,
RecentLocationAccesses recentAccesses) {
super(context);
mRecentLocationAccesses = recentAccesses;
}
@Override
public String getPreferenceKey() {
return KEY_APPS_DASHBOARD;
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
final LayoutPreference preference = (LayoutPreference) screen.findPreference(
KEY_APPS_DASHBOARD);
final View view = preference.findViewById(R.id.app_entities_header);
mController = AppEntitiesHeaderController.newInstance(mContext, view)
.setHeaderTitleRes(R.string.location_category_recent_location_access)
.setHeaderDetailsRes(R.string.location_recent_location_access_view_details)
.setHeaderDetailsClickListener((View v) -> {
final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE);
intent.putExtra(Intent.EXTRA_PERMISSION_NAME,
Manifest.permission.ACCESS_FINE_LOCATION);
mContext.startActivity(intent);
});
}
@Override
public void updateState(Preference preference) {
updateRecentApps();
}
private void updateRecentApps() {
final List<RecentLocationAccesses.Access> recentLocationAccesses =
mRecentLocationAccesses.getAppListSorted();
if (recentLocationAccesses.size() > 0) {
// Display the top 3 preferences to container in original order.
int i = 0;
for (; i < Math.min(recentLocationAccesses.size(), MAXIMUM_APP_COUNT); i++) {
final RecentLocationAccesses.Access access = recentLocationAccesses.get(i);
mController.setAppEntity(i, access.icon, access.label, access.contentDescription);
}
for (; i < MAXIMUM_APP_COUNT; i++) {
mController.removeAppEntity(i);
}
} else {
// If there's no item to display, add a "No recent apps" item.
}
mController.apply();
}
}

View File

@@ -1,155 +0,0 @@
/*
* 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.location;
import android.content.Context;
import android.os.Bundle;
import android.os.UserHandle;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.location.RecentLocationApps;
import com.android.settingslib.widget.apppreference.AppPreference;
import java.util.List;
public class RecentLocationRequestPreferenceController extends LocationBasePreferenceController {
/** Key for preference category "Recent location requests" */
private static final String KEY_RECENT_LOCATION_REQUESTS = "recent_location_requests";
@VisibleForTesting
static final String KEY_SEE_ALL_BUTTON = "recent_location_requests_see_all_button";
private final LocationSettings mFragment;
private final RecentLocationApps mRecentLocationApps;
private PreferenceCategory mCategoryRecentLocationRequests;
private Preference mSeeAllButton;
/** Used in this class and {@link RecentLocationRequestSeeAllPreferenceController} */
static class PackageEntryClickedListener implements Preference.OnPreferenceClickListener {
private final DashboardFragment mFragment;
private final String mPackage;
private final UserHandle mUserHandle;
public PackageEntryClickedListener(DashboardFragment fragment, String packageName,
UserHandle userHandle) {
mFragment = fragment;
mPackage = packageName;
mUserHandle = userHandle;
}
@Override
public boolean onPreferenceClick(Preference preference) {
// start new fragment to display extended information
final Bundle args = new Bundle();
args.putString(AppInfoDashboardFragment.ARG_PACKAGE_NAME, mPackage);
new SubSettingLauncher(mFragment.getContext())
.setDestination(AppInfoDashboardFragment.class.getName())
.setArguments(args)
.setTitleRes(R.string.application_info_label)
.setUserHandle(mUserHandle)
.setSourceMetricsCategory(mFragment.getMetricsCategory())
.launch();
return true;
}
}
public RecentLocationRequestPreferenceController(Context context, LocationSettings fragment,
Lifecycle lifecycle) {
this(context, fragment, lifecycle, new RecentLocationApps(context));
}
@VisibleForTesting
RecentLocationRequestPreferenceController(Context context, LocationSettings fragment,
Lifecycle lifecycle, RecentLocationApps recentApps) {
super(context, lifecycle);
mFragment = fragment;
mRecentLocationApps = recentApps;
}
@Override
public String getPreferenceKey() {
return KEY_RECENT_LOCATION_REQUESTS;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mCategoryRecentLocationRequests =
(PreferenceCategory) screen.findPreference(KEY_RECENT_LOCATION_REQUESTS);
mSeeAllButton = screen.findPreference(KEY_SEE_ALL_BUTTON);
}
@Override
public void updateState(Preference preference) {
mCategoryRecentLocationRequests.removeAll();
mSeeAllButton.setVisible(false);
final Context prefContext = preference.getContext();
final List<RecentLocationApps.Request> recentLocationRequests =
mRecentLocationApps.getAppListSorted();
if (recentLocationRequests.size() > 3) {
// Display the top 3 preferences to container in original order.
for (int i = 0; i < 3; i++) {
mCategoryRecentLocationRequests.addPreference(
createAppPreference(prefContext, recentLocationRequests.get(i)));
}
// Display a button to list all requests
mSeeAllButton.setVisible(true);
} else if (recentLocationRequests.size() > 0) {
// Add preferences to container in original order (already sorted by recency).
for (RecentLocationApps.Request request : recentLocationRequests) {
mCategoryRecentLocationRequests.addPreference(
createAppPreference(prefContext, request));
}
} else {
// If there's no item to display, add a "No recent apps" item.
final Preference banner = createAppPreference(prefContext);
banner.setTitle(R.string.location_no_recent_apps);
banner.setSelectable(false);
mCategoryRecentLocationRequests.addPreference(banner);
}
}
@Override
public void onLocationModeChanged(int mode, boolean restricted) {
mCategoryRecentLocationRequests.setEnabled(mLocationEnabler.isEnabled(mode));
}
@VisibleForTesting
AppPreference createAppPreference(Context prefContext) {
return new AppPreference(prefContext);
}
@VisibleForTesting
AppPreference createAppPreference(Context prefContext, RecentLocationApps.Request request) {
final AppPreference pref = createAppPreference(prefContext);
pref.setSummary(request.contentDescription);
pref.setIcon(request.icon);
pref.setTitle(request.label);
pref.setOnPreferenceClickListener(new PackageEntryClickedListener(
mFragment, request.packageName, request.userHandle));
return pref;
}
}

View File

@@ -1,93 +0,0 @@
/*
* Copyright 2018 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.location;
import android.content.Context;
import android.provider.SearchIndexableResource;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/** Dashboard Fragment to display all recent location requests, sorted by recency. */
@SearchIndexable
public class RecentLocationRequestSeeAllFragment extends DashboardFragment {
private static final String TAG = "RecentLocationReqAll";
public static final String PATH =
"com.android.settings.location.RecentLocationRequestSeeAllFragment";
@Override
public int getMetricsCategory() {
return MetricsEvent.RECENT_LOCATION_REQUESTS_ALL;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.location_recent_requests_see_all;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return buildPreferenceControllers(context, getSettingsLifecycle(), this);
}
private static List<AbstractPreferenceController> buildPreferenceControllers(
Context context, Lifecycle lifecycle, RecentLocationRequestSeeAllFragment fragment) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(
new RecentLocationRequestSeeAllPreferenceController(context, lifecycle, fragment));
return controllers;
}
/**
* For Search.
*/
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
public List<SearchIndexableResource> getXmlResourcesToIndex(
Context context, boolean enabled) {
final SearchIndexableResource sir = new SearchIndexableResource(context);
sir.xmlResId = R.xml.location_recent_requests_see_all;
return Arrays.asList(sir);
}
@Override
public List<AbstractPreferenceController> getPreferenceControllers(Context
context) {
return buildPreferenceControllers(
context, /* lifecycle = */ null, /* fragment = */ null);
}
};
}

View File

@@ -1,98 +0,0 @@
/*
* Copyright 2018 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.location;
import android.content.Context;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.location.RecentLocationApps;
import com.android.settingslib.widget.apppreference.AppPreference;
import java.util.List;
/** Preference controller for preference category displaying all recent location requests. */
public class RecentLocationRequestSeeAllPreferenceController
extends LocationBasePreferenceController {
/** Key for preference category "All recent location requests" */
private static final String KEY_ALL_RECENT_LOCATION_REQUESTS = "all_recent_location_requests";
private final RecentLocationRequestSeeAllFragment mFragment;
private PreferenceCategory mCategoryAllRecentLocationRequests;
private RecentLocationApps mRecentLocationApps;
public RecentLocationRequestSeeAllPreferenceController(
Context context, Lifecycle lifecycle, RecentLocationRequestSeeAllFragment fragment) {
this(context, lifecycle, fragment, new RecentLocationApps(context));
}
@VisibleForTesting
RecentLocationRequestSeeAllPreferenceController(
Context context,
Lifecycle lifecycle,
RecentLocationRequestSeeAllFragment fragment,
RecentLocationApps recentLocationApps) {
super(context, lifecycle);
mFragment = fragment;
mRecentLocationApps = recentLocationApps;
}
@Override
public String getPreferenceKey() {
return KEY_ALL_RECENT_LOCATION_REQUESTS;
}
@Override
public void onLocationModeChanged(int mode, boolean restricted) {
mCategoryAllRecentLocationRequests.setEnabled(mLocationEnabler.isEnabled(mode));
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mCategoryAllRecentLocationRequests =
(PreferenceCategory) screen.findPreference(KEY_ALL_RECENT_LOCATION_REQUESTS);
}
@Override
public void updateState(Preference preference) {
mCategoryAllRecentLocationRequests.removeAll();
List<RecentLocationApps.Request> requests = mRecentLocationApps.getAppListSorted();
for (RecentLocationApps.Request request : requests) {
Preference appPreference = createAppPreference(preference.getContext(), request);
mCategoryAllRecentLocationRequests.addPreference(appPreference);
}
}
@VisibleForTesting
AppPreference createAppPreference(
Context prefContext, RecentLocationApps.Request request) {
final AppPreference pref = new AppPreference(prefContext);
pref.setSummary(request.contentDescription);
pref.setIcon(request.icon);
pref.setTitle(request.label);
pref.setOnPreferenceClickListener(
new RecentLocationRequestPreferenceController.PackageEntryClickedListener(
mFragment, request.packageName, request.userHandle));
return pref;
}
}

View File

@@ -0,0 +1,99 @@
package com.android.settings.location;
import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.LocationManager;
import android.permission.RuntimePermissionPresenter;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import java.util.Arrays;
import java.util.Collections;
public class TopLevelLocationPreferenceController extends BasePreferenceController implements
LifecycleObserver, OnStart, OnStop {
private static final IntentFilter INTENT_FILTER_LOCATION_MODE_CHANGED =
new IntentFilter(LocationManager.MODE_CHANGED_ACTION);
private final LocationManager mLocationManager;
/** Total number of apps that has location permission. */
private int mNumTotal = -1;
private BroadcastReceiver mReceiver;
private Preference mPreference;
public TopLevelLocationPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public CharSequence getSummary() {
if (mLocationManager.isLocationEnabled()) {
if (mNumTotal == -1) {
return mContext.getString(R.string.location_settings_loading_app_permission_stats);
}
return mContext.getResources().getQuantityString(
R.plurals.location_settings_summary_location_on,
mNumTotal, mNumTotal);
} else {
return mContext.getString(R.string.location_settings_summary_location_off);
}
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
mPreference = preference;
refreshSummary(preference);
// Bail out if location has been disabled.
if (!mLocationManager.isLocationEnabled()) {
return;
}
RuntimePermissionPresenter.getInstance(mContext).countPermissionApps(
Arrays.asList(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION), false, false,
(numApps) -> {
mNumTotal = numApps;
refreshSummary(preference);
}, null);
}
@Override
public void onStart() {
if (mReceiver == null) {
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
refreshLocationMode();
}
};
}
mContext.registerReceiver(mReceiver, INTENT_FILTER_LOCATION_MODE_CHANGED);
refreshLocationMode();
}
@Override
public void onStop() {
mContext.unregisterReceiver(mReceiver);
}
private void refreshLocationMode() {
// 'null' is checked inside updateState(), so no need to check here.
updateState(mPreference);
}
}

View File

@@ -18,7 +18,9 @@ package com.android.settings.network;
import android.content.Context;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.wifi.WifiConnectionPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import androidx.annotation.VisibleForTesting;
@@ -29,9 +31,11 @@ import androidx.preference.PreferenceScreen;
// are two or more active mobile subscriptions. It shows an overview of available network
// connections with an entry for wifi (if connected) and an entry for each subscription.
public class MultiNetworkHeaderController extends BasePreferenceController implements
WifiConnectionPreferenceController.UpdateListener,
SubscriptionsPreferenceController.UpdateListener {
public static final String TAG = "MultiNetworkHdrCtrl";
private WifiConnectionPreferenceController mWifiController;
private SubscriptionsPreferenceController mSubscriptionsController;
private PreferenceCategory mPreferenceCategory;
@@ -40,13 +44,22 @@ public class MultiNetworkHeaderController extends BasePreferenceController imple
}
public void init(Lifecycle lifecycle) {
mWifiController = createWifiController(lifecycle);
mSubscriptionsController = createSubscriptionsController(lifecycle);
// TODO(asargent) - add in a controller for showing wifi status here
}
@VisibleForTesting
WifiConnectionPreferenceController createWifiController(Lifecycle lifecycle) {
final int prefOrder = 0;
return new WifiConnectionPreferenceController(mContext, lifecycle, this, mPreferenceKey,
prefOrder, MetricsProto.MetricsEvent.SETTINGS_NETWORK_CATEGORY);
}
@VisibleForTesting
SubscriptionsPreferenceController createSubscriptionsController(Lifecycle lifecycle) {
return new SubscriptionsPreferenceController(mContext, lifecycle, this, mPreferenceKey, 10);
final int prefStartOrder = 10;
return new SubscriptionsPreferenceController(mContext, lifecycle, this, mPreferenceKey,
prefStartOrder);
}
@Override
@@ -54,6 +67,7 @@ public class MultiNetworkHeaderController extends BasePreferenceController imple
super.displayPreference(screen);
mPreferenceCategory = (PreferenceCategory) screen.findPreference(mPreferenceKey);
mPreferenceCategory.setVisible(isAvailable());
mWifiController.displayPreference(screen);
mSubscriptionsController.displayPreference(screen);
}

View File

@@ -24,6 +24,7 @@ import com.android.settings.R;
import com.android.settings.accounts.AccountFeatureProvider;
import com.android.settings.applications.ApplicationFeatureProvider;
import com.android.settings.biometrics.face.FaceFeatureProvider;
import com.android.settings.bluetooth.BluetoothFeatureProvider;
import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
@@ -114,6 +115,8 @@ public abstract class FeatureFactory {
public abstract FaceFeatureProvider getFaceFeatureProvider();
public abstract BluetoothFeatureProvider getBluetoothFeatureProvider(Context context);
public static final class FactoryNotFoundException extends RuntimeException {
public FactoryNotFoundException(Throwable throwable) {
super("Unable to create factory. Did you misconfigure Proguard?", throwable);

View File

@@ -30,6 +30,8 @@ import com.android.settings.applications.ApplicationFeatureProvider;
import com.android.settings.applications.ApplicationFeatureProviderImpl;
import com.android.settings.biometrics.face.FaceFeatureProvider;
import com.android.settings.biometrics.face.FaceFeatureProviderImpl;
import com.android.settings.bluetooth.BluetoothFeatureProvider;
import com.android.settings.bluetooth.BluetoothFeatureProviderImpl;
import com.android.settings.connecteddevice.dock.DockUpdaterFeatureProviderImpl;
import com.android.settings.core.instrumentation.SettingsMetricsFeatureProvider;
import com.android.settings.dashboard.DashboardFeatureProvider;
@@ -81,6 +83,7 @@ public class FeatureFactoryImpl extends FeatureFactory {
private PanelFeatureProvider mPanelFeatureProvider;
private ContextualCardFeatureProvider mContextualCardFeatureProvider;
private FaceFeatureProvider mFaceFeatureProvider;
private BluetoothFeatureProvider mBluetoothFeatureProvider;
@Override
public SupportFeatureProvider getSupportFeatureProvider(Context context) {
@@ -242,4 +245,13 @@ public class FeatureFactoryImpl extends FeatureFactory {
}
return mFaceFeatureProvider;
}
@Override
public BluetoothFeatureProvider getBluetoothFeatureProvider(Context context) {
if (mBluetoothFeatureProvider == null) {
mBluetoothFeatureProvider = new BluetoothFeatureProviderImpl(
context.getApplicationContext());
}
return mBluetoothFeatureProvider;
}
}

View File

@@ -135,8 +135,9 @@ public class SetupChooseLockPassword extends ChooseLockPassword {
@Override
protected void updateUi() {
super.updateUi();
mSkipButton.setVisibility(mForFingerprint ? View.GONE : View.VISIBLE);
// Show the skip button during SUW but not during Settings > Biometric Enrollment
mSkipButton.setVisibility(View.VISIBLE);
if (mOptionsButton != null) {
mOptionsButton.setVisibility(
mUiStage == Stage.Introduction ? View.VISIBLE : View.GONE);

View File

@@ -70,17 +70,15 @@ public class SetupChooseLockPattern extends ChooseLockPattern {
ChooseLockTypeDialogFragment.newInstance(mUserId)
.show(getChildFragmentManager(), null));
}
// enable skip button only during setup wizard and not with fingerprint flow.
if (!mForFingerprint) {
Button skipButton = view.findViewById(R.id.skip_button);
skipButton.setVisibility(View.VISIBLE);
skipButton.setOnClickListener(v -> {
SetupSkipDialog dialog = SetupSkipDialog.newInstance(
getActivity().getIntent()
.getBooleanExtra(SetupSkipDialog.EXTRA_FRP_SUPPORTED, false));
dialog.show(getFragmentManager());
// Show the skip button during SUW but not during Settings > Biometric Enrollment
Button skipButton = view.findViewById(R.id.skip_button);
skipButton.setVisibility(View.VISIBLE);
skipButton.setOnClickListener(v -> {
SetupSkipDialog dialog = SetupSkipDialog.newInstance(
getActivity().getIntent()
.getBooleanExtra(SetupSkipDialog.EXTRA_FRP_SUPPORTED, false));
dialog.show(getFragmentManager());
});
}
return view;
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2018 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.slices;
import android.content.Context;
import android.util.AttributeSet;
import androidx.slice.Slice;
import androidx.slice.widget.SliceView;
import com.android.settings.R;
import com.android.settingslib.widget.LayoutPreference;
/**
* Preference for {@link SliceView}
*/
public class SlicePreference extends LayoutPreference {
private SliceView mSliceView;
public SlicePreference(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.slicePreferenceStyle);
mSliceView = findViewById(R.id.slice_view);
}
public SlicePreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, R.attr.slicePreferenceStyle);
mSliceView = findViewById(R.id.slice_view);
}
public void onSliceUpdated(Slice slice) {
mSliceView.onChanged(slice);
notifyChanged();
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2018 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.slices;
import android.content.Context;
import android.net.Uri;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.preference.PreferenceScreen;
import androidx.slice.Slice;
import androidx.slice.widget.SliceLiveData;
import androidx.slice.widget.SliceView;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
/**
* Default {@link BasePreferenceController} for {@link SliceView}. It will take {@link Uri} for
* Slice and display what's inside this {@link Uri}
*/
public class SlicePreferenceController extends BasePreferenceController implements
LifecycleObserver, OnStart, OnStop, Observer<Slice> {
@VisibleForTesting
LiveData<Slice> mLiveData;
private SlicePreference mSlicePreference;
private Uri mUri;
public SlicePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mSlicePreference = (SlicePreference) screen.findPreference(
getPreferenceKey());
}
@Override
public int getAvailabilityStatus() {
return mUri != null ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
public void setSliceUri(Uri uri) {
mUri = uri;
mLiveData = SliceLiveData.fromUri(mContext, mUri);
//TODO(b/120803703): figure out why we need to remove observer first
mLiveData.removeObserver(this);
}
@Override
public void onStart() {
if (mLiveData != null) {
mLiveData.observeForever(this);
}
}
@Override
public void onStop() {
if (mLiveData != null) {
mLiveData.removeObserver(this);
}
}
@Override
public void onChanged(Slice slice) {
mSlicePreference.onSliceUpdated(slice);
}
}

View File

@@ -30,6 +30,7 @@ import android.os.Message;
import android.widget.BaseAdapter;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.internal.PreferenceImageView;
@@ -46,7 +47,10 @@ import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.wifi.NetworkRequestErrorDialogFragment.ERROR_DIALOG_TYPE;
import com.android.settingslib.Utils;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.wifi.AccessPoint;
import com.android.settingslib.wifi.WifiTracker;
import com.android.settingslib.wifi.WifiTrackerFactory;
import java.util.ArrayList;
import java.util.List;
@@ -62,10 +66,14 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp
/** Message sent to us to stop scanning wifi and pop up timeout dialog. */
private static final int MESSAGE_STOP_SCAN_WIFI_LIST = 0;
/** Spec defines there should be 5 wifi ap on the list at most. */
private static final int MAX_NUMBER_LIST_ITEM = 5;
/** Delayed time to stop scanning wifi. */
private static final int DELAY_TIME_STOP_SCAN_MS = 30 * 1000;
private List<AccessPoint> mAccessPointList;
private FilterWifiTracker mFilterWifiTracker;
private AccessPointAdapter mDialogAdapter;
private NetworkRequestUserSelectionCallback mUserSelectionCallback;
@@ -159,6 +167,19 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp
if (wifiManager != null) {
wifiManager.unregisterNetworkRequestMatchCallback(this);
}
if (mFilterWifiTracker != null) {
mFilterWifiTracker.onPause();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mFilterWifiTracker != null) {
mFilterWifiTracker.onDestroy();
mFilterWifiTracker = null;
}
}
@Override
@@ -172,6 +193,11 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp
}
// Sets time-out to stop scanning.
mHandler.sendEmptyMessageDelayed(MESSAGE_STOP_SCAN_WIFI_LIST, DELAY_TIME_STOP_SCAN_MS);
if (mFilterWifiTracker == null) {
mFilterWifiTracker = new FilterWifiTracker(getActivity(), getSettingsLifecycle());
}
mFilterWifiTracker.onResume();
}
private final Handler mHandler = new Handler() {
@@ -268,17 +294,33 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp
@Override
public void onMatch(List<ScanResult> scanResults) {
// TODO(b/119846365): Checks if we could escalate the converting effort.
// Converts ScanResult to WifiConfiguration.
List<WifiConfiguration> wifiConfigurations = null;
final WifiManager wifiManager = getContext().getApplicationContext()
.getSystemService(WifiManager.class);
if (wifiManager != null) {
wifiConfigurations = wifiManager.getAllMatchingWifiConfigs(scanResults);
mHandler.removeMessages(MESSAGE_STOP_SCAN_WIFI_LIST);
renewAccessPointList(scanResults);
notifyAdapterRefresh();
}
// Updates internal AccessPoint list from WifiTracker. scanResults are used to update key list
// of AccessPoint, and could be null if there is no necessary to update key list.
private void renewAccessPointList(List<ScanResult> scanResults) {
if (mFilterWifiTracker == null) {
return;
}
setUpAccessPointList(wifiConfigurations);
// TODO(b/119846365): Checks if we could escalate the converting effort.
// Updates keys of scanResults into FilterWifiTracker for updating matched AccessPoints.
if (scanResults != null) {
mFilterWifiTracker.updateKeys(scanResults);
}
// Re-gets matched AccessPoints from WifiTracker.
final List<AccessPoint> list = getAccessPointList();
list.clear();
list.addAll(mFilterWifiTracker.getAccessPoints());
}
@VisibleForTesting
void notifyAdapterRefresh() {
if (getDialogAdapter() != null) {
getDialogAdapter().notifyDataSetChanged();
}
@@ -286,48 +328,99 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp
@Override
public void onUserSelectionConnectSuccess(WifiConfiguration wificonfiguration) {
if (getDialogAdapter() != null) {
updateAccessPointListItem(wificonfiguration);
getDialogAdapter().notifyDataSetChanged();
}
// Dismisses current dialog, since connection is success.
dismiss();
}
@Override
public void onUserSelectionConnectFailure(WifiConfiguration wificonfiguration) {
if (mDialogAdapter != null) {
updateAccessPointListItem(wificonfiguration);
getDialogAdapter().notifyDataSetChanged();
}
stopScanningAndPopErrorDialog(ERROR_DIALOG_TYPE.ABORT);
}
private void updateAccessPointListItem(WifiConfiguration wificonfiguration) {
if (wificonfiguration == null) {
return;
private final class FilterWifiTracker {
private final List<String> mAccessPointKeys;
private final WifiTracker mWifiTracker;
public FilterWifiTracker(Context context, Lifecycle lifecycle) {
mWifiTracker = WifiTrackerFactory.create(context, mWifiListener,
lifecycle, /* includeSaved */ true, /* includeScans */ true);
mAccessPointKeys = new ArrayList<>();
}
final List<AccessPoint> accessPointList = getAccessPointList();
final int accessPointListSize = accessPointList.size();
/**
* Updates key list from input. {@code onMatch()} may be called in multi-times according
* wifi scanning result, so needs patchwork here.
*/
public void updateKeys(List<ScanResult> scanResults) {
for (ScanResult scanResult : scanResults) {
final String key = AccessPoint.getKey(scanResult);
if (!mAccessPointKeys.contains(key)) {
mAccessPointKeys.add(key);
}
}
}
for (int i = 0; i < accessPointListSize; i++) {
AccessPoint accessPoint = accessPointList.get(i);
// It is the same AccessPoint SSID, and should be replaced to update latest properties.
if (accessPoint.matches(wificonfiguration)) {
accessPointList.set(i, new AccessPoint(getContext(), wificonfiguration));
break;
/**
* Returns only AccessPoints whose key is in {@code mAccessPointKeys}.
*
* @return List of matched AccessPoints.
*/
public List<AccessPoint> getAccessPoints() {
final List<AccessPoint> allAccessPoints = mWifiTracker.getAccessPoints();
final List<AccessPoint> result = new ArrayList<>();
// The order should be kept, because order means wifi score (sorting in WifiTracker).
int count = 0;
for (AccessPoint accessPoint : allAccessPoints) {
final String key = accessPoint.getKey();
if (mAccessPointKeys.contains(key)) {
result.add(accessPoint);
count++;
// Limits how many count of items could show.
if (count >= MAX_NUMBER_LIST_ITEM) {
break;
}
}
}
return result;
}
private WifiTracker.WifiListener mWifiListener = new WifiTracker.WifiListener() {
@Override
public void onWifiStateChanged(int state) {
notifyAdapterRefresh();
}
@Override
public void onConnectedChanged() {
notifyAdapterRefresh();
}
@Override
public void onAccessPointsChanged() {
notifyAdapterRefresh();
}
};
public void onDestroy() {
if (mWifiTracker != null) {
mWifiTracker.onDestroy();
}
}
public void onResume() {
if (mWifiTracker != null) {
mWifiTracker.onStart();
}
}
public void onPause() {
if (mWifiTracker != null) {
mWifiTracker.onStop();
}
}
}
private void setUpAccessPointList(List<WifiConfiguration> wifiConfigurations) {
// Grants for zero size input, since maybe current wifi is off or somethings are wrong.
if (wifiConfigurations == null) {
return;
}
final List<AccessPoint> accessPointList = getAccessPointList();
accessPointList.clear();
for (WifiConfiguration config : wifiConfigurations) {
accessPointList.add(new AccessPoint(getContext(), config));
}
}
}

View File

@@ -0,0 +1,177 @@
/*
* Copyright (C) 2018 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.wifi;
import android.content.Context;
import android.os.Bundle;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.wifi.AccessPoint;
import com.android.settingslib.wifi.AccessPointPreference;
import com.android.settingslib.wifi.WifiTracker;
import com.android.settingslib.wifi.WifiTrackerFactory;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
/**
* This places a preference into a PreferenceGroup owned by some parent
* controller class when there is a wifi connection present.
*/
public class WifiConnectionPreferenceController extends AbstractPreferenceController implements
WifiTracker.WifiListener {
private static final String TAG = "WifiConnPrefCtrl";
private static final String KEY = "active_wifi_connection";
private UpdateListener mUpdateListener;
private Context mPrefContext;
private String mPreferenceGroupKey;
private PreferenceGroup mPreferenceGroup;
private WifiTracker mWifiTracker;
private AccessPointPreference mPreference;
private AccessPointPreference.UserBadgeCache mBadgeCache;
private int order;
private int mMetricsCategory;
/**
* Used to notify a parent controller that this controller has changed in availability, or has
* updated the content in the preference that it manages.
*/
public interface UpdateListener {
void onChildrenUpdated();
}
/**
* @param context the context for the UI where we're placing the preference
* @param lifecycle for listening to lifecycle events for the UI
* @param updateListener for notifying a parent controller of changes
* @param preferenceGroupKey the key to use to lookup the PreferenceGroup where this controller
* will add its preference
* @param order the order that the preference added by this controller should use -
* useful when this preference needs to be ordered in a specific way
* relative to others in the PreferenceGroup
* @param metricsCategory - the category to use as the source when handling the click on the
* pref to go to the wifi connection detail page
*/
public WifiConnectionPreferenceController(Context context, Lifecycle lifecycle,
UpdateListener updateListener, String preferenceGroupKey, int order,
int metricsCategory) {
super(context);
mUpdateListener = updateListener;
mPreferenceGroupKey = preferenceGroupKey;
mWifiTracker = WifiTrackerFactory.create(context, this, lifecycle, true /* includeSaved */,
true /* includeScans */);
this.order = order;
mMetricsCategory = metricsCategory;
mBadgeCache = new AccessPointPreference.UserBadgeCache(context.getPackageManager());
}
@Override
public boolean isAvailable() {
return mWifiTracker.isConnected() && getCurrentAccessPoint() != null;
}
@Override
public String getPreferenceKey() {
return KEY;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreferenceGroup = (PreferenceGroup) screen.findPreference(mPreferenceGroupKey);
mPrefContext = screen.getContext();
update();
}
private AccessPoint getCurrentAccessPoint() {
for (AccessPoint accessPoint : mWifiTracker.getAccessPoints()) {
if (accessPoint.isActive()) {
return accessPoint;
}
}
return null;
}
private void updatePreference(AccessPoint accessPoint) {
if (mPreference != null) {
mPreferenceGroup.removePreference(mPreference);
mPreference = null;
}
if (accessPoint == null) {
return;
}
if (mPrefContext != null) {
mPreference = new AccessPointPreference(accessPoint, mPrefContext, mBadgeCache,
R.drawable.ic_wifi_signal_0, false /* forSavedNetworks */);
mPreference.setKey(KEY);
mPreference.refresh();
mPreference.setOrder(order);
mPreference.setOnPreferenceClickListener(pref -> {
Bundle args = new Bundle();
mPreference.getAccessPoint().saveWifiState(args);
new SubSettingLauncher(mPrefContext)
.setTitleRes(R.string.pref_title_network_details)
.setDestination(WifiNetworkDetailsFragment.class.getName())
.setArguments(args)
.setSourceMetricsCategory(mMetricsCategory)
.launch();
return true;
});
mPreferenceGroup.addPreference(mPreference);
}
}
private void update() {
AccessPoint connectedAccessPoint = null;
if (mWifiTracker.isConnected()) {
connectedAccessPoint = getCurrentAccessPoint();
}
if (connectedAccessPoint == null) {
updatePreference(null);
} else {
if (mPreference == null || !mPreference.getAccessPoint().equals(connectedAccessPoint)) {
updatePreference(connectedAccessPoint);
} else if (mPreference != null) {
mPreference.refresh();
}
}
mUpdateListener.onChildrenUpdated();
}
@Override
public void onWifiStateChanged(int state) {
update();
}
@Override
public void onConnectedChanged() {
update();
}
@Override
public void onAccessPointsChanged() {
update();
}
}

View File

@@ -18,6 +18,7 @@ package com.android.settings.wifi.dpp;
import android.os.Bundle;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
/**
@@ -30,6 +31,11 @@ public class WifiDppAddDeviceFragment extends WifiDppQrCodeBaseFragment {
return R.layout.wifi_dpp_add_device_fragment;
}
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_CONFIGURATOR;
}
@Override
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);

View File

@@ -18,6 +18,7 @@ package com.android.settings.wifi.dpp;
import android.os.Bundle;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
/**
@@ -30,6 +31,11 @@ public class WifiDppChooseSavedWifiNetworkFragment extends WifiDppQrCodeBaseFrag
return R.layout.wifi_dpp_choose_saved_wifi_network_fragment;
}
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_CONFIGURATOR;
}
@Override
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);

View File

@@ -27,7 +27,6 @@ import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.core.InstrumentedActivity;
import com.android.settings.R;
@@ -67,8 +66,7 @@ public class WifiDppConfiguratorActivity extends InstrumentedActivity implements
@Override
public int getMetricsCategory() {
//TODO:Should we use a new metrics category for Wi-Fi DPP?
return MetricsProto.MetricsEvent.WIFI_NETWORK_DETAILS;
return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_CONFIGURATOR;
}
@Override

View File

@@ -27,7 +27,6 @@ import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.core.InstrumentedActivity;
import com.android.settings.R;
@@ -47,8 +46,7 @@ public class WifiDppEnrolleeActivity extends InstrumentedActivity {
@Override
public int getMetricsCategory() {
//TODO:Should we use a new metrics category for Wi-Fi DPP?
return MetricsProto.MetricsEvent.WIFI_NETWORK_DETAILS;
return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_ENROLLEE;
}
@Override

View File

@@ -26,8 +26,6 @@ import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.R;
@@ -45,6 +43,7 @@ import com.android.settings.R;
* {@code WifiDppAddDeviceFragment}
*/
public abstract class WifiDppQrCodeBaseFragment extends InstrumentedFragment {
private ImageView mHeaderIcon;
private TextView mTitle;
private TextView mDescription;
@@ -61,12 +60,6 @@ public abstract class WifiDppQrCodeBaseFragment extends InstrumentedFragment {
abstract protected int getLayout();
@Override
public int getMetricsCategory() {
//TODO:Should we use a new metrics category for Wi-Fi DPP?
return MetricsProto.MetricsEvent.WIFI_NETWORK_DETAILS;
}
@Override
public final void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -81,6 +74,7 @@ public abstract class WifiDppQrCodeBaseFragment extends InstrumentedFragment {
}
private void initView(View view) {
mHeaderIcon = view.findViewById(R.id.header_icon);
mTitle = view.findViewById(R.id.title);
mDescription = view.findViewById(R.id.description);
mErrorMessage = view.findViewById(R.id.error_message);
@@ -95,6 +89,10 @@ public abstract class WifiDppQrCodeBaseFragment extends InstrumentedFragment {
mButtonRight = view.findViewById(R.id.button_right);
}
protected void setHeaderIconImageResource(int resId) {
mHeaderIcon.setImageResource(resId);
}
protected void setTitle(String title) {
mTitle.setText(title);
}

View File

@@ -23,6 +23,7 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
/**
@@ -35,6 +36,11 @@ public class WifiDppQrCodeGeneratorFragment extends WifiDppQrCodeBaseFragment {
return R.layout.wifi_dpp_qrcode_generator_fragment;
}
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_CONFIGURATOR;
}
// Container Activity must implement this interface
public interface OnQrCodeGeneratorFragmentAddButtonClickedListener {
public void onQrCodeGeneratorFragmentAddButtonClicked();
@@ -45,6 +51,7 @@ public class WifiDppQrCodeGeneratorFragment extends WifiDppQrCodeBaseFragment {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHeaderIconImageResource(R.drawable.ic_qrcode_24dp);
WifiNetworkConfig wifiNetworkConfig = ((WifiNetworkConfig.Retriever) getActivity())
.getWifiNetworkConfig();
if (!WifiNetworkConfig.isValidConfig(wifiNetworkConfig)) {
@@ -79,7 +86,7 @@ public class WifiDppQrCodeGeneratorFragment extends WifiDppQrCodeBaseFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
MenuItem item = menu.add(0, Menu.FIRST, 0, R.string.next_label);
item.setIcon(R.drawable.ic_menu_add);
item.setIcon(R.drawable.ic_scan_24dp);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
super.onCreateOptionsMenu(menu, inflater);

View File

@@ -33,6 +33,7 @@ import android.view.TextureView;
import android.view.TextureView.SurfaceTextureListener;
import android.view.View;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.wifi.qrcode.QrCamera;
import com.android.settings.wifi.qrcode.QrDecorateView;
@@ -55,6 +56,15 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl
return R.layout.wifi_dpp_qrcode_scanner_fragment;
}
@Override
public int getMetricsCategory() {
if (mConfiguratorMode) {
return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_CONFIGURATOR;
} else {
return MetricsProto.MetricsEvent.SETTINGS_WIFI_DPP_ENROLLEE;
}
}
/**
* Configurator container activity of the fragment should create instance with this constructor.
*/
@@ -79,6 +89,8 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHeaderIconImageResource(R.drawable.ic_scan_24dp);
if (mConfiguratorMode) {
setTitle(getString(R.string.wifi_dpp_add_device_to_network));

View File

@@ -51,6 +51,7 @@ import android.widget.ScrollView;
import androidx.fragment.app.FragmentActivity;
import com.android.settings.testutils.shadow.ShadowUtils;
import com.google.android.setupcompat.item.FooterButton;
import org.junit.Before;
import org.junit.Test;
@@ -386,7 +387,7 @@ public class MasterClearTest {
public void testOnGlobalLayout_shouldNotRemoveListener() {
final ViewTreeObserver viewTreeObserver = mock(ViewTreeObserver.class);
mMasterClear.mScrollView = mScrollView;
mMasterClear.mInitiateButton = mock(Button.class);
mMasterClear.mInitiateButton = mock(FooterButton.class);
doReturn(true).when(mMasterClear).hasReachedBottom(any());
when(mScrollView.getViewTreeObserver()).thenReturn(viewTreeObserver);

View File

@@ -0,0 +1,125 @@
package com.android.settings.applications.specialaccess.financialapps;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import static android.Manifest.permission.SMS_FINANCIAL_TRANSACTIONS;
import static android.Manifest.permission.READ_SMS;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
import java.util.Arrays;
@RunWith(RobolectricTestRunner.class)
public class FinancialAppsControllerTest {
@Mock
private PackageManager mPackageManager;
@Mock
private AppOpsManager mAppOpsManager;
@Mock
private PreferenceScreen mRoot;
@Mock
private Preference mPreference;
private Context mContext;
private PackageInfo mPackageInfoNoPermissionRequested;
private PackageInfo mPackageInfoPermissionRequestedQPlus;
private PackageInfo mPackageInfoPermissionRequestedPreQ;
private FinancialAppsController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOpsManager);
initializePackageInfos();
mController = new FinancialAppsController(mContext, "key");
mController.displayPreference(mRoot);
}
private void initializePackageInfos() {
mPackageInfoNoPermissionRequested = new PackageInfo();
mPackageInfoNoPermissionRequested.applicationInfo = new ApplicationInfo();
mPackageInfoPermissionRequestedQPlus = new PackageInfo();
mPackageInfoPermissionRequestedQPlus.applicationInfo = new ApplicationInfo();
// TODO(b/121161546): update after robolectric test support Q
//mPackageInfoPermissionRequestedQPlus.applicationInfo.targetSdkVersion =
// Build.VERSION_CODES.Q;
mPackageInfoPermissionRequestedQPlus.applicationInfo.uid = 2001;
mPackageInfoPermissionRequestedQPlus.applicationInfo.nonLocalizedLabel = "QPLUS Package";
mPackageInfoPermissionRequestedQPlus.packageName = "QPLUS";
mPackageInfoPermissionRequestedQPlus.requestedPermissions =
new String[] {SMS_FINANCIAL_TRANSACTIONS};
mPackageInfoPermissionRequestedPreQ = new PackageInfo();
mPackageInfoPermissionRequestedPreQ.applicationInfo = new ApplicationInfo();
mPackageInfoPermissionRequestedPreQ.applicationInfo.targetSdkVersion = Build.VERSION_CODES.M;
mPackageInfoPermissionRequestedPreQ.applicationInfo.uid = 2002;
mPackageInfoPermissionRequestedPreQ.applicationInfo.nonLocalizedLabel = "PREQ Package";
mPackageInfoPermissionRequestedPreQ.packageName = "PREQ";
mPackageInfoPermissionRequestedPreQ.requestedPermissions = new String[] {READ_SMS};
}
@Test
public void isAvailable_true() {
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void noPreferenceAddedWhenNoPackageRequestPermission() {
when(mPackageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS))
.thenReturn(new ArrayList<PackageInfo>(
Arrays.asList(mPackageInfoNoPermissionRequested)));
mController.updateState(null);
assertThat(mController.mRoot.getPreferenceCount()).isEqualTo(0);
}
//TODO(b/121161546): Add these tests after robolectric test support Q
/*
@Test
public void preferenceAddedWhenPreQPackageRequestPermission() {
when(mPackageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS))
.thenReturn(new ArrayList<PackageInfo>(
Arrays.asList(mPackageInfoPermissionRequestedPreQ)));
mController.updateState(null);
assertThat(mController.mRoot.getPreferenceCount()).isEqualTo(1);
SwitchPreference pref = (SwitchPreference) mController.mRoot.getPreference(0);
assertThat(pref).isNotNull();
}
@Test
public void preferenceAddedWhenQPlusPackageRequestPermission() {
when(mPackageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS))
.thenReturn(new ArrayList<PackageInfo>(
Arrays.asList(mPackageInfoPermissionRequestedQPlus)));
mController.updateState(null);
assertThat(mController.mRoot.getPreferenceCount()).isEqualTo(1);
SwitchPreference pref = (SwitchPreference) mController.mRoot.getPreference(0);
assertThat(pref).isNotNull();
}*/
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2018 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 android.net.Uri;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class BluetoothFeatureProviderImplTest {
private static final String PARAMETER_KEY = "addr";
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
private BluetoothFeatureProvider mBluetoothFeatureProvider;
@Before
public void setUp() {
mBluetoothFeatureProvider = new BluetoothFeatureProviderImpl(
RuntimeEnvironment.application);
}
@Test
public void getBluetoothDeviceSettingsUri_containCorrectMacAddress() {
final Uri uri = mBluetoothFeatureProvider.getBluetoothDeviceSettingsUri(MAC_ADDRESS);
assertThat(uri.getQueryParameterNames()).containsExactly(PARAMETER_KEY);
assertThat(uri.getQueryParameter(PARAMETER_KEY)).isEqualTo(MAC_ADDRESS);
}
}

View File

@@ -42,13 +42,16 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@RunWith(RobolectricTestRunner.class)
public class ContextualCardManagerTest {
private static final String TEST_SLICE_URI = "context://test/test";
private static final String TEST_SLICE_NAME = "test_name";
@Mock
ContextualCardUpdateListener mListener;
@@ -61,7 +64,8 @@ public class ContextualCardManagerTest {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
final ContextualCardsFragment fragment = new ContextualCardsFragment();
mManager = new ContextualCardManager(mContext, fragment.getSettingsLifecycle());
mManager = new ContextualCardManager(mContext, fragment.getSettingsLifecycle(),
null /* bundle */);
}
@Test
@@ -135,9 +139,74 @@ public class ContextualCardManagerTest {
verify(manager, never()).onContextualCardUpdated(anyMap());
}
@Test
public void onFinishCardLoading_newLaunch_twoLoadedCards_shouldShowTwoCards() {
mManager.mStartTime = System.currentTimeMillis();
mManager.setListener(mListener);
final List<ContextualCard> cards = new ArrayList<>();
cards.add(buildContextualCard(TEST_SLICE_URI));
cards.add(buildContextualCard(TEST_SLICE_URI));
mManager.onFinishCardLoading(cards);
assertThat(mManager.mContextualCards).hasSize(2);
}
@Test
public void onFinishCardLoading_hasSavedCard_shouldOnlyShowSavedCard() {
mManager.setListener(mListener);
final List<String> savedCardNames = new ArrayList<>();
savedCardNames.add(TEST_SLICE_NAME);
mManager.mIsFirstLaunch = false;
mManager.mSavedCards = savedCardNames;
final ContextualCard newCard =
new ContextualCard.Builder()
.setName("test_name2")
.setCardType(ContextualCard.CardType.SLICE)
.setSliceUri(Uri.parse("content://test/test2"))
.build();
final List<ContextualCard> loadedCards = new ArrayList<>();
loadedCards.add(buildContextualCard(TEST_SLICE_URI));
loadedCards.add(newCard);
mManager.onFinishCardLoading(loadedCards);
final List<String> actualCards = mManager.mContextualCards.stream()
.map(ContextualCard::getName)
.collect(Collectors.toList());
final List<String> expectedCards = Arrays.asList(TEST_SLICE_NAME);
assertThat(actualCards).containsExactlyElementsIn(expectedCards);
}
@Test
public void onFinishCardLoading_reloadData_shouldOnlyShowOldCard() {
mManager.setListener(mListener);
mManager.mIsFirstLaunch = false;
//old card
mManager.mContextualCards.add(buildContextualCard(TEST_SLICE_URI));
final ContextualCard newCard =
new ContextualCard.Builder()
.setName("test_name2")
.setCardType(ContextualCard.CardType.SLICE)
.setSliceUri(Uri.parse("content://test/test2"))
.build();
final List<ContextualCard> loadedCards = new ArrayList<>();
loadedCards.add(buildContextualCard(TEST_SLICE_URI));
loadedCards.add(newCard);
mManager.onFinishCardLoading(loadedCards);
final List<String> actualCards = mManager.mContextualCards.stream()
.map(ContextualCard::getName)
.collect(Collectors.toList());
final List<String> expectedCards = Arrays.asList(TEST_SLICE_NAME);
assertThat(actualCards).containsExactlyElementsIn(expectedCards);
}
private ContextualCard buildContextualCard(String sliceUri) {
return new ContextualCard.Builder()
.setName("test_name")
.setName(TEST_SLICE_NAME)
.setCardType(ContextualCard.CardType.SLICE)
.setSliceUri(Uri.parse(sliceUri))
.build();
}

View File

@@ -5,6 +5,10 @@ import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.provider.Settings;
import androidx.lifecycle.LifecycleOwner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -21,11 +25,16 @@ public class AppLocationPermissionPreferenceControllerTest {
@Mock
private Context mContext;
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mController = new AppLocationPermissionPreferenceController(mContext);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mController = new AppLocationPermissionPreferenceController(mContext, mLifecycle);
}
@Test

View File

@@ -84,30 +84,31 @@ public class LocationEnablerTest {
}
@Test
public void onResume_shouldSetActiveAndRegisterListener() {
mEnabler.onResume();
public void onStart_shouldSetActiveAndRegisterListener() {
mEnabler.onStart();
verify(mContext).registerReceiver(eq(mEnabler.mReceiver),
eq(LocationEnabler.INTENT_FILTER_LOCATION_MODE_CHANGED));
}
@Test
public void onResume_shouldRefreshLocationMode() {
mEnabler.onResume();
public void onStart_shouldRefreshLocationMode() {
mEnabler.onStart();
verify(mEnabler).refreshLocationMode();
}
@Test
public void onPause_shouldUnregisterListener() {
mEnabler.onPause();
public void onStop_shouldUnregisterListener() {
mEnabler.onStart();
mEnabler.onStop();
verify(mContext).unregisterReceiver(mEnabler.mReceiver);
}
@Test
public void onReceive_shouldRefreshLocationMode() {
mEnabler.onResume();
mEnabler.onStart();
reset(mListener);
mEnabler.mReceiver.onReceive(mContext, new Intent());

View File

@@ -1,217 +0,0 @@
/*
* 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.location;
import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
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.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
import android.text.TextUtils;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.location.RecentLocationApps;
import com.android.settingslib.location.RecentLocationApps.Request;
import com.android.settingslib.widget.apppreference.AppPreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class RecentLocationRequestPreferenceControllerTest {
@Mock
private LocationSettings mFragment;
@Mock
private PreferenceCategory mCategory;
@Mock
private PreferenceScreen mScreen;
@Mock
private RecentLocationApps mRecentLocationApps;
@Mock
private Preference mSeeAllButton;
private Context mContext;
private RecentLocationRequestPreferenceController mController;
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mController = spy(new RecentLocationRequestPreferenceController(
mContext, mFragment, mLifecycle, mRecentLocationApps));
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mCategory);
when(mScreen.findPreference(mController.KEY_SEE_ALL_BUTTON)).thenReturn(mSeeAllButton);
final String key = mController.getPreferenceKey();
when(mCategory.getKey()).thenReturn(key);
when(mCategory.getContext()).thenReturn(mContext);
}
@Test
public void onLocationModeChanged_LocationOn_shouldEnablePreference() {
mController.displayPreference(mScreen);
mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_BATTERY_SAVING, false);
verify(mCategory).setEnabled(true);
}
@Test
public void onLocationModeChanged_LocationOff_shouldDisablePreference() {
mController.displayPreference(mScreen);
mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_OFF, false);
verify(mCategory).setEnabled(false);
}
@Test
public void updateState_noRecentRequest_shouldRemoveAllAndAddBanner() {
doReturn(new ArrayList<>()).when(mRecentLocationApps).getAppListSorted();
mController.displayPreference(mScreen);
mController.updateState(mCategory);
verify(mCategory).removeAll();
final String title = mContext.getString(R.string.location_no_recent_apps);
verify(mCategory).addPreference(argThat(titleMatches(title)));
}
@Test
public void updateState_hasRecentRequest_shouldRemoveAllAndAddInjectedSettings() {
List<Request> requests = createMockRequests(2);
doReturn(requests).when(mRecentLocationApps).getAppListSorted();
mController.displayPreference(mScreen);
mController.updateState(mCategory);
verify(mCategory).removeAll();
// Verifies two preferences are added in original order
InOrder inOrder = Mockito.inOrder(mCategory);
inOrder.verify(mCategory).addPreference(argThat(titleMatches("appTitle0")));
inOrder.verify(mCategory).addPreference(argThat(titleMatches("appTitle1")));
}
@Test
public void updateState_hasOverThreeRequests_shouldDisplaySeeAllButton() {
List<Request> requests = createMockRequests(6);
when(mRecentLocationApps.getAppListSorted()).thenReturn(requests);
mController.displayPreference(mScreen);
mController.updateState(mCategory);
verify(mCategory).removeAll();
// Verifies the first three preferences are added
InOrder inOrder = Mockito.inOrder(mCategory);
inOrder.verify(mCategory).addPreference(argThat(titleMatches("appTitle0")));
inOrder.verify(mCategory).addPreference(argThat(titleMatches("appTitle1")));
inOrder.verify(mCategory).addPreference(argThat(titleMatches("appTitle2")));
verify(mCategory, never()).addPreference(argThat(titleMatches("appTitle3")));
// Verifies the "See all" preference is visible
verify(mSeeAllButton).setVisible(true);
}
@Test
public void createAppPreference_shouldAddClickListener() {
final Request request = mock(Request.class);
final AppPreference preference = mock(AppPreference.class);
doReturn(preference).when(mController).createAppPreference(any(Context.class));
mController.createAppPreference(mContext, request);
verify(preference).setOnPreferenceClickListener(
any(RecentLocationRequestPreferenceController.PackageEntryClickedListener.class));
}
@Test
public void onPreferenceClick_shouldLaunchAppDetails() {
final Context context = mock(Context.class);
when(mFragment.getContext()).thenReturn(context);
final List<RecentLocationApps.Request> requests = new ArrayList<>();
final Request request = mock(Request.class);
requests.add(request);
doReturn(requests).when(mRecentLocationApps).getAppListSorted();
final AppPreference preference = new AppPreference(mContext);
doReturn(preference).when(mController).createAppPreference(any(Context.class));
mController.displayPreference(mScreen);
mController.updateState(mCategory);
final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
preference.performClick();
verify(context).startActivity(intent.capture());
assertThat(intent.getValue().getStringExtra(EXTRA_SHOW_FRAGMENT))
.isEqualTo(AppInfoDashboardFragment.class.getName());
}
private static ArgumentMatcher<Preference> titleMatches(String expected) {
return preference -> TextUtils.equals(expected, preference.getTitle());
}
private List<RecentLocationApps.Request> createMockRequests(int count) {
List<RecentLocationApps.Request> requests = new ArrayList<>();
for (int i = 0; i < count; i++) {
// Add mock requests
Request req = mock(Request.class, "request" + i);
requests.add(req);
// Map mock AppPreferences with mock requests
String title = "appTitle" + i;
AppPreference appPreference = mock(AppPreference.class, "AppPreference" + i);
doReturn(title).when(appPreference).getTitle();
doReturn(appPreference)
.when(mController).createAppPreference(any(Context.class), eq(req));
}
return requests;
}
}

View File

@@ -1,126 +0,0 @@
/*
* Copyright 2018 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.location;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
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.provider.Settings.Secure;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.location.RecentLocationApps;
import com.android.settingslib.location.RecentLocationApps.Request;
import com.android.settingslib.widget.apppreference.AppPreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Collections;
/** Unit tests for {@link RecentLocationRequestSeeAllPreferenceController} */
@RunWith(RobolectricTestRunner.class)
public class RecentLocationRequestSeeAllPreferenceControllerTest {
@Mock
RecentLocationRequestSeeAllFragment mFragment;
@Mock
private PreferenceScreen mScreen;
@Mock
private PreferenceCategory mCategory;
@Mock
private RecentLocationApps mRecentLocationApps;
private Context mContext;
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
private RecentLocationRequestSeeAllPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mController = spy(
new RecentLocationRequestSeeAllPreferenceController(
mContext, mLifecycle, mFragment, mRecentLocationApps));
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mCategory);
final String key = mController.getPreferenceKey();
when(mCategory.getKey()).thenReturn(key);
when(mCategory.getContext()).thenReturn(mContext);
}
@Test
public void onLocationModeChanged_locationOn_shouldEnablePreference() {
mController.displayPreference(mScreen);
mController.onLocationModeChanged(Secure.LOCATION_MODE_HIGH_ACCURACY, false);
verify(mCategory).setEnabled(true);
}
@Test
public void onLocationModeChanged_locationOff_shouldDisablePreference() {
mController.displayPreference(mScreen);
mController.onLocationModeChanged(Secure.LOCATION_MODE_OFF, false);
verify(mCategory).setEnabled(false);
}
@Test
public void updateState_shouldRemoveAll() {
doReturn(Collections.EMPTY_LIST).when(mRecentLocationApps).getAppListSorted();
mController.displayPreference(mScreen);
mController.updateState(mCategory);
verify(mCategory).removeAll();
}
@Test
public void updateState_hasRecentLocationRequest_shouldAddPreference() {
Request request = mock(Request.class);
AppPreference appPreference = mock(AppPreference.class);
doReturn(appPreference)
.when(mController).createAppPreference(any(Context.class), eq(request));
when(mRecentLocationApps.getAppListSorted())
.thenReturn(new ArrayList<>(Collections.singletonList(request)));
mController.displayPreference(mScreen);
mController.updateState(mCategory);
verify(mCategory).removeAll();
verify(mCategory).addPreference(appPreference);
}
}

View File

@@ -29,6 +29,7 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.telephony.SubscriptionManager;
import com.android.settings.wifi.WifiConnectionPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
@@ -55,6 +56,8 @@ public class MultiNetworkHeaderControllerTest {
@Mock
private PreferenceCategory mPreferenceCategory;
@Mock
private WifiConnectionPreferenceController mWifiController;
@Mock
private SubscriptionsPreferenceController mSubscriptionsController;
@Mock
private SubscriptionManager mSubscriptionManager;
@@ -74,6 +77,7 @@ public class MultiNetworkHeaderControllerTest {
when(mPreferenceScreen.findPreference(eq(KEY_HEADER))).thenReturn(mPreferenceCategory);
mHeaderController = spy(new MultiNetworkHeaderController(mContext, KEY_HEADER));
doReturn(mWifiController).when(mHeaderController).createWifiController(mLifecycle);
doReturn(mSubscriptionsController).when(mHeaderController).createSubscriptionsController(
mLifecycle);
}
@@ -85,8 +89,9 @@ public class MultiNetworkHeaderControllerTest {
// When calling displayPreference, the header itself should only be visible if the
// subscriptions controller says it is available. This is a helper for test cases of this logic.
private void displayPreferenceTest(boolean subscriptionsAvailable,
private void displayPreferenceTest(boolean wifiAvailable, boolean subscriptionsAvailable,
boolean setVisibleExpectedValue) {
when(mWifiController.isAvailable()).thenReturn(wifiAvailable);
when(mSubscriptionsController.isAvailable()).thenReturn(subscriptionsAvailable);
mHeaderController.init(mLifecycle);
@@ -96,13 +101,23 @@ public class MultiNetworkHeaderControllerTest {
}
@Test
public void displayPreference_subscriptionsNotAvailable_categoryIsNotVisible() {
displayPreferenceTest(false, false);
public void displayPreference_bothNotAvailable_categoryIsNotVisible() {
displayPreferenceTest(false, false, false);
}
@Test
public void displayPreference_subscriptionsAvailable_categoryIsVisible() {
displayPreferenceTest(true, true);
public void displayPreference_wifiAvailableButNotSubscriptions_categoryIsNotVisible() {
displayPreferenceTest(true, false, false);
}
@Test
public void displayPreference_subscriptionsAvailableButNotWifi_categoryIsVisible() {
displayPreferenceTest(false, true, true);
}
@Test
public void displayPreference_bothAvailable_categoryIsVisible() {
displayPreferenceTest(true, true, true);
}
@Test

View File

@@ -0,0 +1,161 @@
/*
* Copyright (C) 2018 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.network;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
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.content.Context;
import com.android.settings.wifi.WifiConnectionPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.wifi.AccessPoint;
import com.android.settingslib.wifi.AccessPointPreference;
import com.android.settingslib.wifi.WifiTracker;
import com.android.settingslib.wifi.WifiTrackerFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
@RunWith(RobolectricTestRunner.class)
public class WifiConnectionPreferenceControllerTest {
private static final String KEY = "wifi_connection";
@Mock
WifiTracker mWifiTracker;
@Mock
PreferenceScreen mScreen;
@Mock
PreferenceCategory mPreferenceCategory;
private Context mContext;
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
private WifiConnectionPreferenceController mController;
private int mOnChildUpdatedCount;
private WifiConnectionPreferenceController.UpdateListener mUpdateListener;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
WifiTrackerFactory.setTestingWifiTracker(mWifiTracker);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
when(mScreen.findPreference(eq(KEY))).thenReturn(mPreferenceCategory);
when(mScreen.getContext()).thenReturn(mContext);
mUpdateListener = () -> mOnChildUpdatedCount++;
mController = new WifiConnectionPreferenceController(mContext, mLifecycle, mUpdateListener,
KEY, 0, 0);
}
@Test
public void isAvailable_noWiFiConnection_availableIsFalse() {
when(mWifiTracker.isConnected()).thenReturn(false);
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void displayPreference_noWiFiConnection_noPreferenceAdded() {
when(mWifiTracker.isConnected()).thenReturn(false);
when(mWifiTracker.getAccessPoints()).thenReturn(new ArrayList<>());
mController.displayPreference(mScreen);
verify(mPreferenceCategory, never()).addPreference(any());
}
@Test
public void displayPreference_hasWiFiConnection_preferenceAdded() {
when(mWifiTracker.isConnected()).thenReturn(true);
final AccessPoint accessPoint = mock(AccessPoint.class);
when(accessPoint.isActive()).thenReturn(true);
when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint));
mController.displayPreference(mScreen);
verify(mPreferenceCategory).addPreference(any(AccessPointPreference.class));
}
@Test
public void onConnectedChanged_wifiBecameDisconnected_preferenceRemoved() {
when(mWifiTracker.isConnected()).thenReturn(true);
final AccessPoint accessPoint = mock(AccessPoint.class);
when(accessPoint.isActive()).thenReturn(true);
when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint));
mController.displayPreference(mScreen);
final ArgumentCaptor<AccessPointPreference> captor = ArgumentCaptor.forClass(
AccessPointPreference.class);
verify(mPreferenceCategory).addPreference(captor.capture());
final AccessPointPreference pref = captor.getValue();
when(mWifiTracker.isConnected()).thenReturn(false);
when(mWifiTracker.getAccessPoints()).thenReturn(new ArrayList<>());
final int onUpdatedCountBefore = mOnChildUpdatedCount;
mController.onConnectedChanged();
verify(mPreferenceCategory).removePreference(pref);
assertThat(mOnChildUpdatedCount).isEqualTo(onUpdatedCountBefore + 1);
}
@Test
public void onAccessPointsChanged_wifiBecameConnectedToDifferentAP_preferenceReplaced() {
when(mWifiTracker.isConnected()).thenReturn(true);
final AccessPoint accessPoint1 = mock(AccessPoint.class);
when(accessPoint1.isActive()).thenReturn(true);
when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint1));
mController.displayPreference(mScreen);
final ArgumentCaptor<AccessPointPreference> captor = ArgumentCaptor.forClass(
AccessPointPreference.class);
final AccessPoint accessPoint2 = mock(AccessPoint.class);
when(accessPoint1.isActive()).thenReturn(false);
when(accessPoint2.isActive()).thenReturn(true);
when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint1, accessPoint2));
final int onUpdatedCountBefore = mOnChildUpdatedCount;
mController.onAccessPointsChanged();
verify(mPreferenceCategory, times(2)).addPreference(captor.capture());
final AccessPointPreference pref1 = captor.getAllValues().get(0);
final AccessPointPreference pref2 = captor.getAllValues().get(1);
assertThat(pref1.getAccessPoint()).isEqualTo(accessPoint1);
assertThat(pref2.getAccessPoint()).isEqualTo(accessPoint2);
verify(mPreferenceCategory).removePreference(eq(pref1));
assertThat(mOnChildUpdatedCount).isEqualTo(onUpdatedCountBefore + 1);
}
}

View File

@@ -164,23 +164,6 @@ public class SetupChooseLockPatternTest {
assertThat(findFragment(mActivity).mChosenPattern).isNull();
}
@Test
public void skipButton_shouldNotBeVisible_duringFingerprintFlow() {
final Intent intent =
SetupChooseLockPattern.modifyIntentForSetup(
application,
new IntentBuilder(application)
.setUserId(UserHandle.myUserId())
.setForFingerprint(true)
.build());
mActivity = ActivityController.of(new SetupChooseLockPattern(), intent).setup().get();
Button skipButton = mActivity.findViewById(R.id.skip_button);
assertThat(skipButton).isNotNull();
assertThat(skipButton.getVisibility()).isEqualTo(View.GONE);
}
private ChooseLockPatternFragment findFragment(FragmentActivity activity) {
return (ChooseLockPatternFragment)
activity.getSupportFragmentManager().findFragmentById(R.id.main_content);

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2018 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.slices;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.net.Uri;
import androidx.lifecycle.LiveData;
import androidx.slice.Slice;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class SlicePreferenceControllerTest {
private static final String KEY = "slice_preference_key";
@Mock
private LiveData<Slice> mLiveData;
private Context mContext;
private SlicePreferenceController mController;
private Uri mUri;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mController = new SlicePreferenceController(mContext, KEY);
mController.mLiveData = mLiveData;
mUri = Uri.EMPTY;
}
@Test
public void isAvailable_uriNull_returnFalse() {
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void isAvailable_uriNotNull_returnTrue() {
mController.setSliceUri(mUri);
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void onStart_registerObserver() {
mController.onStart();
verify(mLiveData).observeForever(mController);
}
@Test
public void onStop_unregisterObserver() {
mController.onStop();
verify(mLiveData).removeObserver(mController);
}
}

View File

@@ -24,6 +24,7 @@ import android.content.Context;
import com.android.settings.accounts.AccountFeatureProvider;
import com.android.settings.applications.ApplicationFeatureProvider;
import com.android.settings.biometrics.face.FaceFeatureProvider;
import com.android.settings.bluetooth.BluetoothFeatureProvider;
import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
@@ -66,6 +67,7 @@ public class FakeFeatureFactory extends FeatureFactory {
public final AccountFeatureProvider mAccountFeatureProvider;
public final ContextualCardFeatureProvider mContextualCardFeatureProvider;
public final FaceFeatureProvider mFaceFeatureProvider;
public final BluetoothFeatureProvider mBluetoothFeatureProvider;
public PanelFeatureProvider panelFeatureProvider;
public SlicesFeatureProvider slicesFeatureProvider;
@@ -111,6 +113,7 @@ public class FakeFeatureFactory extends FeatureFactory {
mContextualCardFeatureProvider = mock(ContextualCardFeatureProvider.class);
panelFeatureProvider = mock(PanelFeatureProvider.class);
mFaceFeatureProvider = mock(FaceFeatureProvider.class);
mBluetoothFeatureProvider = mock(BluetoothFeatureProvider.class);
}
@Override
@@ -207,4 +210,9 @@ public class FakeFeatureFactory extends FeatureFactory {
public FaceFeatureProvider getFaceFeatureProvider() {
return mFaceFeatureProvider;
}
@Override
public BluetoothFeatureProvider getBluetoothFeatureProvider(Context context) {
return mBluetoothFeatureProvider;
}
}

View File

@@ -18,9 +18,13 @@ package com.android.settings.wifi;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settingslib.wifi.WifiTracker;
import com.android.settingslib.wifi.WifiTrackerFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -34,6 +38,10 @@ public class NetworkRequestDialogActivityTest {
@Test
public void LaunchActivity_shouldShowNetworkRequestDialog() {
// Mocks fake WifiTracker, in case of exception in NetworkRequestDialogFragment.onResume().
WifiTracker wifiTracker = mock(WifiTracker.class);
WifiTrackerFactory.setTestingWifiTracker(wifiTracker);
Robolectric.setupActivity(NetworkRequestDialogActivity.class);
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();

View File

@@ -51,6 +51,10 @@ import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import com.android.settingslib.wifi.WifiTracker;
import com.android.settingslib.wifi.WifiTrackerFactory;
import org.robolectric.shadows.ShadowLooper;
@RunWith(RobolectricTestRunner.class)
@@ -58,16 +62,21 @@ import org.robolectric.shadows.ShadowLooper;
public class NetworkRequestDialogFragmentTest {
private static final String KEY_SSID = "key_ssid";
private static final String KEY_SECURITY = "key_security";
private FragmentActivity mActivity;
private NetworkRequestDialogFragment networkRequestDialogFragment;
private Context mContext;
private WifiTracker mWifiTracker;
@Before
public void setUp() {
mActivity = Robolectric.setupActivity(FragmentActivity.class);
networkRequestDialogFragment = spy(NetworkRequestDialogFragment.newInstance());
mContext = spy(RuntimeEnvironment.application);
mWifiTracker = mock(WifiTracker.class);
WifiTrackerFactory.setTestingWifiTracker(mWifiTracker);
}
@Test
@@ -140,71 +149,47 @@ public class NetworkRequestDialogFragmentTest {
}
@Test
public void updateAccessPointList_onUserSelectionConnectSuccess_updateCorrectly() {
List<AccessPoint> accessPointList = spy(new ArrayList<>());
Bundle bundle = new Bundle();
bundle.putString(KEY_SSID, "Test AP 1");
accessPointList.add(new AccessPoint(mContext, bundle));
bundle.putString(KEY_SSID, "Test AP 2");
accessPointList.add(new AccessPoint(mContext, bundle));
bundle.putString(KEY_SSID, "Test AP 3");
accessPointList.add(new AccessPoint(mContext, bundle));
bundle.putString(KEY_SSID, "Test AP 4");
accessPointList.add(new AccessPoint(mContext, bundle));
public void updateAccessPointList_onUserSelectionConnectSuccess_shouldCloseTheDialog() {
List<AccessPoint> accessPointList = createAccessPointList();
when(networkRequestDialogFragment.getAccessPointList()).thenReturn(accessPointList);
networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), null);
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(alertDialog.isShowing()).isTrue();
// Test if config would update list.
WifiConfiguration config = new WifiConfiguration();
config.SSID = "Test AP 3";
networkRequestDialogFragment.onUserSelectionConnectSuccess(config);
AccessPoint verifyAccessPoint = new AccessPoint(mContext, config);
verify(accessPointList, times(1)).set(2, verifyAccessPoint);
assertThat(alertDialog.isShowing()).isFalse();
}
@Test
public void updateAccessPointList_onUserSelectionConnectFailure_updateCorrectly() {
List<AccessPoint> accessPointList = spy(new ArrayList<>());
Bundle bundle = new Bundle();
bundle.putString(KEY_SSID, "Test AP 1");
accessPointList.add(new AccessPoint(mContext, bundle));
bundle.putString(KEY_SSID, "Test AP 2");
accessPointList.add(new AccessPoint(mContext, bundle));
bundle.putString(KEY_SSID, "Test AP 3");
accessPointList.add(new AccessPoint(mContext, bundle));
bundle.putString(KEY_SSID, "Test AP 4");
accessPointList.add(new AccessPoint(mContext, bundle));
public void updateAccessPointList_onUserSelectionConnectFailure_shouldCallTimeoutDialog() {
FakeNetworkRequestDialogFragment fakeFragment = new FakeNetworkRequestDialogFragment();
FakeNetworkRequestDialogFragment spyFakeFragment = spy(fakeFragment);
List<AccessPoint> accessPointList = createAccessPointList();
when(spyFakeFragment.getAccessPointList()).thenReturn(accessPointList);
spyFakeFragment.show(mActivity.getSupportFragmentManager(), null);
when(networkRequestDialogFragment.getAccessPointList()).thenReturn(accessPointList);
networkRequestDialogFragment.show(mActivity.getSupportFragmentManager(), null);
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(alertDialog.isShowing()).isTrue();
// Test if config would update list.
WifiConfiguration config = new WifiConfiguration();
config.SSID = "Test AP 3";
networkRequestDialogFragment.onUserSelectionConnectFailure(config);
fakeFragment.onUserSelectionConnectFailure(config);
AccessPoint verifyAccessPoint = new AccessPoint(mContext, config);
verify(accessPointList, times(1)).set(2, verifyAccessPoint);
assertThat(fakeFragment.bCalledStopAndPop).isTrue();
}
@Test
public void onUserSelectionCallbackRegistration_shouldCallSelect() {
List<AccessPoint> accessPointList = spy(new ArrayList<>());
Bundle bundle = new Bundle();
bundle.putString(KEY_SSID, "Test AP 1");
accessPointList.add(new AccessPoint(mContext, bundle));
bundle.putString(KEY_SSID, "Test AP 2");
accessPointList.add(new AccessPoint(mContext, bundle));
bundle.putString(KEY_SSID, "Test AP 3");
AccessPoint clickedAccessPoint = new AccessPoint(mContext, bundle);
public void onUserSelectionCallbackRegistration_onClick_shouldCallSelect() {
// Assert.
final int indexClickItem = 3;
List<AccessPoint> accessPointList = createAccessPointList();
AccessPoint clickedAccessPoint = accessPointList.get(indexClickItem);
clickedAccessPoint.generateOpenNetworkConfig();
accessPointList.add(clickedAccessPoint);
bundle.putString(KEY_SSID, "Test AP 4");
accessPointList.add(new AccessPoint(mContext, bundle));
when(networkRequestDialogFragment.getAccessPointList()).thenReturn(accessPointList);
NetworkRequestUserSelectionCallback selectionCallback = mock(
@@ -212,40 +197,66 @@ public class NetworkRequestDialogFragmentTest {
AlertDialog dialog = mock(AlertDialog.class);
networkRequestDialogFragment.onUserSelectionCallbackRegistration(selectionCallback);
networkRequestDialogFragment.onClick(dialog, 2);
// Act.
networkRequestDialogFragment.onClick(dialog, indexClickItem);
// Check.
verify(selectionCallback, times(1)).select(clickedAccessPoint.getConfig());
}
@Test
public void onMatch_shouldUpdatedList() {
// Prepares WifiManager.
// Assert.
when(networkRequestDialogFragment.getContext()).thenReturn(mContext);
Context applicationContext = spy(RuntimeEnvironment.application.getApplicationContext());
when(mContext.getApplicationContext()).thenReturn(applicationContext);
WifiManager wifiManager = mock(WifiManager.class);
when(applicationContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(wifiManager);
networkRequestDialogFragment.onResume();
List<AccessPoint> accessPointList = createAccessPointList();
when(mWifiTracker.getAccessPoints()).thenReturn(accessPointList);
List<WifiConfiguration> wifiConfigurationList = new ArrayList<>();
WifiConfiguration config = new WifiConfiguration();
final String SSID_AP1 = "Test AP 1";
config.SSID = SSID_AP1;
wifiConfigurationList.add(config);
config = new WifiConfiguration();
final String SSID_AP2 = "Test AP 2";
config.SSID = SSID_AP2;
wifiConfigurationList.add(config);
// Prepares callback converted data.
List<ScanResult> scanResults = new ArrayList<>();
when(wifiManager.getAllMatchingWifiConfigs(scanResults)).thenReturn(wifiConfigurationList);
ScanResult scanResult = new ScanResult();
scanResult.SSID = SSID_AP1;
scanResult.capabilities = "WEP";
scanResults.add(scanResult);
scanResult = new ScanResult();
scanResult.SSID = SSID_AP2;
scanResult.capabilities = "WEP";
scanResults.add(scanResult);
// Act.
networkRequestDialogFragment.onMatch(scanResults);
List<AccessPoint> accessPointList = networkRequestDialogFragment.getAccessPointList();
assertThat(accessPointList).isNotEmpty();
assertThat(accessPointList.size()).isEqualTo(2);
assertThat(accessPointList.get(0).getSsid()).isEqualTo(SSID_AP1);
assertThat(accessPointList.get(1).getSsid()).isEqualTo(SSID_AP2);
// Check.
List<AccessPoint> returnList = networkRequestDialogFragment.getAccessPointList();
assertThat(returnList).isNotEmpty();
assertThat(returnList.size()).isEqualTo(2);
assertThat(returnList.get(0).getSsid()).isEqualTo(SSID_AP1);
assertThat(returnList.get(1).getSsid()).isEqualTo(SSID_AP2);
}
private List<AccessPoint> createAccessPointList() {
List<AccessPoint> accessPointList = spy(new ArrayList<>());
Bundle bundle = new Bundle();
bundle.putString(KEY_SSID, "Test AP 1");
bundle.putInt(KEY_SECURITY, 1);
accessPointList.add(new AccessPoint(mContext, bundle));
bundle.putString(KEY_SSID, "Test AP 2");
bundle.putInt(KEY_SECURITY, 1);
accessPointList.add(new AccessPoint(mContext, bundle));
bundle.putString(KEY_SSID, "Test AP 3");
bundle.putInt(KEY_SECURITY, 2);
AccessPoint clickedAccessPoint = new AccessPoint(mContext, bundle);
accessPointList.add(clickedAccessPoint);
bundle.putString(KEY_SSID, "Test AP 4");
bundle.putInt(KEY_SECURITY, 0);
accessPointList.add(new AccessPoint(mContext, bundle));
return accessPointList;
}
}