Snap for 5262136 from e56df8877a to qt-release
Change-Id: Icdd5c6facb754c84367b2188739fd4a053ec9c4e
This commit is contained in:
@@ -1249,12 +1249,11 @@
|
||||
android:label="@string/local_backup_password_title"
|
||||
android:exported="false" />
|
||||
|
||||
<activity android:name="CredentialStorage"
|
||||
<activity android:name=".security.CredentialStorage"
|
||||
android:theme="@style/Transparent"
|
||||
android:launchMode="singleTop"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize">
|
||||
<intent-filter android:priority="1">
|
||||
<action android:name="com.android.credentials.UNLOCK" />
|
||||
<action android:name="com.android.credentials.INSTALL" />
|
||||
<action android:name="com.android.credentials.RESET" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
@@ -2978,6 +2977,10 @@
|
||||
<action android:name="android.settings.panel.action.VOLUME" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.settings.panel.action.NFC" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<provider android:name=".slices.SettingsSliceProvider"
|
||||
|
||||
@@ -51,6 +51,18 @@
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:textColor="?android:attr/colorAccent"/>
|
||||
|
||||
<TextView
|
||||
style="@style/device_info_dialog_label"
|
||||
android:id="@+id/module_version_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/module_version"/>
|
||||
<TextView
|
||||
style="@style/device_info_dialog_value"
|
||||
android:id="@+id/module_version_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
style="@style/device_info_dialog_label"
|
||||
android:id="@+id/baseband_version_label"
|
||||
|
||||
@@ -85,12 +85,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
FaceEnrollAccessibilitySwitch:messageText="@string/security_settings_face_enroll_introduction_accessibility_diversity"/>
|
||||
|
||||
<com.android.settings.biometrics.face.FaceEnrollAccessibilityToggle
|
||||
android:id="@+id/toggle_vision"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
FaceEnrollAccessibilitySwitch:messageText="@string/security_settings_face_enroll_introduction_accessibility_vision"/>
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="@dimen/homepage_card_dismissal_margin_bottom"
|
||||
android:gravity="bottom|end">
|
||||
|
||||
<Button
|
||||
@@ -49,8 +48,8 @@
|
||||
style="@style/ContextualCardDismissalButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/homepage_card_dismissal_button_side_margin"
|
||||
android:layout_marginEnd="@dimen/homepage_card_dismissal_button_side_margin"
|
||||
android:layout_marginStart="@dimen/homepage_card_dismissal_button_margin_start"
|
||||
android:layout_marginEnd="@dimen/homepage_card_dismissal_button_margin_end"
|
||||
android:text="@string/contextual_card_dismiss_keep"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
android:layout_weight="1"
|
||||
android:textSize="18sp"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
style="@style/info_label"/>
|
||||
|
||||
<ProgressBar
|
||||
|
||||
@@ -335,10 +335,10 @@
|
||||
<dimen name="homepage_half_card_title_margin_top">12dp</dimen>
|
||||
|
||||
<!-- Homepage dismissal cards size and padding -->
|
||||
<dimen name="homepage_card_dismissal_margin_top">16dp</dimen>
|
||||
<dimen name="homepage_card_dismissal_margin_bottom">2dp</dimen>
|
||||
<dimen name="homepage_card_dismissal_margin_top">12dp</dimen>
|
||||
<dimen name="homepage_card_dismissal_side_margin">16dp</dimen>
|
||||
<dimen name="homepage_card_dismissal_button_side_margin">6dp</dimen>
|
||||
<dimen name="homepage_card_dismissal_button_margin_start">4dp</dimen>
|
||||
<dimen name="homepage_card_dismissal_button_margin_end">6dp</dimen>
|
||||
|
||||
<!-- Horizontal divider size and margin -->
|
||||
<dimen name="horizontal_divider_margin_top">4dp</dimen>
|
||||
|
||||
@@ -1430,6 +1430,16 @@
|
||||
<!-- Title shown on security settings to allow the user to change their lockscreen password [CHAR LIMIT=22]-->
|
||||
<string name="unlock_change_lock_password_title">Change unlock password</string>
|
||||
|
||||
<!-- Footer preference text in the screen lock type picker to indicate which app is requesting a new screen lock and that it requests for a strong PIN or password [CHAR LIMIT=NONE] -->
|
||||
<string name="unlock_footer_high_complexity_requested"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> requests a strong PIN or password.</string>
|
||||
<!-- Footer preference text in the screen lock type picker to indicate which app is requesting a new screen lock and that it requests for a medium strength PIN or password [CHAR LIMIT=NONE] -->
|
||||
<string name="unlock_footer_medium_complexity_requested"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> requests a new PIN or password.</string>
|
||||
<!-- Footer preference text in the screen lock type picker to indicate which app is requesting a new screen lock and it requests for any screen lock [CHAR LIMIT=NONE] -->
|
||||
<string name="unlock_footer_low_complexity_requested"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> requests a new pattern, PIN or password.</string>
|
||||
<!-- Footer preference text in the screen lock type picker to indicate which app is requesting a new screen lock [CHAR LIMIT=NONE] -->
|
||||
<string name="unlock_footer_none_complexity_requested"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> requests a new screen lock.</string>
|
||||
|
||||
|
||||
<!-- Message shown on the lock screen when the user incorrectly enters their lock and it counts towards the max attempts before their data on the device is wiped. [CHAR LIMIT=NONE] -->
|
||||
<string name="lock_failed_attempts_before_wipe">Try again. Attempt <xliff:g id="current_attempts">%1$d</xliff:g> of <xliff:g id="total_attempts">%2$d</xliff:g>.</string>
|
||||
|
||||
@@ -2902,6 +2912,8 @@
|
||||
<string name="kernel_version">Kernel version</string>
|
||||
<!-- About phone screen, setting option name [CHAR LIMIT=40] -->
|
||||
<string name="build_number">Build number</string>
|
||||
<!-- About phone screen, mainline module versions [CHAR LIMIT=40] -->
|
||||
<string name="module_version">Mainline module versions</string>
|
||||
|
||||
<!-- About phone screen, show when a value of some status item is unavailable. -->
|
||||
<string name="device_info_not_available">Not available</string>
|
||||
@@ -4444,6 +4456,10 @@
|
||||
<string name="keyboard_shortcuts_helper">Keyboard shortcuts helper</string>
|
||||
<!-- Summary text for the 'keyboard shortcuts helper' dialog. [CHAR LIMIT=100] -->
|
||||
<string name="keyboard_shortcuts_helper_summary">Display available shortcuts</string>
|
||||
<!-- Title for the 'Work profile' preference category inside Languages and inputs'. [CHAR LIMIT=45] -->
|
||||
<string name="language_and_input_for_work_category_title">Work profile</string>
|
||||
<!-- Title for the 'Virtual keyboards for work' preference. [CHAR LIMIT=45] -->
|
||||
<string name="virtual_keyboards_for_work_title">Virtual keyboard for work</string>
|
||||
|
||||
<!-- Summary text for keyboards when no layout has been selected. [CHAR LIMIT=35] -->
|
||||
<string name="default_keyboard_layout">Default</string>
|
||||
@@ -4475,6 +4491,8 @@
|
||||
<!-- User dictionary settings --><skip />
|
||||
<!-- User dictionary settings. The title of the list item to go into the Personal dictionary settings screen. [CHAR LIMIT=35] -->
|
||||
<string name="user_dict_settings_title">Personal dictionary</string>
|
||||
<!-- Title for the 'Spell checker for work' preference. [CHAR LIMIT=45] -->
|
||||
<string name="user_dict_settings_for_work_title">Personal dictionary for work</string>
|
||||
<!-- User dictionary settings. The summary of the list item to go into the Personal dictionary settings screen. -->
|
||||
<string name="user_dict_settings_summary">""</string>
|
||||
<!-- User dictionary settings. The title of the menu item to add a new word to the user dictionary. -->
|
||||
@@ -5731,10 +5749,6 @@
|
||||
<string name="credentials_erased">Credential storage is erased.</string>
|
||||
<!-- Toast message [CHAR LIMIT=30] when credential storage containing private keys and certificates could not be erased (opposite of string credentials_erased) -->
|
||||
<string name="credentials_not_erased">Credential storage couldn\u2019t be erased.</string>
|
||||
<!-- This string is in a dialog, and the dialog shows up on a device that's managed by a user's company. It lets the user know that they need to have a secure lock screen (PIN, password, or pattern) before they can use credential storage [CHAR LIMIT=NONE] -->
|
||||
<string name="credentials_configure_lock_screen_hint">Before you can use credential storage, your device need to have a secure lock screen</string>
|
||||
<!-- This string is for the content of the button that leads user to lock screen settings [CHAR LIMIT=20] -->
|
||||
<string name="credentials_configure_lock_screen_button">SET LOCK</string>
|
||||
<!-- Title of Usage Access preference item [CHAR LIMIT=30] -->
|
||||
<string name="usage_access_title">Apps with usage access</string>
|
||||
|
||||
@@ -6368,6 +6382,8 @@
|
||||
|
||||
<!-- Title for spell checker settings -->
|
||||
<string name="spellcheckers_settings_title">Spell checker</string>
|
||||
<!-- Title for spell checker settings for work [CHAR LIMIT=45]-->
|
||||
<string name="spellcheckers_settings_for_work_title">Spell checker for work</string>
|
||||
|
||||
<!-- Prompt for the user to enter their current full-backup password -->
|
||||
<string name="current_backup_pw_prompt">Type your current full backup password here</string>
|
||||
@@ -10363,6 +10379,17 @@
|
||||
<string name="mobile_data_settings_title">Mobile data</string>
|
||||
<!-- Mobile network settings screen, title of Mobile data switch preference [CHAR LIMIT=NONE] -->
|
||||
<string name="mobile_data_settings_summary">Access data using mobile network</string>
|
||||
<!-- Mobile network settings screen, title of item showing the name of the default subscription
|
||||
that will be used for calls. This only appears in multi-SIM mode. [CHAR LIMIT=NONE] -->
|
||||
<string name="calls_preference">Calls preference</string>
|
||||
<!-- Mobile network settings screen, title of item showing the name of the default subscription
|
||||
that will be used for SMS messages. This only appears in multi-SIM mode. [CHAR LIMIT=NONE] -->
|
||||
<string name="sms_preference">SMS preference</string>
|
||||
<!-- Mobile network settings screen, a label in a chooser dialog that appears when choosing the
|
||||
default subscription to use for either calls or SMS when in multi-SIM mode. This label means
|
||||
that the user will be asked which mobile network subscription to use every time they place a
|
||||
call or send an SMS, instead of defaulting to one particular subscription. [CHAR LIMIT=40]-->
|
||||
<string name="calls_and_sms_ask_every_time">Ask every time</string>
|
||||
|
||||
<!-- Summary of the 'Mobile network' item on the Network & internet page when there is no mobile
|
||||
service setup yet (eg no SIM card inserted and no eSIM configured). Tapping it leads to a
|
||||
@@ -10450,8 +10477,8 @@
|
||||
<!-- See less items in contextual homepage [CHAR LIMIT=30]-->
|
||||
<string name="see_less">See less</string>
|
||||
|
||||
<!-- Title for Network connection request Dialog [CHAR LIMIT=30] -->
|
||||
<string name="network_connection_request_dialog_title">Choose a device</string>
|
||||
<!-- Title for Network connection request Dialog [CHAR LIMIT=60] -->
|
||||
<string name="network_connection_request_dialog_title">Device to use with <xliff:g id="appName" example="ThirdPartyAppName">%1$s</xliff:g></string>
|
||||
<!-- Message for Network connection timeout Dialog [CHAR LIMIT=NONE] -->
|
||||
<string name="network_connection_timeout_dialog_message">No devices found. Make sure devices are turned on and available to connect.</string>
|
||||
<!-- OK button for Network connection timeout Dialog [CHAR LIMIT=30] -->
|
||||
|
||||
@@ -460,8 +460,7 @@
|
||||
|
||||
<style name="ContextualCardDismissalButton"
|
||||
parent="android:Widget.DeviceDefault.Button.Borderless.Colored">
|
||||
<item name="android:minWidth">10dp</item>
|
||||
<item name="android:minHeight">16dp</item>
|
||||
<item name="android:minWidth">24dp</item>
|
||||
<item name="android:textAllCaps">false</item>
|
||||
</style>
|
||||
|
||||
|
||||
@@ -92,4 +92,27 @@
|
||||
android:summary="@string/vibrate_input_devices_summary"
|
||||
settings:controller="com.android.settings.inputmethod.GameControllerPreferenceController" />
|
||||
|
||||
<com.android.settings.widget.WorkOnlyCategory
|
||||
android:key="language_and_input_for_work_category"
|
||||
android:title="@string/language_and_input_for_work_category_title">
|
||||
|
||||
<Preference
|
||||
android:key="virtual_keyboards_for_work_pref"
|
||||
android:title="@string/virtual_keyboards_for_work_title"
|
||||
android:fragment="com.android.settings.inputmethod.VirtualKeyboardFragment"
|
||||
settings:controller="com.android.settings.inputmethod.VirtualKeyboardForWorkPreferenceController" />
|
||||
|
||||
<Preference
|
||||
android:key="spellcheckers_settings_for_work_pref"
|
||||
android:title="@string/spellcheckers_settings_for_work_title"
|
||||
android:fragment="com.android.settings.inputmethod.SpellCheckersSettings"
|
||||
settings:controller="com.android.settings.language.UserDictionaryForWorkPreferenceController" />
|
||||
|
||||
<Preference
|
||||
android:key="user_dictionary_settings_for_work_pref"
|
||||
android:title="@string/user_dict_settings_for_work_title"
|
||||
android:fragment="com.android.settings.inputmethod.UserDictionaryList"
|
||||
settings:controller="com.android.settings.inputmethod.SpellCheckerForWorkPreferenceController" />
|
||||
</com.android.settings.widget.WorkOnlyCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
<com.android.settings.widget.GearPreference
|
||||
android:key="default_assist"
|
||||
android:title="@string/default_assist_title"
|
||||
android:summary="@string/summary_placeholder"
|
||||
android:fragment="com.android.settings.applications.assist.DefaultAssistPicker" />
|
||||
android:summary="@string/summary_placeholder" />
|
||||
|
||||
<Preference
|
||||
android:key="gesture_assist_application"
|
||||
|
||||
@@ -17,24 +17,37 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:key="mobile_network_pref_screen"
|
||||
settings:initialExpandedChildrenCount="5">
|
||||
settings:initialExpandedChildrenCount="7">
|
||||
|
||||
<com.android.settings.datausage.DataUsageSummaryPreference
|
||||
android:key="status_header"
|
||||
android:visibility="gone"
|
||||
android:selectable="false" />
|
||||
|
||||
<ListPreference
|
||||
android:key="calls_preference"
|
||||
android:title="@string/calls_preference"
|
||||
settings:controller="com.android.settings.network.telephony.CallsDefaultSubscriptionController"
|
||||
settings:allowDividerAbove="true" />
|
||||
|
||||
<ListPreference
|
||||
android:key="sms_preference"
|
||||
android:title="@string/sms_preference"
|
||||
settings:controller="com.android.settings.network.telephony.SmsDefaultSubscriptionController" />
|
||||
|
||||
<Preference
|
||||
android:key="cdma_lte_data_service_key"
|
||||
android:title="@string/cdma_lte_data_service"
|
||||
settings:controller="com.android.settings.network.telephony.DataServiceSetupPreferenceController">
|
||||
</Preference>
|
||||
settings:controller="com.android.settings.network.telephony.DataServiceSetupPreferenceController"
|
||||
settings:allowDividerAbove="true"
|
||||
settings:allowDividerBelow="false" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="mobile_data_enable"
|
||||
android:title="@string/mobile_data_settings_title"
|
||||
android:summary="@string/mobile_data_settings_summary"
|
||||
settings:controller="com.android.settings.network.telephony.MobileDataPreferenceController"/>
|
||||
settings:controller="com.android.settings.network.telephony.MobileDataPreferenceController"
|
||||
settings:allowDividerAbove="true"/>
|
||||
|
||||
<com.android.settingslib.RestrictedSwitchPreference
|
||||
android:key="button_roaming_key"
|
||||
|
||||
@@ -40,8 +40,8 @@ import com.android.settings.core.InstrumentedFragment;
|
||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.ButtonFooterMixin;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
import java.util.List;
|
||||
@@ -150,8 +150,8 @@ public class EncryptionInterstitial extends SettingsActivity {
|
||||
GlifLayout layout = (GlifLayout) view;
|
||||
layout.setHeaderText(getActivity().getTitle());
|
||||
|
||||
final ButtonFooterMixin buttonFooterMixin = layout.getMixin(ButtonFooterMixin.class);
|
||||
buttonFooterMixin.setSecondaryButton(
|
||||
final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
|
||||
mixin.setSecondaryButton(
|
||||
new FooterButton.Builder(getContext())
|
||||
.setText(R.string.encryption_interstitial_no)
|
||||
.setListener(this::onNoButtonClicked)
|
||||
@@ -160,7 +160,7 @@ public class EncryptionInterstitial extends SettingsActivity {
|
||||
.build()
|
||||
);
|
||||
|
||||
buttonFooterMixin.setPrimaryButton(
|
||||
mixin.setPrimaryButton(
|
||||
new FooterButton.Builder(getContext())
|
||||
.setText(R.string.encryption_interstitial_yes)
|
||||
.setListener(this::onYesButtonClicked)
|
||||
|
||||
@@ -66,9 +66,9 @@ import com.android.settings.password.ConfirmLockPattern;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
|
||||
import com.google.android.setupcompat.TemplateLayout;
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.item.FooterButton.ButtonType;
|
||||
import com.google.android.setupcompat.template.ButtonFooterMixin;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupcompat.template.FooterButton.ButtonType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -417,8 +417,8 @@ 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.setPrimaryButton(
|
||||
final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
|
||||
mixin.setPrimaryButton(
|
||||
new FooterButton.Builder(getActivity())
|
||||
.setText(R.string.master_clear_button_text)
|
||||
.setListener(mInitiateListener)
|
||||
@@ -426,7 +426,7 @@ public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutL
|
||||
.setTheme(R.style.SudGlifButton_Primary)
|
||||
.build()
|
||||
);
|
||||
mInitiateButton = buttonFooterMixin.getPrimaryButton();
|
||||
mInitiateButton = mixin.getPrimaryButton();
|
||||
}
|
||||
|
||||
private void getContentDescription(View v, StringBuffer description) {
|
||||
|
||||
@@ -45,9 +45,9 @@ import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
|
||||
import com.google.android.setupcompat.TemplateLayout;
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.item.FooterButton.ButtonType;
|
||||
import com.google.android.setupcompat.template.ButtonFooterMixin;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupcompat.template.FooterButton.ButtonType;
|
||||
|
||||
/**
|
||||
* Confirm and execute a reset of the device to a clean "just out of the box"
|
||||
@@ -154,8 +154,8 @@ public class MasterClearConfirm extends InstrumentedFragment {
|
||||
private void establishFinalConfirmationState() {
|
||||
final TemplateLayout layout = mContentView.findViewById(R.id.setup_wizard_layout);
|
||||
|
||||
final ButtonFooterMixin buttonFooterMixin = layout.getMixin(ButtonFooterMixin.class);
|
||||
buttonFooterMixin.setPrimaryButton(
|
||||
final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
|
||||
mixin.setPrimaryButton(
|
||||
new FooterButton.Builder(getActivity())
|
||||
.setText(R.string.master_clear_button_text)
|
||||
.setListener(mFinalClickListener)
|
||||
|
||||
@@ -32,7 +32,6 @@ import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.text.TextUtils;
|
||||
import android.transition.TransitionManager;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -103,8 +102,6 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
*/
|
||||
public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
|
||||
|
||||
public static final String BACK_STACK_PREFS = ":settings:prefs";
|
||||
|
||||
// extras that allow any preference activity to be launched as part of a wizard
|
||||
|
||||
// show Back and Next buttons? takes boolean parameter
|
||||
@@ -374,14 +371,13 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
setTitleFromIntent(intent);
|
||||
|
||||
Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
|
||||
switchToFragment(initialFragmentName, initialArguments, true, false,
|
||||
mInitialTitleResId, mInitialTitle, false);
|
||||
switchToFragment(initialFragmentName, initialArguments, true,
|
||||
mInitialTitleResId, mInitialTitle);
|
||||
} else {
|
||||
// Show search icon as up affordance if we are displaying the main Dashboard
|
||||
mInitialTitleResId = R.string.dashboard_title;
|
||||
switchToFragment(TopLevelSettings.class.getName(), null /* args */, false, false,
|
||||
mInitialTitleResId, mInitialTitle, false);
|
||||
|
||||
switchToFragment(TopLevelSettings.class.getName(), null /* args */, false,
|
||||
mInitialTitleResId, mInitialTitle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,7 +563,7 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
* Switch to a specific Fragment with taking care of validation, Title and BackStack
|
||||
*/
|
||||
private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
|
||||
boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
|
||||
int titleResId, CharSequence title) {
|
||||
Log.d(LOG_TAG, "Switching to fragment " + fragmentName);
|
||||
if (validate && !isValidFragment(fragmentName)) {
|
||||
throw new IllegalArgumentException("Invalid fragment for this activity: "
|
||||
@@ -576,12 +572,6 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
Fragment f = Fragment.instantiate(this, fragmentName, args);
|
||||
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
|
||||
transaction.replace(R.id.main_content, f);
|
||||
if (withTransition) {
|
||||
TransitionManager.beginDelayedTransition(mContent);
|
||||
}
|
||||
if (addToBackStack) {
|
||||
transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
|
||||
}
|
||||
if (titleResId > 0) {
|
||||
transaction.setBreadCrumbTitle(titleResId);
|
||||
} else if (title != null) {
|
||||
|
||||
@@ -314,6 +314,9 @@ public class RecentAppsPreferenceController extends AbstractPreferenceController
|
||||
Log.d(TAG, "System package, skipping " + pkgName);
|
||||
return false;
|
||||
}
|
||||
if (AppUtils.isHiddenSystemModule(mContext, pkgName)) {
|
||||
return false;
|
||||
}
|
||||
final Intent launchIntent = new Intent().addCategory(Intent.CATEGORY_LAUNCHER)
|
||||
.setPackage(pkgName);
|
||||
|
||||
|
||||
@@ -20,9 +20,11 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserManager;
|
||||
import android.permission.PermissionControllerManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.internal.util.CollectionUtils;
|
||||
import com.android.settings.R;
|
||||
@@ -43,6 +45,10 @@ public abstract class DefaultAppShortcutPreferenceControllerBase extends BasePre
|
||||
|
||||
private final RoleManager mRoleManager;
|
||||
|
||||
private boolean mAppQualified;
|
||||
|
||||
private PreferenceScreen mPreferenceScreen;
|
||||
|
||||
public DefaultAppShortcutPreferenceControllerBase(Context context, String preferenceKey,
|
||||
String roleName, String packageName) {
|
||||
super(context, preferenceKey);
|
||||
@@ -51,6 +57,17 @@ public abstract class DefaultAppShortcutPreferenceControllerBase extends BasePre
|
||||
mPackageName = packageName;
|
||||
|
||||
mRoleManager = context.getSystemService(RoleManager.class);
|
||||
|
||||
// TODO: STOPSHIP(b/110557011): Remove this check once we have all default apps migrated.
|
||||
if (mRoleName != null) {
|
||||
final PermissionControllerManager permissionControllerManager =
|
||||
mContext.getSystemService(PermissionControllerManager.class);
|
||||
permissionControllerManager.isApplicationQualifiedForRole(mRoleName, mPackageName,
|
||||
mContext.getMainExecutor(), qualified -> {
|
||||
mAppQualified = qualified;
|
||||
refreshAvailability();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: STOPSHIP(b/110557011): Remove this once we have all default apps migrated.
|
||||
@@ -59,6 +76,23 @@ public abstract class DefaultAppShortcutPreferenceControllerBase extends BasePre
|
||||
this(context, preferenceKey, null /* roleName */, packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
|
||||
mPreferenceScreen = screen;
|
||||
}
|
||||
|
||||
private void refreshAvailability() {
|
||||
if (mPreferenceScreen != null) {
|
||||
final Preference preference = mPreferenceScreen.findPreference(getPreferenceKey());
|
||||
if (preference != null) {
|
||||
preference.setVisible(isAvailable());
|
||||
updateState(preference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
if (mContext.getSystemService(UserManager.class).isManagedProfile()) {
|
||||
@@ -104,7 +138,7 @@ public abstract class DefaultAppShortcutPreferenceControllerBase extends BasePre
|
||||
protected boolean hasAppCapability() {
|
||||
// TODO: STOPSHIP(b/110557011): Remove this check once we have all default apps migrated.
|
||||
if (mRoleName != null) {
|
||||
return mRoleManager.isRoleAvailable(mRoleName);
|
||||
return mAppQualified;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,246 +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.applications.assist;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.provider.Settings;
|
||||
import android.service.voice.VoiceInteractionService;
|
||||
import android.service.voice.VoiceInteractionServiceInfo;
|
||||
import android.speech.RecognitionService;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.internal.app.AssistUtils;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
|
||||
import com.android.settingslib.applications.DefaultAppInfo;
|
||||
import com.android.settingslib.widget.CandidateInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DefaultAssistPicker extends DefaultAppPickerFragment {
|
||||
|
||||
private static final String TAG = "DefaultAssistPicker";
|
||||
private static final Intent ASSIST_SERVICE_PROBE =
|
||||
new Intent(VoiceInteractionService.SERVICE_INTERFACE);
|
||||
private static final Intent ASSIST_ACTIVITY_PROBE =
|
||||
new Intent(Intent.ACTION_ASSIST);
|
||||
|
||||
@VisibleForTesting
|
||||
final List<Info> mAvailableAssistants = new ArrayList<>();
|
||||
|
||||
private AssistUtils mAssistUtils;
|
||||
private ActivityManager mActivityManager;
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.DEFAULT_ASSIST_PICKER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldShowItemNone() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
mAssistUtils = new AssistUtils(context);
|
||||
mActivityManager = context.getSystemService(ActivityManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.default_assist_settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<DefaultAppInfo> getCandidates() {
|
||||
mAvailableAssistants.clear();
|
||||
addAssistServices();
|
||||
addAssistActivities();
|
||||
|
||||
final List<String> packages = new ArrayList<>();
|
||||
final List<DefaultAppInfo> candidates = new ArrayList<>();
|
||||
for (Info info : mAvailableAssistants) {
|
||||
final String packageName = info.component.getPackageName();
|
||||
if (packages.contains(packageName)) {
|
||||
// A service appears before an activity thus overrides it if from the same package.
|
||||
continue;
|
||||
}
|
||||
packages.add(packageName);
|
||||
candidates.add(new DefaultAppInfo(getContext(), mPm, mUserId, info.component));
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDefaultKey() {
|
||||
final ComponentName cn = getCurrentAssist();
|
||||
if (cn != null) {
|
||||
return new DefaultAppInfo(getContext(), mPm, mUserId, cn).getKey();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getConfirmationMessage(CandidateInfo appInfo) {
|
||||
if (appInfo == null) {
|
||||
return null;
|
||||
}
|
||||
return getContext().getString(R.string.assistant_security_warning, appInfo.loadLabel());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean setDefaultKey(String key) {
|
||||
if (TextUtils.isEmpty(key)) {
|
||||
setAssistNone();
|
||||
return true;
|
||||
}
|
||||
ComponentName cn = ComponentName.unflattenFromString(key);
|
||||
final Info info = findAssistantByPackageName(cn.getPackageName());
|
||||
if (info == null) {
|
||||
setAssistNone();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (info.isVoiceInteractionService()) {
|
||||
setAssistService(info);
|
||||
} else {
|
||||
setAssistActivity(info);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public ComponentName getCurrentAssist() {
|
||||
return mAssistUtils.getAssistComponentForUser(mUserId);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void addAssistServices() {
|
||||
if (mActivityManager.isLowRamDevice()) {
|
||||
return;
|
||||
}
|
||||
final List<ResolveInfo> services = mPm.queryIntentServices(
|
||||
ASSIST_SERVICE_PROBE, PackageManager.GET_META_DATA);
|
||||
for (ResolveInfo resolveInfo : services) {
|
||||
VoiceInteractionServiceInfo voiceInteractionServiceInfo =
|
||||
new VoiceInteractionServiceInfo(mPm, resolveInfo.serviceInfo);
|
||||
if (!voiceInteractionServiceInfo.getSupportsAssist()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mAvailableAssistants.add(new Info(
|
||||
new ComponentName(resolveInfo.serviceInfo.packageName,
|
||||
resolveInfo.serviceInfo.name),
|
||||
voiceInteractionServiceInfo));
|
||||
}
|
||||
}
|
||||
|
||||
private void addAssistActivities() {
|
||||
final List<ResolveInfo> activities = mPm.queryIntentActivities(
|
||||
ASSIST_ACTIVITY_PROBE, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
for (ResolveInfo resolveInfo : activities) {
|
||||
mAvailableAssistants.add(new Info(
|
||||
new ComponentName(resolveInfo.activityInfo.packageName,
|
||||
resolveInfo.activityInfo.name)));
|
||||
}
|
||||
}
|
||||
|
||||
private Info findAssistantByPackageName(String packageName) {
|
||||
for (Info info : mAvailableAssistants) {
|
||||
if (TextUtils.equals(info.component.getPackageName(), packageName)) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void setAssistNone() {
|
||||
Settings.Secure.putString(getContext().getContentResolver(),
|
||||
Settings.Secure.ASSISTANT, "");
|
||||
Settings.Secure.putString(getContext().getContentResolver(),
|
||||
Settings.Secure.VOICE_INTERACTION_SERVICE, "");
|
||||
Settings.Secure.putString(getContext().getContentResolver(),
|
||||
Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer());
|
||||
}
|
||||
|
||||
private void setAssistService(Info serviceInfo) {
|
||||
final String serviceComponentName = serviceInfo.component.
|
||||
flattenToShortString();
|
||||
final String serviceRecognizerName = new ComponentName(
|
||||
serviceInfo.component.getPackageName(),
|
||||
serviceInfo.voiceInteractionServiceInfo.getRecognitionService())
|
||||
.flattenToShortString();
|
||||
|
||||
Settings.Secure.putString(getContext().getContentResolver(),
|
||||
Settings.Secure.ASSISTANT, serviceComponentName);
|
||||
Settings.Secure.putString(getContext().getContentResolver(),
|
||||
Settings.Secure.VOICE_INTERACTION_SERVICE, serviceComponentName);
|
||||
Settings.Secure.putString(getContext().getContentResolver(),
|
||||
Settings.Secure.VOICE_RECOGNITION_SERVICE, serviceRecognizerName);
|
||||
}
|
||||
|
||||
private void setAssistActivity(Info activityInfo) {
|
||||
Settings.Secure.putString(getContext().getContentResolver(),
|
||||
Settings.Secure.ASSISTANT, activityInfo.component.flattenToShortString());
|
||||
Settings.Secure.putString(getContext().getContentResolver(),
|
||||
Settings.Secure.VOICE_INTERACTION_SERVICE, "");
|
||||
Settings.Secure.putString(getContext().getContentResolver(),
|
||||
Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer());
|
||||
}
|
||||
|
||||
private String getDefaultRecognizer() {
|
||||
final ResolveInfo resolveInfo = mPm.resolveService(
|
||||
new Intent(RecognitionService.SERVICE_INTERFACE), PackageManager.GET_META_DATA);
|
||||
if (resolveInfo == null || resolveInfo.serviceInfo == null) {
|
||||
Log.w(TAG, "Unable to resolve default voice recognition service.");
|
||||
return "";
|
||||
}
|
||||
|
||||
return new ComponentName(resolveInfo.serviceInfo.packageName,
|
||||
resolveInfo.serviceInfo.name).flattenToShortString();
|
||||
}
|
||||
|
||||
static class Info {
|
||||
public final ComponentName component;
|
||||
public final VoiceInteractionServiceInfo voiceInteractionServiceInfo;
|
||||
|
||||
Info(ComponentName component) {
|
||||
this.component = component;
|
||||
this.voiceInteractionServiceInfo = null;
|
||||
}
|
||||
|
||||
Info(ComponentName component, VoiceInteractionServiceInfo voiceInteractionServiceInfo) {
|
||||
this.component = component;
|
||||
this.voiceInteractionServiceInfo = voiceInteractionServiceInfo;
|
||||
}
|
||||
|
||||
public boolean isVoiceInteractionService() {
|
||||
return voiceInteractionServiceInfo != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,15 +16,19 @@
|
||||
|
||||
package com.android.settings.applications.assist;
|
||||
|
||||
import android.app.role.RoleManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.provider.Settings;
|
||||
import android.service.voice.VoiceInteractionService;
|
||||
import android.service.voice.VoiceInteractionServiceInfo;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.internal.app.AssistUtils;
|
||||
import com.android.settings.R;
|
||||
@@ -38,6 +42,7 @@ public class DefaultAssistPreferenceController extends DefaultAppPreferenceContr
|
||||
private final AssistUtils mAssistUtils;
|
||||
private final boolean mShowSetting;
|
||||
private final String mPrefKey;
|
||||
private final Intent mIntent;
|
||||
|
||||
public DefaultAssistPreferenceController(Context context, String prefKey,
|
||||
boolean showSetting) {
|
||||
@@ -45,6 +50,15 @@ public class DefaultAssistPreferenceController extends DefaultAppPreferenceContr
|
||||
mPrefKey = prefKey;
|
||||
mShowSetting = showSetting;
|
||||
mAssistUtils = new AssistUtils(context);
|
||||
|
||||
final String packageName = mPackageManager.getPermissionControllerPackageName();
|
||||
if (packageName != null) {
|
||||
mIntent = new Intent(Intent.ACTION_MANAGE_DEFAULT_APP)
|
||||
.setPackage(packageName)
|
||||
.putExtra(Intent.EXTRA_ROLE_NAME, RoleManager.ROLE_ASSISTANT);
|
||||
} else {
|
||||
mIntent = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,6 +86,17 @@ public class DefaultAssistPreferenceController extends DefaultAppPreferenceContr
|
||||
.setComponent(new ComponentName(cn.getPackageName(), activity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handlePreferenceTreeClick(Preference preference) {
|
||||
if (TextUtils.equals(preference.getKey(), "default_assist")) {
|
||||
if (mIntent != null) {
|
||||
mContext.startActivity(mIntent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return mContext.getResources().getBoolean(R.bool.config_show_assist_and_voice_input);
|
||||
|
||||
@@ -32,8 +32,8 @@ import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
|
||||
import com.android.settings.core.InstrumentedActivity;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.ButtonFooterMixin;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
/**
|
||||
@@ -78,7 +78,7 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
||||
protected boolean mLaunchedConfirmLock;
|
||||
protected byte[] mToken;
|
||||
protected int mUserId;
|
||||
protected ButtonFooterMixin mButtonFooterMixin;
|
||||
protected FooterBarMixin mFooterBarMixin;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -137,8 +137,8 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
|
||||
}
|
||||
|
||||
protected FooterButton getNextButton() {
|
||||
if (mButtonFooterMixin != null) {
|
||||
return mButtonFooterMixin.getPrimaryButton();
|
||||
if (mFooterBarMixin != null) {
|
||||
return mFooterBarMixin.getPrimaryButton();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import com.android.settings.R;
|
||||
import com.android.settings.password.ChooseLockGeneric;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.span.LinkSpan;
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,8 +33,8 @@ import com.android.settings.biometrics.BiometricEnrollSidecar;
|
||||
import com.android.settings.biometrics.BiometricErrorDialog;
|
||||
import com.android.settings.biometrics.BiometricsEnrollEnrolling;
|
||||
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.ButtonFooterMixin;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -91,8 +91,8 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling {
|
||||
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
|
||||
this, android.R.interpolator.linear_out_slow_in);
|
||||
|
||||
mButtonFooterMixin = getLayout().getMixin(ButtonFooterMixin.class);
|
||||
mButtonFooterMixin.setSecondaryButton(
|
||||
mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
|
||||
mFooterBarMixin.setSecondaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.security_settings_face_enroll_enrolling_skip)
|
||||
.setListener(this::onSkipButtonClick)
|
||||
|
||||
@@ -23,8 +23,8 @@ import android.view.View;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.biometrics.BiometricEnrollBase;
|
||||
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.ButtonFooterMixin;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
|
||||
/**
|
||||
* Activity which concludes face enrollment.
|
||||
@@ -37,8 +37,8 @@ public class FaceEnrollFinish extends BiometricEnrollBase {
|
||||
setContentView(R.layout.face_enroll_finish);
|
||||
setHeaderText(R.string.security_settings_face_enroll_finish_title);
|
||||
|
||||
mButtonFooterMixin = getLayout().getMixin(ButtonFooterMixin.class);
|
||||
mButtonFooterMixin.setPrimaryButton(
|
||||
mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
|
||||
mFooterBarMixin.setPrimaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.security_settings_face_enroll_done)
|
||||
.setListener(this::onNextButtonClick)
|
||||
|
||||
@@ -34,8 +34,8 @@ import com.android.settings.biometrics.BiometricEnrollIntroduction;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.ButtonFooterMixin;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.span.LinkSpan;
|
||||
|
||||
public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
@@ -43,7 +43,6 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
private static final String TAG = "FaceIntro";
|
||||
|
||||
private FaceManager mFaceManager;
|
||||
private FaceEnrollAccessibilityToggle mSwitchVision;
|
||||
private FaceEnrollAccessibilityToggle mSwitchDiversity;
|
||||
|
||||
@Override
|
||||
@@ -57,11 +56,10 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
accessibilityLayout.setVisibility(View.VISIBLE);
|
||||
});
|
||||
|
||||
mSwitchVision = findViewById(R.id.toggle_vision);
|
||||
mSwitchDiversity = findViewById(R.id.toggle_diversity);
|
||||
|
||||
mButtonFooterMixin = getLayout().getMixin(ButtonFooterMixin.class);
|
||||
mButtonFooterMixin.setSecondaryButton(
|
||||
mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
|
||||
mFooterBarMixin.setSecondaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.security_settings_face_enroll_introduction_cancel)
|
||||
.setListener(this::onCancelButtonClick)
|
||||
@@ -70,7 +68,7 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
.build()
|
||||
);
|
||||
|
||||
mButtonFooterMixin.setPrimaryButton(
|
||||
mFooterBarMixin.setPrimaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.wizard_next)
|
||||
.setListener(this::onNextButtonClick)
|
||||
@@ -108,16 +106,16 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
|
||||
@Override
|
||||
protected FooterButton getCancelButton() {
|
||||
if (mButtonFooterMixin != null) {
|
||||
return mButtonFooterMixin.getSecondaryButton();
|
||||
if (mFooterBarMixin != null) {
|
||||
return mFooterBarMixin.getSecondaryButton();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FooterButton getNextButton() {
|
||||
if (mButtonFooterMixin != null) {
|
||||
return mButtonFooterMixin.getPrimaryButton();
|
||||
if (mFooterBarMixin != null) {
|
||||
return mFooterBarMixin.getPrimaryButton();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -167,7 +165,6 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
} else {
|
||||
intent.setClass(this, FaceEnrollEnrolling.class);
|
||||
}
|
||||
intent.putExtra(EXTRA_KEY_REQUIRE_VISION, mSwitchVision.isChecked());
|
||||
intent.putExtra(EXTRA_KEY_REQUIRE_DIVERSITY, mSwitchDiversity.isChecked());
|
||||
return intent;
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ import com.android.settings.biometrics.BiometricErrorDialog;
|
||||
import com.android.settings.biometrics.BiometricsEnrollEnrolling;
|
||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.ButtonFooterMixin;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
|
||||
/**
|
||||
* Activity which handles the actual enrolling for fingerprint.
|
||||
@@ -137,8 +137,8 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
|
||||
mProgressBar = (ProgressBar) findViewById(R.id.fingerprint_progress_bar);
|
||||
mVibrator = getSystemService(Vibrator.class);
|
||||
|
||||
mButtonFooterMixin = getLayout().getMixin(ButtonFooterMixin.class);
|
||||
mButtonFooterMixin.setSecondaryButton(
|
||||
mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
|
||||
mFooterBarMixin.setSecondaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.security_settings_fingerprint_enroll_enrolling_skip)
|
||||
.setListener(this::onSkipButtonClick)
|
||||
|
||||
@@ -30,8 +30,8 @@ import com.android.settings.biometrics.BiometricEnrollBase;
|
||||
import com.android.settings.biometrics.BiometricEnrollSidecar.Listener;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.ButtonFooterMixin;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
|
||||
/**
|
||||
* Activity explaining the fingerprint sensor location for fingerprint enrollment.
|
||||
@@ -48,8 +48,8 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(getContentView());
|
||||
mButtonFooterMixin = getLayout().getMixin(ButtonFooterMixin.class);
|
||||
mButtonFooterMixin.setSecondaryButton(
|
||||
mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
|
||||
mFooterBarMixin.setSecondaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.skip_label)
|
||||
.setListener(this::onSkipButtonClick)
|
||||
|
||||
@@ -26,8 +26,8 @@ import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.biometrics.BiometricEnrollBase;
|
||||
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.ButtonFooterMixin;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
|
||||
/**
|
||||
* Activity which concludes fingerprint enrollment.
|
||||
@@ -42,8 +42,8 @@ public class FingerprintEnrollFinish extends BiometricEnrollBase {
|
||||
setContentView(R.layout.fingerprint_enroll_finish);
|
||||
setHeaderText(R.string.security_settings_fingerprint_enroll_finish_title);
|
||||
|
||||
mButtonFooterMixin = getLayout().getMixin(ButtonFooterMixin.class);
|
||||
mButtonFooterMixin.setSecondaryButton(
|
||||
mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
|
||||
mFooterBarMixin.setSecondaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.fingerprint_enroll_button_add)
|
||||
.setButtonType(FooterButton.ButtonType.SKIP)
|
||||
@@ -51,7 +51,7 @@ public class FingerprintEnrollFinish extends BiometricEnrollBase {
|
||||
.build()
|
||||
);
|
||||
|
||||
mButtonFooterMixin.setPrimaryButton(
|
||||
mFooterBarMixin.setPrimaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.security_settings_fingerprint_enroll_done)
|
||||
.setListener(this::onNextButtonClick)
|
||||
@@ -65,7 +65,7 @@ public class FingerprintEnrollFinish extends BiometricEnrollBase {
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
FooterButton addButton = mButtonFooterMixin.getSecondaryButton();
|
||||
FooterButton addButton = mFooterBarMixin.getSecondaryButton();
|
||||
|
||||
final FingerprintManager fpm = Utils.getFingerprintManagerOrNull(this);
|
||||
boolean hideAddAnother = false;
|
||||
|
||||
@@ -32,8 +32,8 @@ import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settingslib.HelpUtils;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.ButtonFooterMixin;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.span.LinkSpan;
|
||||
|
||||
public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
@@ -47,8 +47,8 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
super.onCreate(savedInstanceState);
|
||||
mFingerprintManager = Utils.getFingerprintManagerOrNull(this);
|
||||
|
||||
mButtonFooterMixin = getLayout().getMixin(ButtonFooterMixin.class);
|
||||
mButtonFooterMixin.setSecondaryButton(
|
||||
mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
|
||||
mFooterBarMixin.setSecondaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.security_settings_face_enroll_introduction_cancel)
|
||||
.setListener(this::onCancelButtonClick)
|
||||
@@ -57,7 +57,7 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
.build()
|
||||
);
|
||||
|
||||
mButtonFooterMixin.setPrimaryButton(
|
||||
mFooterBarMixin.setPrimaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.wizard_next)
|
||||
.setListener(this::onNextButtonClick)
|
||||
@@ -95,16 +95,16 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
|
||||
@Override
|
||||
protected FooterButton getCancelButton() {
|
||||
if (mButtonFooterMixin != null) {
|
||||
return mButtonFooterMixin.getSecondaryButton();
|
||||
if (mFooterBarMixin != null) {
|
||||
return mFooterBarMixin.getSecondaryButton();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FooterButton getNextButton() {
|
||||
if (mButtonFooterMixin != null) {
|
||||
return mButtonFooterMixin.getPrimaryButton();
|
||||
if (mFooterBarMixin != null) {
|
||||
return mFooterBarMixin.getPrimaryButton();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import android.hardware.fingerprint.FingerprintManager;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
|
||||
public class FingerprintSuggestionActivity extends SetupFingerprintEnrollIntroduction {
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import com.android.settings.R;
|
||||
import com.android.settings.SetupWizardUtils;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
|
||||
public class SetupFingerprintEnrollFinish extends FingerprintEnrollFinish {
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment
|
||||
import com.android.settings.password.SetupChooseLockGeneric;
|
||||
import com.android.settings.password.SetupSkipDialog;
|
||||
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
|
||||
public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntroduction {
|
||||
private static final String KEY_LOCK_SCREEN_PRESENT = "wasLockScreenPresent";
|
||||
|
||||
@@ -64,7 +64,6 @@ public class SettingsBaseActivity extends FragmentActivity {
|
||||
|
||||
final TypedArray theme = getTheme().obtainStyledAttributes(android.R.styleable.Theme);
|
||||
if (!theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) {
|
||||
getWindow().addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
}
|
||||
super.setContentView(R.layout.settings_base_layout);
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.core;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.Utils;
|
||||
|
||||
/**
|
||||
* Abstract class to provide additional logic to deal with optional {@link Preference} entries that
|
||||
* are used only when work profile is enabled.
|
||||
*
|
||||
* <p>TODO(b/123376083): Consider merging this into {@link BasePreferenceController}.</p>
|
||||
*/
|
||||
public abstract class WorkProfilePreferenceController extends BasePreferenceController {
|
||||
@Nullable
|
||||
private final UserHandle mWorkProfileUser;
|
||||
|
||||
/**
|
||||
* Constructor of {@link WorkProfilePreferenceController}. Called by
|
||||
* {@link BasePreferenceController#createInstance(Context, String)} through reflection.
|
||||
*
|
||||
* @param context {@link Context} to instantiate this controller.
|
||||
* @param preferenceKey Preference key to be associated with the {@link Preference}.
|
||||
*/
|
||||
public WorkProfilePreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mWorkProfileUser = Utils.getManagedProfile(UserManager.get(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Non-{@code null} {@link UserHandle} when a work profile is enabled.
|
||||
* Otherwise {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
protected UserHandle getWorkProfileUser() {
|
||||
return mWorkProfileUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called back from {@link #handlePreferenceTreeClick(Preference)} to associate source metrics
|
||||
* category.
|
||||
*
|
||||
* @return One of {@link android.app.settings.SettingsEnums}.
|
||||
*/
|
||||
protected abstract int getSourceMetricsCategory();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>When you override this method, do not forget to check {@link #getWorkProfileUser()} to
|
||||
* see if work profile user actually exists or not.</p>
|
||||
*/
|
||||
@AvailabilityStatus
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return mWorkProfileUser != null ? AVAILABLE : DISABLED_FOR_USER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches the specified fragment for the work profile user if the associated
|
||||
* {@link Preference} is clicked. Otherwise just forward it to the super class.
|
||||
*
|
||||
* @param preference the preference being clicked.
|
||||
* @return {@code true} if handled.
|
||||
*/
|
||||
public boolean handlePreferenceTreeClick(Preference preference) {
|
||||
if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
|
||||
return super.handlePreferenceTreeClick(preference);
|
||||
}
|
||||
new SubSettingLauncher(preference.getContext())
|
||||
.setDestination(preference.getFragment())
|
||||
.setSourceMetricsCategory(getSourceMetricsCategory())
|
||||
.setArguments(preference.getExtras())
|
||||
.setUserHandle(mWorkProfileUser)
|
||||
.launch();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -135,8 +135,8 @@ public class DataUsageList extends DataUsageBaseFragment {
|
||||
mUidDetailProvider = new UidDetailProvider(activity);
|
||||
mTelephonyManager = activity.getSystemService(TelephonyManager.class);
|
||||
mUsageAmount = findPreference(KEY_USAGE_AMOUNT);
|
||||
mChart = (ChartDataUsagePreference) findPreference(KEY_CHART_DATA);
|
||||
mApps = (PreferenceGroup) findPreference(KEY_APPS_GROUP);
|
||||
mChart = findPreference(KEY_CHART_DATA);
|
||||
mApps = findPreference(KEY_APPS_GROUP);
|
||||
processArgument();
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@ public class DataUsageList extends DataUsageBaseFragment {
|
||||
getLoaderManager().restartLoader(LOADER_SUMMARY, null /* args */,
|
||||
mNetworkStatsDetailCallbacks);
|
||||
|
||||
final long totalBytes = mCycleData != null
|
||||
final long totalBytes = mCycleData != null && !mCycleData.isEmpty()
|
||||
? mCycleData.get(mCycleSpinner.getSelectedItemPosition()).getTotalUsage() : 0;
|
||||
final CharSequence totalPhrase = DataUsageUtils.formatDataUsage(getActivity(), totalBytes);
|
||||
mUsageAmount.setTitle(getString(R.string.data_used_template, totalPhrase));
|
||||
|
||||
@@ -87,7 +87,7 @@ public class DataUsageSummary extends DataUsageBaseFragment implements DataUsage
|
||||
|
||||
boolean hasMobileData = DataUsageUtils.hasMobileData(context);
|
||||
|
||||
int defaultSubId = DataUsageUtils.getDefaultSubscriptionId(context);
|
||||
final int defaultSubId = SubscriptionManager.getDefaultDataSubscriptionId();
|
||||
if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
||||
hasMobileData = false;
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ import androidx.fragment.app.FragmentActivity;
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.Utils;
|
||||
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.ButtonFooterMixin;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
@@ -63,7 +63,7 @@ public abstract class StorageWizardBase extends FragmentActivity {
|
||||
protected VolumeInfo mVolume;
|
||||
protected DiskInfo mDisk;
|
||||
|
||||
private ButtonFooterMixin mButtonFooterMixin;
|
||||
private FooterBarMixin mFooterBarMixin;
|
||||
private FooterButton mBack;
|
||||
private FooterButton mNext;
|
||||
|
||||
@@ -94,8 +94,8 @@ public abstract class StorageWizardBase extends FragmentActivity {
|
||||
public void setContentView(@LayoutRes int layoutResID) {
|
||||
super.setContentView(layoutResID);
|
||||
|
||||
mButtonFooterMixin = getGlifLayout().getMixin(ButtonFooterMixin.class);
|
||||
mButtonFooterMixin.setSecondaryButton(
|
||||
mFooterBarMixin = getGlifLayout().getMixin(FooterBarMixin.class);
|
||||
mFooterBarMixin.setSecondaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.wizard_back)
|
||||
.setListener(this::onNavigateBack)
|
||||
@@ -103,7 +103,7 @@ public abstract class StorageWizardBase extends FragmentActivity {
|
||||
.setTheme(R.style.SudGlifButton_Secondary)
|
||||
.build()
|
||||
);
|
||||
mButtonFooterMixin.setPrimaryButton(
|
||||
mFooterBarMixin.setPrimaryButton(
|
||||
new FooterButton.Builder(this)
|
||||
.setText(R.string.wizard_next)
|
||||
.setListener(this::onNavigateNext)
|
||||
@@ -111,8 +111,8 @@ public abstract class StorageWizardBase extends FragmentActivity {
|
||||
.setTheme(R.style.SudGlifButton_Primary)
|
||||
.build()
|
||||
);
|
||||
mBack = mButtonFooterMixin.getSecondaryButton();
|
||||
mNext = mButtonFooterMixin.getPrimaryButton();
|
||||
mBack = mFooterBarMixin.getSecondaryButton();
|
||||
mNext = mFooterBarMixin.getPrimaryButton();
|
||||
|
||||
setIcon(com.android.internal.R.drawable.ic_sd_card_48dp);
|
||||
}
|
||||
|
||||
@@ -90,5 +90,6 @@ public class FirmwareVersionDialogFragment extends InstrumentedDialogFragment {
|
||||
new BasebandVersionDialogController(this).initialize();
|
||||
new KernelVersionDialogController(this).initialize();
|
||||
new BuildNumberDialogController(this).initialize();
|
||||
new ModuleVersionDialogController(this).initialize();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.deviceinfo.firmwareversion;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
public class ModuleVersionDialogController {
|
||||
|
||||
private static final String TAG = "MainlineModuleControl";
|
||||
|
||||
@VisibleForTesting
|
||||
static final int MODULE_VERSION_LABEL_ID = R.id.module_version_label;
|
||||
@VisibleForTesting
|
||||
static final int MODULE_VERSION_VALUE_ID = R.id.module_version_value;
|
||||
|
||||
private final FirmwareVersionDialogFragment mDialog;
|
||||
private final Context mContext;
|
||||
private final PackageManager mPackageManager;
|
||||
|
||||
public ModuleVersionDialogController(FirmwareVersionDialogFragment dialog) {
|
||||
mDialog = dialog;
|
||||
mContext = mDialog.getContext();
|
||||
mPackageManager = mContext.getPackageManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the mainline module version field of the dialog.
|
||||
*/
|
||||
public void initialize() {
|
||||
final String moduleProvider = mContext.getString(
|
||||
com.android.internal.R.string.config_defaultModuleMetadataProvider);
|
||||
if (!TextUtils.isEmpty(moduleProvider)) {
|
||||
try {
|
||||
mDialog.setText(MODULE_VERSION_VALUE_ID,
|
||||
mPackageManager.getPackageInfo(moduleProvider, 0 /* flags */).versionName);
|
||||
return;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(TAG, "Failed to get mainline version.", e);
|
||||
}
|
||||
}
|
||||
mDialog.removeSettingFromScreen(MODULE_VERSION_LABEL_ID);
|
||||
mDialog.removeSettingFromScreen(MODULE_VERSION_VALUE_ID);
|
||||
}
|
||||
}
|
||||
@@ -18,14 +18,12 @@ import android.hardware.display.ColorDisplayManager;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.internal.app.ColorDisplayController;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
public class ColorModePreferenceController extends BasePreferenceController {
|
||||
private static final String TAG = "ColorModePreference";
|
||||
|
||||
private ColorDisplayController mColorDisplayController;
|
||||
private ColorDisplayManager mColorDisplayManager;
|
||||
|
||||
public ColorModePreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
@@ -35,30 +33,30 @@ public class ColorModePreferenceController extends BasePreferenceController {
|
||||
public int getAvailabilityStatus() {
|
||||
return mContext.getSystemService(ColorDisplayManager.class)
|
||||
.isDeviceColorManaged()
|
||||
&& !getColorDisplayController().getAccessibilityTransformActivated() ?
|
||||
&& !ColorDisplayManager.areAccessibilityTransformsEnabled(mContext) ?
|
||||
AVAILABLE_UNSEARCHABLE : DISABLED_FOR_USER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
final int colorMode = getColorDisplayController().getColorMode();
|
||||
if (colorMode == ColorDisplayController.COLOR_MODE_AUTOMATIC) {
|
||||
final int colorMode = getColorDisplayManager().getColorMode();
|
||||
if (colorMode == ColorDisplayManager.COLOR_MODE_AUTOMATIC) {
|
||||
return mContext.getText(R.string.color_mode_option_automatic);
|
||||
}
|
||||
if (colorMode == ColorDisplayController.COLOR_MODE_SATURATED) {
|
||||
if (colorMode == ColorDisplayManager.COLOR_MODE_SATURATED) {
|
||||
return mContext.getText(R.string.color_mode_option_saturated);
|
||||
}
|
||||
if (colorMode == ColorDisplayController.COLOR_MODE_BOOSTED) {
|
||||
if (colorMode == ColorDisplayManager.COLOR_MODE_BOOSTED) {
|
||||
return mContext.getText(R.string.color_mode_option_boosted);
|
||||
}
|
||||
return mContext.getText(R.string.color_mode_option_natural);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ColorDisplayController getColorDisplayController() {
|
||||
if (mColorDisplayController == null) {
|
||||
mColorDisplayController = new ColorDisplayController(mContext);
|
||||
ColorDisplayManager getColorDisplayManager() {
|
||||
if (mColorDisplayManager == null) {
|
||||
mColorDisplayManager = mContext.getSystemService(ColorDisplayManager.class);
|
||||
}
|
||||
return mColorDisplayController;
|
||||
return mColorDisplayManager;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,20 @@
|
||||
package com.android.settings.display;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.display.ColorDisplayManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.SearchIndexableResource;
|
||||
|
||||
import android.provider.Settings.Secure;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.internal.app.ColorDisplayController;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.search.Indexable;
|
||||
@@ -36,8 +42,7 @@ import java.util.List;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@SearchIndexable
|
||||
public class ColorModePreferenceFragment extends RadioButtonPickerFragment
|
||||
implements ColorDisplayController.Callback {
|
||||
public class ColorModePreferenceFragment extends RadioButtonPickerFragment {
|
||||
|
||||
@VisibleForTesting
|
||||
static final String KEY_COLOR_MODE_NATURAL = "color_mode_natural";
|
||||
@@ -48,21 +53,41 @@ public class ColorModePreferenceFragment extends RadioButtonPickerFragment
|
||||
@VisibleForTesting
|
||||
static final String KEY_COLOR_MODE_AUTOMATIC = "color_mode_automatic";
|
||||
|
||||
private ColorDisplayController mController;
|
||||
private ContentObserver mContentObserver;
|
||||
private ColorDisplayManager mColorDisplayManager;
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
mController = new ColorDisplayController(context);
|
||||
mController.setListener(this);
|
||||
|
||||
mColorDisplayManager = context.getSystemService(ColorDisplayManager.class);
|
||||
|
||||
final ContentResolver cr = context.getContentResolver();
|
||||
mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
super.onChange(selfChange, uri);
|
||||
if (ColorDisplayManager.areAccessibilityTransformsEnabled(getContext())) {
|
||||
// Color modes are not configurable when Accessibility transforms are enabled.
|
||||
// Close this fragment in that case.
|
||||
getActivity().finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
cr.registerContentObserver(
|
||||
Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
|
||||
false /* notifyForDescendants */, mContentObserver, mUserId);
|
||||
cr.registerContentObserver(
|
||||
Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
|
||||
false /* notifyForDescendants */, mContentObserver, mUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
if (mController != null) {
|
||||
mController.setListener(null);
|
||||
mController = null;
|
||||
if (mContentObserver != null) {
|
||||
getContext().getContentResolver().unregisterContentObserver(mContentObserver);
|
||||
mContentObserver = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,22 +115,22 @@ public class ColorModePreferenceFragment extends RadioButtonPickerFragment
|
||||
final int[] availableColorModes = c.getResources().getIntArray(
|
||||
com.android.internal.R.array.config_availableColorModes);
|
||||
|
||||
List<ColorModeCandidateInfo> candidates = new ArrayList<ColorModeCandidateInfo>();
|
||||
List<ColorModeCandidateInfo> candidates = new ArrayList<>();
|
||||
if (availableColorModes != null) {
|
||||
for (int colorMode : availableColorModes) {
|
||||
if (colorMode == ColorDisplayController.COLOR_MODE_NATURAL) {
|
||||
if (colorMode == ColorDisplayManager.COLOR_MODE_NATURAL) {
|
||||
candidates.add(new ColorModeCandidateInfo(
|
||||
c.getText(R.string.color_mode_option_natural),
|
||||
KEY_COLOR_MODE_NATURAL, true /* enabled */));
|
||||
} else if (colorMode == ColorDisplayController.COLOR_MODE_BOOSTED) {
|
||||
} else if (colorMode == ColorDisplayManager.COLOR_MODE_BOOSTED) {
|
||||
candidates.add(new ColorModeCandidateInfo(
|
||||
c.getText(R.string.color_mode_option_boosted),
|
||||
KEY_COLOR_MODE_BOOSTED, true /* enabled */));
|
||||
} else if (colorMode == ColorDisplayController.COLOR_MODE_SATURATED) {
|
||||
} else if (colorMode == ColorDisplayManager.COLOR_MODE_SATURATED) {
|
||||
candidates.add(new ColorModeCandidateInfo(
|
||||
c.getText(R.string.color_mode_option_saturated),
|
||||
KEY_COLOR_MODE_SATURATED, true /* enabled */));
|
||||
} else if (colorMode == ColorDisplayController.COLOR_MODE_AUTOMATIC) {
|
||||
} else if (colorMode == ColorDisplayManager.COLOR_MODE_AUTOMATIC) {
|
||||
candidates.add(new ColorModeCandidateInfo(
|
||||
c.getText(R.string.color_mode_option_automatic),
|
||||
KEY_COLOR_MODE_AUTOMATIC, true /* enabled */));
|
||||
@@ -117,12 +142,12 @@ public class ColorModePreferenceFragment extends RadioButtonPickerFragment
|
||||
|
||||
@Override
|
||||
protected String getDefaultKey() {
|
||||
final int colorMode = mController.getColorMode();
|
||||
if (colorMode == ColorDisplayController.COLOR_MODE_AUTOMATIC) {
|
||||
final int colorMode = mColorDisplayManager.getColorMode();
|
||||
if (colorMode == ColorDisplayManager.COLOR_MODE_AUTOMATIC) {
|
||||
return KEY_COLOR_MODE_AUTOMATIC;
|
||||
} else if (colorMode == ColorDisplayController.COLOR_MODE_SATURATED) {
|
||||
} else if (colorMode == ColorDisplayManager.COLOR_MODE_SATURATED) {
|
||||
return KEY_COLOR_MODE_SATURATED;
|
||||
} else if (colorMode == ColorDisplayController.COLOR_MODE_BOOSTED) {
|
||||
} else if (colorMode == ColorDisplayManager.COLOR_MODE_BOOSTED) {
|
||||
return KEY_COLOR_MODE_BOOSTED;
|
||||
}
|
||||
return KEY_COLOR_MODE_NATURAL;
|
||||
@@ -132,16 +157,16 @@ public class ColorModePreferenceFragment extends RadioButtonPickerFragment
|
||||
protected boolean setDefaultKey(String key) {
|
||||
switch (key) {
|
||||
case KEY_COLOR_MODE_NATURAL:
|
||||
mController.setColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
|
||||
mColorDisplayManager.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
|
||||
break;
|
||||
case KEY_COLOR_MODE_BOOSTED:
|
||||
mController.setColorMode(ColorDisplayController.COLOR_MODE_BOOSTED);
|
||||
mColorDisplayManager.setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED);
|
||||
break;
|
||||
case KEY_COLOR_MODE_SATURATED:
|
||||
mController.setColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
|
||||
mColorDisplayManager.setColorMode(ColorDisplayManager.COLOR_MODE_SATURATED);
|
||||
break;
|
||||
case KEY_COLOR_MODE_AUTOMATIC:
|
||||
mController.setColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC);
|
||||
mColorDisplayManager.setColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
@@ -179,15 +204,6 @@ public class ColorModePreferenceFragment extends RadioButtonPickerFragment
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccessibilityTransformChanged(boolean state) {
|
||||
// Color modes are no not configurable when Accessibility transforms are enabled. Close
|
||||
// this fragment in that case.
|
||||
if (state) {
|
||||
getActivity().onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider() {
|
||||
@Override
|
||||
|
||||
@@ -17,12 +17,13 @@ import android.content.Context;
|
||||
import android.hardware.display.ColorDisplayManager;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings.Secure;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.internal.app.ColorDisplayController;
|
||||
import com.android.settings.core.TogglePreferenceController;
|
||||
|
||||
public class DisplayWhiteBalancePreferenceController extends TogglePreferenceController {
|
||||
private ColorDisplayController mColorDisplayController;
|
||||
|
||||
private ColorDisplayManager mColorDisplayManager;
|
||||
|
||||
public DisplayWhiteBalancePreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
@@ -33,8 +34,8 @@ public class DisplayWhiteBalancePreferenceController extends TogglePreferenceCon
|
||||
// Display white balance is only valid in linear light space. COLOR_MODE_SATURATED implies
|
||||
// unmanaged color mode, and hence unknown color processing conditions.
|
||||
return ColorDisplayManager.isDisplayWhiteBalanceAvailable(mContext) &&
|
||||
getColorDisplayController().getColorMode() !=
|
||||
ColorDisplayController.COLOR_MODE_SATURATED ?
|
||||
getColorDisplayManager().getColorMode() !=
|
||||
ColorDisplayManager.COLOR_MODE_SATURATED ?
|
||||
AVAILABLE : DISABLED_FOR_USER;
|
||||
}
|
||||
|
||||
@@ -51,10 +52,11 @@ public class DisplayWhiteBalancePreferenceController extends TogglePreferenceCon
|
||||
return true;
|
||||
}
|
||||
|
||||
ColorDisplayController getColorDisplayController() {
|
||||
if (mColorDisplayController == null) {
|
||||
mColorDisplayController = new ColorDisplayController(mContext);
|
||||
@VisibleForTesting
|
||||
ColorDisplayManager getColorDisplayManager() {
|
||||
if (mColorDisplayManager == null) {
|
||||
mColorDisplayManager = mContext.getSystemService(ColorDisplayManager.class);
|
||||
}
|
||||
return mColorDisplayController;
|
||||
return mColorDisplayManager;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,8 +67,7 @@ public class NightDisplayActivationPreferenceController extends TogglePreference
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
|
||||
final LayoutPreference preference = (LayoutPreference) screen.findPreference(
|
||||
getPreferenceKey());
|
||||
final LayoutPreference preference = screen.findPreference(getPreferenceKey());
|
||||
mTurnOnButton = preference.findViewById(R.id.night_display_turn_on_button);
|
||||
mTurnOnButton.setOnClickListener(mListener);
|
||||
mTurnOffButton = preference.findViewById(R.id.night_display_turn_off_button);
|
||||
@@ -106,14 +105,14 @@ public class NightDisplayActivationPreferenceController extends TogglePreference
|
||||
final int autoMode = mController.getAutoMode();
|
||||
|
||||
String buttonText;
|
||||
if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
|
||||
if (autoMode == ColorDisplayManager.AUTO_MODE_CUSTOM_TIME) {
|
||||
buttonText = mContext.getString(isActivated
|
||||
? R.string.night_display_activation_off_custom
|
||||
: R.string.night_display_activation_on_custom,
|
||||
mTimeFormatter.getFormattedTimeString(isActivated
|
||||
? mController.getCustomStartTime()
|
||||
: mController.getCustomEndTime()));
|
||||
} else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
|
||||
} else if (autoMode == ColorDisplayManager.AUTO_MODE_TWILIGHT) {
|
||||
buttonText = mContext.getString(isActivated
|
||||
? R.string.night_display_activation_off_twilight
|
||||
: R.string.night_display_activation_on_twilight);
|
||||
|
||||
@@ -23,7 +23,6 @@ import androidx.preference.DropDownPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.internal.app.ColorDisplayController;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
@@ -31,11 +30,11 @@ public class NightDisplayAutoModePreferenceController extends BasePreferenceCont
|
||||
implements Preference.OnPreferenceChangeListener {
|
||||
|
||||
private DropDownPreference mPreference;
|
||||
private ColorDisplayController mController;
|
||||
private ColorDisplayManager mManager;
|
||||
|
||||
public NightDisplayAutoModePreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
mController = new ColorDisplayController(context);
|
||||
mManager = context.getSystemService(ColorDisplayManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -48,7 +47,7 @@ public class NightDisplayAutoModePreferenceController extends BasePreferenceCont
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
|
||||
mPreference = (DropDownPreference) screen.findPreference(getPreferenceKey());
|
||||
mPreference = screen.findPreference(getPreferenceKey());
|
||||
|
||||
mPreference.setEntries(new CharSequence[]{
|
||||
mContext.getString(R.string.night_display_auto_mode_never),
|
||||
@@ -56,19 +55,19 @@ public class NightDisplayAutoModePreferenceController extends BasePreferenceCont
|
||||
mContext.getString(R.string.night_display_auto_mode_twilight)
|
||||
});
|
||||
mPreference.setEntryValues(new CharSequence[]{
|
||||
String.valueOf(ColorDisplayController.AUTO_MODE_DISABLED),
|
||||
String.valueOf(ColorDisplayController.AUTO_MODE_CUSTOM),
|
||||
String.valueOf(ColorDisplayController.AUTO_MODE_TWILIGHT)
|
||||
String.valueOf(ColorDisplayManager.AUTO_MODE_DISABLED),
|
||||
String.valueOf(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME),
|
||||
String.valueOf(ColorDisplayManager.AUTO_MODE_TWILIGHT)
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void updateState(Preference preference) {
|
||||
mPreference.setValue(String.valueOf(mController.getAutoMode()));
|
||||
mPreference.setValue(String.valueOf(mManager.getNightDisplayAutoMode()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
return mController.setAutoMode(Integer.parseInt((String) newValue));
|
||||
return mManager.setNightDisplayAutoMode(Integer.parseInt((String) newValue));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,7 @@ package com.android.settings.display;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.display.ColorDisplayManager;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.internal.app.ColorDisplayController;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
@@ -44,7 +42,8 @@ public class NightDisplayCustomEndTimePreferenceController extends BasePreferenc
|
||||
|
||||
@Override
|
||||
public final void updateState(Preference preference) {
|
||||
preference.setVisible(mController.getAutoMode() == ColorDisplayController.AUTO_MODE_CUSTOM);
|
||||
preference
|
||||
.setVisible(mController.getAutoMode() == ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
|
||||
preference.setSummary(mTimeFormatter.getFormattedTimeString(
|
||||
mController.getCustomEndTime()));
|
||||
}
|
||||
|
||||
@@ -18,9 +18,7 @@ package com.android.settings.display;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.display.ColorDisplayManager;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.internal.app.ColorDisplayController;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
@@ -44,7 +42,8 @@ public class NightDisplayCustomStartTimePreferenceController extends BasePrefere
|
||||
|
||||
@Override
|
||||
public final void updateState(Preference preference) {
|
||||
preference.setVisible(mController.getAutoMode() == ColorDisplayController.AUTO_MODE_CUSTOM);
|
||||
preference
|
||||
.setVisible(mController.getAutoMode() == ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
|
||||
preference.setSummary(mTimeFormatter.getFormattedTimeString(
|
||||
mController.getCustomStartTime()));
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.text.TextUtils;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.internal.app.ColorDisplayController;
|
||||
import com.android.settings.core.SliderPreferenceController;
|
||||
@@ -54,8 +55,7 @@ public class NightDisplayIntensityPreferenceController extends SliderPreferenceC
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
final SeekBarPreference preference = (SeekBarPreference) screen.findPreference(
|
||||
getPreferenceKey());
|
||||
final SeekBarPreference preference = screen.findPreference(getPreferenceKey());
|
||||
preference.setContinuousUpdates(true);
|
||||
preference.setMax(getMaxSteps());
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public class NightDisplayPreferenceController extends AbstractPreferenceControll
|
||||
return true;
|
||||
}
|
||||
final ColorDisplayController controller = new ColorDisplayController(context);
|
||||
return controller.getAutoMode() != ColorDisplayController.AUTO_MODE_DISABLED;
|
||||
return controller.getAutoMode() != ColorDisplayManager.AUTO_MODE_DISABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.settings.display;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import android.hardware.display.ColorDisplayManager;
|
||||
import com.android.internal.app.ColorDisplayController;
|
||||
import com.android.settings.R;
|
||||
|
||||
@@ -54,7 +55,7 @@ public class NightDisplayTimeFormatter {
|
||||
private String getAutoModeSummary(Context context, ColorDisplayController controller) {
|
||||
final boolean isActivated = controller.isActivated();
|
||||
final int autoMode = controller.getAutoMode();
|
||||
if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
|
||||
if (autoMode == ColorDisplayManager.AUTO_MODE_CUSTOM_TIME) {
|
||||
if (isActivated) {
|
||||
return context.getString(R.string.night_display_summary_on_auto_mode_custom,
|
||||
getFormattedTimeString(controller.getCustomEndTime()));
|
||||
@@ -62,7 +63,7 @@ public class NightDisplayTimeFormatter {
|
||||
return context.getString(R.string.night_display_summary_off_auto_mode_custom,
|
||||
getFormattedTimeString(controller.getCustomStartTime()));
|
||||
}
|
||||
} else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
|
||||
} else if (autoMode == ColorDisplayManager.AUTO_MODE_TWILIGHT) {
|
||||
return context.getString(isActivated
|
||||
? R.string.night_display_summary_on_auto_mode_twilight
|
||||
: R.string.night_display_summary_off_auto_mode_twilight);
|
||||
|
||||
@@ -46,6 +46,7 @@ import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.core.InstrumentedPreferenceFragment;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settingslib.applications.AppUtils;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
@@ -351,6 +352,12 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro
|
||||
|
||||
@VisibleForTesting
|
||||
boolean shouldHideSipper(BatterySipper sipper) {
|
||||
// Don't show hidden system module
|
||||
final String packageName = mBatteryUtils.getPackageName(sipper.getUid());
|
||||
if (!TextUtils.isEmpty(packageName)
|
||||
&& AppUtils.isHiddenSystemModule(mContext, packageName)) {
|
||||
return true;
|
||||
}
|
||||
// Don't show over-counted and unaccounted in any condition
|
||||
return sipper.drainType == BatterySipper.DrainType.OVERCOUNTED
|
||||
|| sipper.drainType == BatterySipper.DrainType.UNACCOUNTED;
|
||||
|
||||
@@ -53,7 +53,7 @@ public class SettingsHomepageActivity extends SettingsBaseActivity {
|
||||
|
||||
final Toolbar toolbar = findViewById(R.id.search_action_bar);
|
||||
FeatureFactory.getFactory(this).getSearchFeatureProvider()
|
||||
.initSearchToolbar(this, toolbar);
|
||||
.initSearchToolbar(this /* activity */, toolbar);
|
||||
|
||||
final ImageView avatarView = findViewById(R.id.account_avatar);
|
||||
final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(this, avatarView);
|
||||
|
||||
@@ -30,16 +30,11 @@ public class BluetoothUpdateWorker extends SliceBackgroundWorker implements Blue
|
||||
|
||||
private static final String TAG = "BluetoothUpdateWorker";
|
||||
|
||||
private final Context mContext;
|
||||
private final Uri mUri;
|
||||
private final LocalBluetoothManager mLocalBluetoothManager;
|
||||
|
||||
public BluetoothUpdateWorker(Context context, Uri uri) {
|
||||
super(context, uri);
|
||||
|
||||
mContext = context;
|
||||
mUri = uri;
|
||||
mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
|
||||
mLocalBluetoothManager = Utils.getLocalBtManager(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -89,8 +84,4 @@ public class BluetoothUpdateWorker extends SliceBackgroundWorker implements Blue
|
||||
int bluetoothProfile) {
|
||||
notifySliceChange();
|
||||
}
|
||||
|
||||
private void notifySliceChange() {
|
||||
mContext.getContentResolver().notifyChange(mUri, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.inputmethod;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.view.inputmethod.InputMethodSystemProperty;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.WorkProfilePreferenceController;
|
||||
|
||||
/**
|
||||
* Preference controller for "Spell checker for work".
|
||||
*
|
||||
* @see SpellCheckerPreferenceController
|
||||
*/
|
||||
public final class SpellCheckerForWorkPreferenceController extends WorkProfilePreferenceController {
|
||||
|
||||
public SpellCheckerForWorkPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSourceMetricsCategory() {
|
||||
return SettingsEnums.SETTINGS_LANGUAGE_CATEGORY;
|
||||
}
|
||||
|
||||
@AvailabilityStatus
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
if (!mContext.getResources().getBoolean(R.bool.config_show_spellcheckers_settings)
|
||||
|| !InputMethodSystemProperty.PER_PROFILE_IME_ENABLED) {
|
||||
return UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
return super.getAvailabilityStatus();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.inputmethod;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.view.inputmethod.InputMethodSystemProperty;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.WorkProfilePreferenceController;
|
||||
|
||||
public final class VirtualKeyboardForWorkPreferenceController
|
||||
extends WorkProfilePreferenceController {
|
||||
|
||||
public VirtualKeyboardForWorkPreferenceController(Context context,
|
||||
String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSourceMetricsCategory() {
|
||||
return SettingsEnums.SETTINGS_LANGUAGE_CATEGORY;
|
||||
}
|
||||
|
||||
@AvailabilityStatus
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
if (!mContext.getResources().getBoolean(R.bool.config_show_virtual_keyboard_pref)
|
||||
|| !InputMethodSystemProperty.PER_PROFILE_IME_ENABLED) {
|
||||
return UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
return super.getAvailabilityStatus();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.language;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.view.inputmethod.InputMethodSystemProperty;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.WorkProfilePreferenceController;
|
||||
|
||||
/**
|
||||
* Preference controller for "UserDictionary for work".
|
||||
*
|
||||
* @see UserDictionaryPreferenceController
|
||||
*/
|
||||
public final class UserDictionaryForWorkPreferenceController
|
||||
extends WorkProfilePreferenceController {
|
||||
|
||||
public UserDictionaryForWorkPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSourceMetricsCategory() {
|
||||
return SettingsEnums.SETTINGS_LANGUAGE_CATEGORY;
|
||||
}
|
||||
|
||||
@AvailabilityStatus
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
if (!InputMethodSystemProperty.PER_PROFILE_IME_ENABLED) {
|
||||
return UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
return super.getAvailabilityStatus();
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@
|
||||
*/
|
||||
package com.android.settings.location;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.DAYS;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -74,6 +76,7 @@ public class RecentLocationAccessPreferenceController extends AbstractPreference
|
||||
final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE);
|
||||
intent.putExtra(Intent.EXTRA_PERMISSION_NAME,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
intent.putExtra(Intent.EXTRA_DURATION_MILLIS, DAYS.toMillis(1));
|
||||
mContext.startActivity(intent);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public class SubscriptionUtil {
|
||||
private static List<SubscriptionInfo> sResultsForTesting;
|
||||
|
||||
@VisibleForTesting
|
||||
static void setAvailableSubscriptionsForTesting(List<SubscriptionInfo> results) {
|
||||
public static void setAvailableSubscriptionsForTesting(List<SubscriptionInfo> results) {
|
||||
sResultsForTesting = results;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.telephony;
|
||||
|
||||
import android.content.Context;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
|
||||
public class CallsDefaultSubscriptionController extends DefaultSubscriptionController {
|
||||
|
||||
public CallsDefaultSubscriptionController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SubscriptionInfo getDefaultSubscriptionInfo() {
|
||||
return mManager.getDefaultVoiceSubscriptionInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDefaultSubscriptionId() {
|
||||
return SubscriptionManager.getDefaultVoiceSubscriptionId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDefaultSubscription(int subscriptionId) {
|
||||
mManager.setDefaultVoiceSubId(subscriptionId);
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,6 @@ public class CarrierPreferenceController extends BasePreferenceController {
|
||||
|
||||
public CarrierPreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
mCarrierConfigManager = new CarrierConfigManager(context);
|
||||
mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
}
|
||||
@@ -88,6 +87,7 @@ public class CarrierPreferenceController extends BasePreferenceController {
|
||||
final Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.setComponent(cn);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
|
||||
|
||||
final PackageManager pm = mContext.getPackageManager();
|
||||
final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0 /* flags */);
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.telephony;
|
||||
|
||||
import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
|
||||
import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
|
||||
|
||||
import android.content.Context;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.network.SubscriptionUtil;
|
||||
import com.android.settings.network.SubscriptionsChangeListener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.lifecycle.LifecycleObserver;
|
||||
import androidx.lifecycle.OnLifecycleEvent;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
/**
|
||||
* This implements common controller functionality for a Preference letting the user see/change
|
||||
* what mobile network subscription is used by default for some service controlled by the
|
||||
* SubscriptionManager. This can be used for services such as Calls or SMS.
|
||||
*/
|
||||
public abstract class DefaultSubscriptionController extends BasePreferenceController implements
|
||||
LifecycleObserver, Preference.OnPreferenceChangeListener,
|
||||
SubscriptionsChangeListener.SubscriptionsChangeListenerClient {
|
||||
private static final String TAG = "DefaultSubController";
|
||||
|
||||
protected SubscriptionsChangeListener mChangeListener;
|
||||
protected ListPreference mPreference;
|
||||
protected SubscriptionManager mManager;
|
||||
|
||||
public DefaultSubscriptionController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mManager = context.getSystemService(SubscriptionManager.class);
|
||||
mChangeListener = new SubscriptionsChangeListener(context, this);
|
||||
}
|
||||
|
||||
public void init(Lifecycle lifecycle) {
|
||||
lifecycle.addObserver(this);
|
||||
}
|
||||
|
||||
/** @return SubscriptionInfo for the default subscription for the service, or null if there
|
||||
* isn't one. */
|
||||
protected abstract SubscriptionInfo getDefaultSubscriptionInfo();
|
||||
|
||||
/** @return the id of the default subscription for the service, or
|
||||
* SubscriptionManager.INVALID_SUBSCRIPTION_ID if there isn't one. */
|
||||
protected abstract int getDefaultSubscriptionId();
|
||||
|
||||
/** Called to change the default subscription for the service. */
|
||||
protected abstract void setDefaultSubscription(int subscriptionId);
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(mManager);
|
||||
if (subs.size() > 1) {
|
||||
return AVAILABLE;
|
||||
} else {
|
||||
return CONDITIONALLY_UNAVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
@OnLifecycleEvent(ON_RESUME)
|
||||
public void onResume() {
|
||||
mChangeListener.start();
|
||||
updateEntries();
|
||||
}
|
||||
|
||||
@OnLifecycleEvent(ON_PAUSE)
|
||||
public void onPause() {
|
||||
mChangeListener.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
mPreference = screen.findPreference(getPreferenceKey());
|
||||
updateEntries();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
final SubscriptionInfo info = getDefaultSubscriptionInfo();
|
||||
if (info != null) {
|
||||
return info.getDisplayName();
|
||||
} else {
|
||||
return mContext.getString(R.string.calls_and_sms_ask_every_time);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateEntries() {
|
||||
if (mPreference == null) {
|
||||
return;
|
||||
}
|
||||
if (!isAvailable()) {
|
||||
mPreference.setVisible(false);
|
||||
return;
|
||||
}
|
||||
mPreference.setVisible(true);
|
||||
|
||||
final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(mManager);
|
||||
|
||||
// We'll have one entry for each available subscription, plus one for a "ask me every
|
||||
// time" entry at the end.
|
||||
final CharSequence[] displayNames = new CharSequence[subs.size() + 1];
|
||||
final CharSequence[] subscriptionIds = new CharSequence[subs.size() + 1];
|
||||
|
||||
final int serviceDefaultSubId = getDefaultSubscriptionId();
|
||||
boolean subIsAvailable = false;
|
||||
|
||||
int i = 0;
|
||||
for (; i < subs.size(); i++) {
|
||||
displayNames[i] = subs.get(i).getDisplayName();
|
||||
final int subId = subs.get(i).getSubscriptionId();
|
||||
subscriptionIds[i] = Integer.toString(subId);
|
||||
if (subId == serviceDefaultSubId) {
|
||||
subIsAvailable = true;
|
||||
}
|
||||
}
|
||||
// Add the extra "Ask every time" value at the end.
|
||||
displayNames[i] = mContext.getString(R.string.calls_and_sms_ask_every_time);
|
||||
subscriptionIds[i] = Integer.toString(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
||||
|
||||
mPreference.setEntries(displayNames);
|
||||
mPreference.setEntryValues(subscriptionIds);
|
||||
|
||||
if (subIsAvailable) {
|
||||
mPreference.setValue(Integer.toString(serviceDefaultSubId));
|
||||
} else {
|
||||
mPreference.setValue(Integer.toString(SubscriptionManager.INVALID_SUBSCRIPTION_ID));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final int subscriptionId = Integer.parseInt((String) newValue);
|
||||
setDefaultSubscription(subscriptionId);
|
||||
refreshSummary(mPreference);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAirplaneModeChanged(boolean airplaneModeEnabled) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscriptionsChanged() {
|
||||
if (mPreference != null) {
|
||||
updateEntries();
|
||||
refreshSummary(mPreference);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,6 +126,10 @@ public class MobileNetworkSettings extends RestrictedDashboardFragment {
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
|
||||
if (FeatureFlagPersistent.isEnabled(getContext(), FeatureFlags.NETWORK_INTERNET_V2)) {
|
||||
use(CallsDefaultSubscriptionController.class).init(getLifecycle());
|
||||
use(SmsDefaultSubscriptionController.class).init(getLifecycle());
|
||||
}
|
||||
use(MobileDataPreferenceController.class).init(getFragmentManager(), mSubId);
|
||||
use(RoamingPreferenceController.class).init(getFragmentManager(), mSubId);
|
||||
use(ApnPreferenceController.class).init(mSubId);
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.telephony;
|
||||
|
||||
import android.content.Context;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
|
||||
public class SmsDefaultSubscriptionController extends DefaultSubscriptionController {
|
||||
|
||||
public SmsDefaultSubscriptionController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SubscriptionInfo getDefaultSubscriptionInfo() {
|
||||
return mManager.getDefaultSmsSubscriptionInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDefaultSubscriptionId() {
|
||||
return SubscriptionManager.getDefaultSmsSubscriptionId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDefaultSubscription(int subscriptionId) {
|
||||
mManager.setDefaultSmsSubId(subscriptionId);
|
||||
}
|
||||
}
|
||||
@@ -47,8 +47,8 @@ import com.android.settings.SetupWizardUtils;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.ButtonFooterMixin;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
public class RedactionInterstitial extends SettingsActivity {
|
||||
@@ -133,8 +133,8 @@ public class RedactionInterstitial extends SettingsActivity {
|
||||
}
|
||||
|
||||
final GlifLayout layout = view.findViewById(R.id.setup_wizard_layout);
|
||||
final ButtonFooterMixin buttonFooterMixin = layout.getMixin(ButtonFooterMixin.class);
|
||||
buttonFooterMixin.setPrimaryButton(
|
||||
final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
|
||||
mixin.setPrimaryButton(
|
||||
new FooterButton.Builder(getContext())
|
||||
.setText(R.string.app_notifications_dialog_done)
|
||||
.setListener(this::onDoneButtonClicked)
|
||||
|
||||
53
src/com/android/settings/panel/NfcPanel.java
Normal file
53
src/com/android/settings/panel/NfcPanel.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package com.android.settings.panel;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SubSettings;
|
||||
import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
|
||||
import com.android.settings.slices.CustomSliceRegistry;
|
||||
import com.android.settings.slices.SliceBuilderUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NfcPanel implements PanelContent {
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
public static NfcPanel create(Context context) {
|
||||
return new NfcPanel(context);
|
||||
}
|
||||
|
||||
private NfcPanel(Context context) {
|
||||
mContext = context.getApplicationContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle() {
|
||||
return mContext.getText(R.string.nfc_quick_toggle_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Uri> getSlices() {
|
||||
final List<Uri> uris = new ArrayList<>();
|
||||
uris.add(CustomSliceRegistry.NFC_SLICE_URI);
|
||||
return uris;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getSeeMoreIntent() {
|
||||
final String screenTitle =
|
||||
mContext.getText(R.string.connected_device_connections_title).toString();
|
||||
Intent intent = SliceBuilderUtils.buildSearchResultPageIntent(mContext,
|
||||
AdvancedConnectedDeviceDashboardFragment.class.getName(),
|
||||
null /* key */,
|
||||
screenTitle,
|
||||
SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY);
|
||||
intent.setClassName(mContext.getPackageName(), SubSettings.class.getName());
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,8 @@ public class PanelFeatureProviderImpl implements PanelFeatureProvider {
|
||||
return InternetConnectivityPanel.create(context);
|
||||
case Settings.Panel.ACTION_VOLUME:
|
||||
return VolumePanel.create(context);
|
||||
case Settings.Panel.ACTION_NFC:
|
||||
return NfcPanel.create(context);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("No matching panel for: " + panelType);
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.os.Looper;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.InstrumentedFragment;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -39,11 +40,6 @@ import java.util.concurrent.Executor;
|
||||
*/
|
||||
public class BiometricFragment extends InstrumentedFragment {
|
||||
|
||||
private static final String KEY_TITLE = "title";
|
||||
private static final String KEY_SUBTITLE = "subtitle";
|
||||
private static final String KEY_DESCRIPTION = "description";
|
||||
private static final String KEY_NEGATIVE_TEXT = "negative_text";
|
||||
|
||||
// Re-set by the application. Should be done upon orientation changes, etc
|
||||
private Executor mClientExecutor;
|
||||
private AuthenticationCallback mClientCallback;
|
||||
@@ -53,7 +49,7 @@ public class BiometricFragment extends InstrumentedFragment {
|
||||
|
||||
// Created/Initialized once and retained
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
private PromptInfo mPromptInfo;
|
||||
private Bundle mBundle;
|
||||
private BiometricPrompt mBiometricPrompt;
|
||||
private CancellationSignal mCancellationSignal;
|
||||
|
||||
@@ -82,13 +78,17 @@ public class BiometricFragment extends InstrumentedFragment {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
mAuthenticationCallback.onAuthenticationError(
|
||||
BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON,
|
||||
mPromptInfo.getNegativeButtonText());
|
||||
mBundle.getString(BiometricPrompt.KEY_NEGATIVE_TEXT));
|
||||
}
|
||||
};
|
||||
|
||||
public static BiometricFragment newInstance(PromptInfo info) {
|
||||
/**
|
||||
* @param bundle Bundle passed from {@link BiometricPrompt.Builder#buildIntent()}
|
||||
* @return
|
||||
*/
|
||||
public static BiometricFragment newInstance(Bundle bundle) {
|
||||
BiometricFragment biometricFragment = new BiometricFragment();
|
||||
biometricFragment.setArguments(info.getBundle());
|
||||
biometricFragment.setArguments(bundle);
|
||||
return biometricFragment;
|
||||
}
|
||||
|
||||
@@ -119,14 +119,16 @@ public class BiometricFragment extends InstrumentedFragment {
|
||||
super.onCreate(savedInstanceState);
|
||||
setRetainInstance(true);
|
||||
|
||||
mPromptInfo = new PromptInfo(getArguments());
|
||||
mBundle = getArguments();
|
||||
mBiometricPrompt = new BiometricPrompt.Builder(getContext())
|
||||
.setTitle(mPromptInfo.getTitle())
|
||||
.setTitle(mBundle.getString(BiometricPrompt.KEY_TITLE))
|
||||
.setUseDefaultTitle() // use default title if title is null/empty
|
||||
.setSubtitle(mPromptInfo.getSubtitle())
|
||||
.setDescription(mPromptInfo.getDescription())
|
||||
.setNegativeButton(mPromptInfo.getNegativeButtonText(), mClientExecutor,
|
||||
mNegativeButtonListener)
|
||||
.setSubtitle(mBundle.getString(BiometricPrompt.KEY_SUBTITLE))
|
||||
.setDescription(mBundle.getString(BiometricPrompt.KEY_DESCRIPTION))
|
||||
.setRequireConfirmation(mBundle.getBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION))
|
||||
.setNegativeButton(getResources().getString(
|
||||
R.string.confirm_device_credential_use_alternate_method),
|
||||
mClientExecutor, mNegativeButtonListener)
|
||||
.build();
|
||||
mCancellationSignal = new CancellationSignal();
|
||||
|
||||
@@ -139,65 +141,5 @@ public class BiometricFragment extends InstrumentedFragment {
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.BIOMETRIC_FRAGMENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple wrapper for BiometricPrompt.PromptInfo. Since we want to manage the lifecycle
|
||||
* of BiometricPrompt correctly, the information needs to be stored in here.
|
||||
*/
|
||||
static class PromptInfo {
|
||||
private final Bundle mBundle;
|
||||
|
||||
private PromptInfo(Bundle bundle) {
|
||||
mBundle = bundle;
|
||||
}
|
||||
|
||||
Bundle getBundle() {
|
||||
return mBundle;
|
||||
}
|
||||
|
||||
public CharSequence getTitle() {
|
||||
return mBundle.getCharSequence(KEY_TITLE);
|
||||
}
|
||||
|
||||
public CharSequence getSubtitle() {
|
||||
return mBundle.getCharSequence(KEY_SUBTITLE);
|
||||
}
|
||||
|
||||
public CharSequence getDescription() {
|
||||
return mBundle.getCharSequence(KEY_DESCRIPTION);
|
||||
}
|
||||
|
||||
public CharSequence getNegativeButtonText() {
|
||||
return mBundle.getCharSequence(KEY_NEGATIVE_TEXT);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final Bundle mBundle = new Bundle();
|
||||
|
||||
public Builder setTitle(@NonNull CharSequence title) {
|
||||
mBundle.putCharSequence(KEY_TITLE, title);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setSubtitle(@Nullable CharSequence subtitle) {
|
||||
mBundle.putCharSequence(KEY_SUBTITLE, subtitle);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDescription(@Nullable CharSequence description) {
|
||||
mBundle.putCharSequence(KEY_DESCRIPTION, description);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setNegativeButtonText(@NonNull CharSequence text) {
|
||||
mBundle.putCharSequence(KEY_NEGATIVE_TEXT, text);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PromptInfo build() {
|
||||
return new PromptInfo(mBundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,13 +18,20 @@ package com.android.settings.password;
|
||||
|
||||
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
|
||||
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
|
||||
|
||||
import static com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment.RESULT_FINISHED;
|
||||
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
|
||||
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.admin.DevicePolicyManager.PasswordComplexity;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -66,6 +73,8 @@ import com.android.settings.search.SearchFeatureProvider;
|
||||
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
import com.android.settingslib.RestrictedPreference;
|
||||
import com.android.settingslib.widget.FooterPreference;
|
||||
import com.android.settingslib.widget.FooterPreferenceMixinCompat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -152,6 +161,14 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
private UserManager mUserManager;
|
||||
private ChooseLockGenericController mController;
|
||||
|
||||
/**
|
||||
* From intent extra {@link ChooseLockSettingsHelper#EXTRA_KEY_REQUESTED_MIN_COMPLEXITY}.
|
||||
*/
|
||||
@PasswordComplexity private int mRequestedMinComplexity;
|
||||
|
||||
/** From intent extra {@link ChooseLockSettingsHelper#EXTRA_KEY_CALLER_APP_NAME}. */
|
||||
private String mCallerAppName = null;
|
||||
|
||||
protected boolean mForFingerprint = false;
|
||||
protected boolean mForFace = false;
|
||||
|
||||
@@ -195,6 +212,10 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||
mForFace = getActivity().getIntent().getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
|
||||
mRequestedMinComplexity = getActivity().getIntent()
|
||||
.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
|
||||
mCallerAppName =
|
||||
getActivity().getIntent().getStringExtra(EXTRA_KEY_CALLER_APP_NAME);
|
||||
mForChangeCredRequiredForBoot = getArguments() != null && getArguments().getBoolean(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT);
|
||||
mUserManager = UserManager.get(getActivity());
|
||||
@@ -217,7 +238,8 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
UserManager.get(getActivity()),
|
||||
getArguments(),
|
||||
getActivity().getIntent().getExtras()).getIdentifier();
|
||||
mController = new ChooseLockGenericController(getContext(), mUserId);
|
||||
mController =
|
||||
new ChooseLockGenericController(getContext(), mUserId, mRequestedMinComplexity);
|
||||
if (ACTION_SET_NEW_PASSWORD.equals(chooseLockAction)
|
||||
&& UserManager.get(getActivity()).isManagedProfile(mUserId)
|
||||
&& mLockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
|
||||
@@ -291,6 +313,9 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
// Forward the target user id to ChooseLockGeneric.
|
||||
chooseLockGenericIntent.putExtra(Intent.EXTRA_USER_ID, mUserId);
|
||||
chooseLockGenericIntent.putExtra(CONFIRM_CREDENTIALS, !mPasswordConfirmed);
|
||||
chooseLockGenericIntent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY,
|
||||
mRequestedMinComplexity);
|
||||
chooseLockGenericIntent.putExtra(EXTRA_KEY_CALLER_APP_NAME, mCallerAppName);
|
||||
if (mUserPassword != null) {
|
||||
chooseLockGenericIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
|
||||
mUserPassword);
|
||||
@@ -461,6 +486,13 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
protected void addPreferences() {
|
||||
addPreferencesFromResource(R.xml.security_settings_picker);
|
||||
|
||||
if (!TextUtils.isEmpty(mCallerAppName)) {
|
||||
FooterPreferenceMixinCompat footerMixin =
|
||||
new FooterPreferenceMixinCompat(this, getSettingsLifecycle());
|
||||
FooterPreference footer = footerMixin.createFooterPreference();
|
||||
footer.setTitle(getFooterString());
|
||||
}
|
||||
|
||||
// Used for testing purposes
|
||||
findPreference(ScreenLockType.NONE.preferenceKey).setViewId(R.id.lock_none);
|
||||
findPreference(KEY_SKIP_FINGERPRINT).setViewId(R.id.lock_none);
|
||||
@@ -469,6 +501,27 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
findPreference(ScreenLockType.PASSWORD.preferenceKey).setViewId(R.id.lock_password);
|
||||
}
|
||||
|
||||
private String getFooterString() {
|
||||
@StringRes int stringId;
|
||||
switch (mRequestedMinComplexity) {
|
||||
case PASSWORD_COMPLEXITY_HIGH:
|
||||
stringId = R.string.unlock_footer_high_complexity_requested;
|
||||
break;
|
||||
case PASSWORD_COMPLEXITY_MEDIUM:
|
||||
stringId = R.string.unlock_footer_medium_complexity_requested;
|
||||
break;
|
||||
case PASSWORD_COMPLEXITY_LOW:
|
||||
stringId = R.string.unlock_footer_low_complexity_requested;
|
||||
break;
|
||||
case PASSWORD_COMPLEXITY_NONE:
|
||||
default:
|
||||
stringId = R.string.unlock_footer_none_complexity_requested;
|
||||
break;
|
||||
}
|
||||
|
||||
return getResources().getString(stringId, mCallerAppName);
|
||||
}
|
||||
|
||||
private void updatePreferenceText() {
|
||||
if (mForFingerprint) {
|
||||
setPreferenceTitle(ScreenLockType.PATTERN,
|
||||
@@ -624,6 +677,7 @@ public class ChooseLockGeneric extends SettingsActivity {
|
||||
ChooseLockPassword.IntentBuilder builder =
|
||||
new ChooseLockPassword.IntentBuilder(getContext())
|
||||
.setPasswordQuality(quality)
|
||||
.setRequestedMinComplexity(mRequestedMinComplexity)
|
||||
.setForFingerprint(mForFingerprint)
|
||||
.setForFace(mForFace)
|
||||
.setUserId(mUserId);
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.admin.DevicePolicyManager.PasswordComplexity;
|
||||
import android.app.admin.PasswordMetrics;
|
||||
import android.content.Context;
|
||||
import android.os.UserHandle;
|
||||
|
||||
@@ -36,6 +40,7 @@ public class ChooseLockGenericController {
|
||||
|
||||
private final Context mContext;
|
||||
private final int mUserId;
|
||||
@PasswordComplexity private final int mRequestedMinComplexity;
|
||||
private ManagedLockPasswordProvider mManagedPasswordProvider;
|
||||
private DevicePolicyManager mDpm;
|
||||
|
||||
@@ -43,6 +48,19 @@ public class ChooseLockGenericController {
|
||||
this(
|
||||
context,
|
||||
userId,
|
||||
PASSWORD_COMPLEXITY_NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param requestedMinComplexity specifies the min password complexity to be taken into account
|
||||
* when determining the available screen lock types
|
||||
*/
|
||||
public ChooseLockGenericController(Context context, int userId,
|
||||
@PasswordComplexity int requestedMinComplexity) {
|
||||
this(
|
||||
context,
|
||||
userId,
|
||||
requestedMinComplexity,
|
||||
context.getSystemService(DevicePolicyManager.class),
|
||||
ManagedLockPasswordProvider.get(context, userId));
|
||||
}
|
||||
@@ -51,21 +69,26 @@ public class ChooseLockGenericController {
|
||||
ChooseLockGenericController(
|
||||
Context context,
|
||||
int userId,
|
||||
@PasswordComplexity int requestedMinComplexity,
|
||||
DevicePolicyManager dpm,
|
||||
ManagedLockPasswordProvider managedLockPasswordProvider) {
|
||||
mContext = context;
|
||||
mUserId = userId;
|
||||
mRequestedMinComplexity = requestedMinComplexity;
|
||||
mManagedPasswordProvider = managedLockPasswordProvider;
|
||||
mDpm = dpm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The higher quality of either the specified {@code quality} or the quality required
|
||||
* by {@link DevicePolicyManager#getPasswordQuality}.
|
||||
* Returns the highest quality among the specified {@code quality}, the quality required by
|
||||
* {@link DevicePolicyManager#getPasswordQuality}, and the quality required by min password
|
||||
* complexity.
|
||||
*/
|
||||
public int upgradeQuality(int quality) {
|
||||
// Compare min allowed password quality
|
||||
return Math.max(quality, mDpm.getPasswordQuality(null, mUserId));
|
||||
// Compare specified quality and dpm quality
|
||||
int dpmUpgradedQuality = Math.max(quality, mDpm.getPasswordQuality(null, mUserId));
|
||||
return Math.max(dpmUpgradedQuality,
|
||||
PasswordMetrics.complexityLevelToMinQuality(mRequestedMinComplexity));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
|
||||
|
||||
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.admin.DevicePolicyManager.PasswordComplexity;
|
||||
import android.app.admin.PasswordMetrics;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
@@ -56,6 +60,7 @@ import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
|
||||
import com.android.internal.widget.TextViewInputDisabler;
|
||||
@@ -68,8 +73,8 @@ import com.android.settings.core.InstrumentedFragment;
|
||||
import com.android.settings.notification.RedactionInterstitial;
|
||||
import com.android.settings.widget.ImeAwareEditText;
|
||||
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.ButtonFooterMixin;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -133,6 +138,11 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntentBuilder setRequestedMinComplexity(@PasswordComplexity int level) {
|
||||
mIntent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, level);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Intent build() {
|
||||
return mIntent;
|
||||
}
|
||||
@@ -190,12 +200,10 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
private int mPasswordMinNumeric = 0;
|
||||
private int mPasswordMinNonLetter = 0;
|
||||
private int mPasswordMinLengthToFulfillAllPolicies = 0;
|
||||
private boolean mPasswordNumSequenceAllowed = true;
|
||||
@PasswordComplexity private int mRequestedMinComplexity = PASSWORD_COMPLEXITY_NONE;
|
||||
protected int mUserId;
|
||||
private byte[] mPasswordHistoryHashFactor;
|
||||
/**
|
||||
* Password requirements that we need to verify.
|
||||
*/
|
||||
private int[] mPasswordRequirements;
|
||||
|
||||
private LockPatternUtils mLockPatternUtils;
|
||||
private SaveAndFinishWorker mSaveAndFinishWorker;
|
||||
@@ -372,7 +380,13 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
mForFingerprint = intent.getBooleanExtra(
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||
mForFace = intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
|
||||
processPasswordRequirements(intent);
|
||||
mRequestedMinComplexity = intent.getIntExtra(
|
||||
EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
|
||||
mRequestedQuality = Math.max(
|
||||
intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, mRequestedQuality),
|
||||
mLockPatternUtils.getRequestedPasswordQuality(mUserId));
|
||||
|
||||
loadDpmPasswordRequirements();
|
||||
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
|
||||
|
||||
if (intent.getBooleanExtra(
|
||||
@@ -407,7 +421,7 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
ViewGroup container = view.findViewById(R.id.password_container);
|
||||
container.setOpticalInsets(Insets.NONE);
|
||||
|
||||
final ButtonFooterMixin mixin = mLayout.getMixin(ButtonFooterMixin.class);
|
||||
final FooterBarMixin mixin = mLayout.getMixin(FooterBarMixin.class);
|
||||
mixin.setSecondaryButton(
|
||||
new FooterButton.Builder(getActivity())
|
||||
.setText(R.string.lockpassword_clear_label)
|
||||
@@ -504,31 +518,6 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
}
|
||||
|
||||
private void setupPasswordRequirementsView(View view) {
|
||||
final List<Integer> passwordRequirements = new ArrayList<>();
|
||||
if (mPasswordMinUpperCase > 0) {
|
||||
passwordRequirements.add(MIN_UPPER_LETTERS_IN_PASSWORD);
|
||||
}
|
||||
if (mPasswordMinLowerCase > 0) {
|
||||
passwordRequirements.add(MIN_LOWER_LETTERS_IN_PASSWORD);
|
||||
}
|
||||
if (mPasswordMinLetters > 0) {
|
||||
if (mPasswordMinLetters > mPasswordMinUpperCase + mPasswordMinLowerCase) {
|
||||
passwordRequirements.add(MIN_LETTER_IN_PASSWORD);
|
||||
}
|
||||
}
|
||||
if (mPasswordMinNumeric > 0) {
|
||||
passwordRequirements.add(MIN_NUMBER_IN_PASSWORD);
|
||||
}
|
||||
if (mPasswordMinSymbols > 0) {
|
||||
passwordRequirements.add(MIN_SYMBOLS_IN_PASSWORD);
|
||||
}
|
||||
if (mPasswordMinNonLetter > 0) {
|
||||
if (mPasswordMinNonLetter > mPasswordMinNumeric + mPasswordMinSymbols) {
|
||||
passwordRequirements.add(MIN_NON_LETTER_IN_PASSWORD);
|
||||
}
|
||||
}
|
||||
// Convert list to array.
|
||||
mPasswordRequirements = passwordRequirements.stream().mapToInt(i -> i).toArray();
|
||||
mPasswordRestrictionView = view.findViewById(R.id.password_requirements_view);
|
||||
mPasswordRestrictionView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
mPasswordRequirementAdapter = new PasswordRequirementAdapter();
|
||||
@@ -603,13 +592,12 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
|
||||
/**
|
||||
* Read the requirements from {@link DevicePolicyManager} and intent and aggregate them.
|
||||
*
|
||||
* @param intent the incoming intent
|
||||
*/
|
||||
private void processPasswordRequirements(Intent intent) {
|
||||
private void loadDpmPasswordRequirements() {
|
||||
final int dpmPasswordQuality = mLockPatternUtils.getRequestedPasswordQuality(mUserId);
|
||||
mRequestedQuality = Math.max(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY,
|
||||
mRequestedQuality), dpmPasswordQuality);
|
||||
if (dpmPasswordQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
|
||||
mPasswordNumSequenceAllowed = false;
|
||||
}
|
||||
mPasswordMinLength = Math.max(LockPatternUtils.MIN_LOCK_PASSWORD_SIZE,
|
||||
mLockPatternUtils.getRequestedMinimumPasswordLength(mUserId));
|
||||
mPasswordMaxLength = mLockPatternUtils.getMaximumPasswordLength(mRequestedQuality);
|
||||
@@ -620,7 +608,7 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
mPasswordMinSymbols = mLockPatternUtils.getRequestedPasswordMinimumSymbols(mUserId);
|
||||
mPasswordMinNonLetter = mLockPatternUtils.getRequestedPasswordMinimumNonLetter(mUserId);
|
||||
|
||||
// Modify the value based on dpm policy.
|
||||
// Modify the value based on dpm policy
|
||||
switch (dpmPasswordQuality) {
|
||||
case PASSWORD_QUALITY_ALPHABETIC:
|
||||
if (mPasswordMinLetters == 0) {
|
||||
@@ -646,19 +634,88 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
mPasswordMinSymbols = 0;
|
||||
mPasswordMinNonLetter = 0;
|
||||
}
|
||||
|
||||
mPasswordMinLengthToFulfillAllPolicies = getMinLengthToFulfillAllPolicies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the dpm requirements and the min complexity requirements.
|
||||
*
|
||||
* <p>Since there are more than one set of metrics to meet the min complexity requirement,
|
||||
* and we are not hard-coding any one of them to be the requirements the user must fulfil,
|
||||
* we are taking what the user has already entered into account when compiling the list of
|
||||
* requirements from min complexity. Then we merge this list with the DPM requirements, and
|
||||
* present the merged set as validation results to the user on the UI.
|
||||
*
|
||||
* <p>For example, suppose min complexity requires either ALPHABETIC(8+), or
|
||||
* ALPHANUMERIC(6+). If the user has entered "a", the length requirement displayed on the UI
|
||||
* would be 8. Then the user appends "1" to make it "a1". We now know the user is entering
|
||||
* an alphanumeric password so we would update the min complexity required min length to 6.
|
||||
* This might result in a little confusion for the user but the UI does not support showing
|
||||
* multiple sets of requirements / validation results as options to users, this is the best
|
||||
* we can do now.
|
||||
*/
|
||||
private void mergeMinComplexityAndDpmRequirements(int userEnteredPasswordQuality) {
|
||||
if (mRequestedMinComplexity == PASSWORD_COMPLEXITY_NONE) {
|
||||
// dpm requirements are dominant if min complexity is none
|
||||
return;
|
||||
}
|
||||
|
||||
// reset dpm requirements
|
||||
loadDpmPasswordRequirements();
|
||||
|
||||
PasswordMetrics minMetrics = PasswordMetrics.getMinimumMetrics(
|
||||
mRequestedMinComplexity, userEnteredPasswordQuality, mRequestedQuality,
|
||||
requiresNumeric(), requiresLettersOrSymbols());
|
||||
mPasswordNumSequenceAllowed = mPasswordNumSequenceAllowed
|
||||
&& minMetrics.quality != PASSWORD_QUALITY_NUMERIC_COMPLEX;
|
||||
mPasswordMinLength = Math.max(mPasswordMinLength, minMetrics.length);
|
||||
mPasswordMinLetters = Math.max(mPasswordMinLetters, minMetrics.letters);
|
||||
mPasswordMinUpperCase = Math.max(mPasswordMinUpperCase, minMetrics.upperCase);
|
||||
mPasswordMinLowerCase = Math.max(mPasswordMinLowerCase, minMetrics.lowerCase);
|
||||
mPasswordMinNumeric = Math.max(mPasswordMinNumeric, minMetrics.numeric);
|
||||
mPasswordMinSymbols = Math.max(mPasswordMinSymbols, minMetrics.symbols);
|
||||
mPasswordMinNonLetter = Math.max(mPasswordMinNonLetter, minMetrics.nonLetter);
|
||||
|
||||
if (minMetrics.quality == PASSWORD_QUALITY_ALPHABETIC) {
|
||||
if (!requiresLettersOrSymbols()) {
|
||||
mPasswordMinLetters = 1;
|
||||
}
|
||||
}
|
||||
if (minMetrics.quality == PASSWORD_QUALITY_ALPHANUMERIC) {
|
||||
if (!requiresLettersOrSymbols()) {
|
||||
mPasswordMinLetters = 1;
|
||||
}
|
||||
if (!requiresNumeric()) {
|
||||
mPasswordMinNumeric = 1;
|
||||
}
|
||||
}
|
||||
|
||||
mPasswordMinLengthToFulfillAllPolicies = getMinLengthToFulfillAllPolicies();
|
||||
}
|
||||
|
||||
private boolean requiresLettersOrSymbols() {
|
||||
// This is the condition for the password to be considered ALPHABETIC according to
|
||||
// PasswordMetrics.computeForPassword()
|
||||
return mPasswordMinLetters + mPasswordMinUpperCase
|
||||
+ mPasswordMinLowerCase + mPasswordMinSymbols + mPasswordMinNonLetter > 0;
|
||||
}
|
||||
|
||||
private boolean requiresNumeric() {
|
||||
return mPasswordMinNumeric > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates PIN/Password and returns the validation result.
|
||||
*
|
||||
* @param password the raw password the user typed in
|
||||
* @return the validation result.
|
||||
*/
|
||||
private int validatePassword(String password) {
|
||||
@VisibleForTesting
|
||||
int validatePassword(String password) {
|
||||
int errorCode = NO_ERROR;
|
||||
final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
|
||||
|
||||
mergeMinComplexityAndDpmRequirements(metrics.quality);
|
||||
|
||||
if (password.length() < mPasswordMinLength) {
|
||||
if (mPasswordMinLength > mPasswordMinLengthToFulfillAllPolicies) {
|
||||
@@ -668,14 +725,25 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
errorCode |= TOO_LONG;
|
||||
} else {
|
||||
// The length requirements are fulfilled.
|
||||
final int dpmQuality = mLockPatternUtils.getRequestedPasswordQuality(mUserId);
|
||||
if (dpmQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX &&
|
||||
metrics.numeric == password.length()) {
|
||||
if (!mPasswordNumSequenceAllowed
|
||||
&& !requiresLettersOrSymbols()
|
||||
&& metrics.numeric == password.length()) {
|
||||
// Check for repeated characters or sequences (e.g. '1234', '0000', '2468')
|
||||
// if DevicePolicyManager requires a complex numeric password. There can be
|
||||
// two cases in the UI: 1. User chooses to enroll a PIN, 2. User chooses to
|
||||
// enroll a password but enters a numeric-only pin. We should carry out the
|
||||
// sequence check in both cases.
|
||||
// if DevicePolicyManager or min password complexity requires a complex numeric
|
||||
// password. There can be two cases in the UI: 1. User chooses to enroll a
|
||||
// PIN, 2. User chooses to enroll a password but enters a numeric-only pin. We
|
||||
// should carry out the sequence check in both cases.
|
||||
//
|
||||
// Conditions for the !requiresLettersOrSymbols() to be necessary:
|
||||
// - DPM requires NUMERIC_COMPLEX
|
||||
// - min complexity not NONE, user picks PASSWORD type so ALPHABETIC or
|
||||
// ALPHANUMERIC is required
|
||||
// Imagine user has entered "12345678", if we don't skip the sequence check, the
|
||||
// validation result would show both "requires a letter" and "sequence not
|
||||
// allowed", while the only requirement the user needs to know is "requires a
|
||||
// letter" because once the user has fulfilled the alphabetic requirement, the
|
||||
// password would not be containing only digits so this check would not be
|
||||
// performed anyway.
|
||||
final int sequence = PasswordMetrics.maxLengthSequence(password);
|
||||
if (sequence > PasswordMetrics.MAX_ALLOWED_SEQUENCE) {
|
||||
errorCode |= CONTAIN_SEQUENTIAL_DIGITS;
|
||||
@@ -706,43 +774,24 @@ public class ChooseLockPassword extends SettingsActivity {
|
||||
}
|
||||
}
|
||||
|
||||
// Check the requirements one by one.
|
||||
for (int i = 0; i < mPasswordRequirements.length; i++) {
|
||||
int passwordRestriction = mPasswordRequirements[i];
|
||||
switch (passwordRestriction) {
|
||||
case MIN_LETTER_IN_PASSWORD:
|
||||
if (metrics.letters < mPasswordMinLetters) {
|
||||
errorCode |= NOT_ENOUGH_LETTER;
|
||||
}
|
||||
break;
|
||||
case MIN_UPPER_LETTERS_IN_PASSWORD:
|
||||
if (metrics.upperCase < mPasswordMinUpperCase) {
|
||||
errorCode |= NOT_ENOUGH_UPPER_CASE;
|
||||
}
|
||||
break;
|
||||
case MIN_LOWER_LETTERS_IN_PASSWORD:
|
||||
if (metrics.lowerCase < mPasswordMinLowerCase) {
|
||||
errorCode |= NOT_ENOUGH_LOWER_CASE;
|
||||
}
|
||||
break;
|
||||
case MIN_SYMBOLS_IN_PASSWORD:
|
||||
if (metrics.symbols < mPasswordMinSymbols) {
|
||||
errorCode |= NOT_ENOUGH_SYMBOLS;
|
||||
}
|
||||
break;
|
||||
case MIN_NUMBER_IN_PASSWORD:
|
||||
if (metrics.numeric < mPasswordMinNumeric) {
|
||||
errorCode |= NOT_ENOUGH_DIGITS;
|
||||
}
|
||||
break;
|
||||
case MIN_NON_LETTER_IN_PASSWORD:
|
||||
if (metrics.nonLetter < mPasswordMinNonLetter) {
|
||||
errorCode |= NOT_ENOUGH_NON_LETTER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (metrics.letters < mPasswordMinLetters) {
|
||||
errorCode |= NOT_ENOUGH_LETTER;
|
||||
}
|
||||
if (metrics.upperCase < mPasswordMinUpperCase) {
|
||||
errorCode |= NOT_ENOUGH_UPPER_CASE;
|
||||
}
|
||||
if (metrics.lowerCase < mPasswordMinLowerCase) {
|
||||
errorCode |= NOT_ENOUGH_LOWER_CASE;
|
||||
}
|
||||
if (metrics.symbols < mPasswordMinSymbols) {
|
||||
errorCode |= NOT_ENOUGH_SYMBOLS;
|
||||
}
|
||||
if (metrics.numeric < mPasswordMinNumeric) {
|
||||
errorCode |= NOT_ENOUGH_DIGITS;
|
||||
}
|
||||
if (metrics.nonLetter < mPasswordMinNonLetter) {
|
||||
errorCode |= NOT_ENOUGH_NON_LETTER;
|
||||
}
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ import com.android.settings.core.InstrumentedFragment;
|
||||
import com.android.settings.notification.RedactionInterstitial;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
import com.google.android.setupcompat.item.FooterButton;
|
||||
import com.google.android.setupcompat.template.ButtonFooterMixin;
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
import com.google.android.setupcompat.template.FooterButton;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -489,7 +489,7 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
}
|
||||
}
|
||||
|
||||
final ButtonFooterMixin mixin = layout.getMixin(ButtonFooterMixin.class);
|
||||
final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
|
||||
mixin.setSecondaryButton(
|
||||
new FooterButton.Builder(getActivity())
|
||||
.setText(R.string.lockpattern_tutorial_cancel_label)
|
||||
@@ -512,7 +512,6 @@ public class ChooseLockPattern extends SettingsActivity {
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
@@ -45,6 +45,18 @@ public final class ChooseLockSettingsHelper {
|
||||
public static final String EXTRA_KEY_FOR_FACE = "for_face";
|
||||
public static final String EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT = "for_cred_req_boot";
|
||||
|
||||
/**
|
||||
* Intent extra for passing the requested min password complexity to later steps in the set new
|
||||
* screen lock flow.
|
||||
*/
|
||||
public static final String EXTRA_KEY_REQUESTED_MIN_COMPLEXITY = "requested_min_complexity";
|
||||
|
||||
/**
|
||||
* Intent extra for passing the label of the calling app to later steps in the set new screen
|
||||
* lock flow.
|
||||
*/
|
||||
public static final String EXTRA_KEY_CALLER_APP_NAME = "caller_app_name";
|
||||
|
||||
/**
|
||||
* When invoked via {@link ConfirmLockPassword.InternalActivity}, this flag
|
||||
* controls if we relax the enforcement of
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.app.admin.DevicePolicyManager;
|
||||
import android.app.trust.TrustManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.hardware.biometrics.BiometricConstants;
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
import android.hardware.biometrics.BiometricPrompt;
|
||||
import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback;
|
||||
@@ -87,6 +88,8 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
private TrustManager mTrustManager;
|
||||
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
|
||||
private Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
private boolean mIsFallback; // BiometricPrompt fallback
|
||||
private boolean mCCLaunched;
|
||||
|
||||
private String mTitle;
|
||||
private String mDetails;
|
||||
@@ -102,6 +105,13 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
|
||||
if (!mGoingToBackground) {
|
||||
if (errorCode == BiometricPrompt.BIOMETRIC_ERROR_USER_CANCELED) {
|
||||
if (mIsFallback) {
|
||||
mBiometricManager.onConfirmDeviceCredentialError(
|
||||
BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
|
||||
getString(
|
||||
com.android.internal.R.string
|
||||
.biometric_error_user_canceled));
|
||||
}
|
||||
finish();
|
||||
} else {
|
||||
// All other errors go to some version of CC
|
||||
@@ -118,6 +128,10 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
ConfirmDeviceCredentialUtils.checkForPendingIntent(
|
||||
ConfirmDeviceCredentialActivity.this);
|
||||
|
||||
if (mIsFallback) {
|
||||
mBiometricManager.onConfirmDeviceCredentialSuccess();
|
||||
}
|
||||
|
||||
setResult(Activity.RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
@@ -158,6 +172,19 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this);
|
||||
final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
|
||||
|
||||
Bundle bpBundle =
|
||||
intent.getBundleExtra(KeyguardManager.EXTRA_BIOMETRIC_PROMPT_BUNDLE);
|
||||
if (bpBundle != null) {
|
||||
mIsFallback = true;
|
||||
// TODO: CDC maybe should show description as well.
|
||||
mTitle = bpBundle.getString(BiometricPrompt.KEY_TITLE);
|
||||
mDetails = bpBundle.getString(BiometricPrompt.KEY_SUBTITLE);
|
||||
} else {
|
||||
bpBundle = new Bundle();
|
||||
bpBundle.putString(BiometricPrompt.KEY_TITLE, mTitle);
|
||||
bpBundle.putString(BiometricPrompt.KEY_SUBTITLE, mDetails);
|
||||
}
|
||||
|
||||
boolean launchedBiometric = false;
|
||||
boolean launchedCDC = false;
|
||||
// If the target is a managed user and user key not unlocked yet, we will force unlock
|
||||
@@ -170,7 +197,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
&& !lockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
|
||||
mCredentialMode = CREDENTIAL_MANAGED;
|
||||
if (isBiometricAllowed(effectiveUserId)) {
|
||||
showBiometricPrompt();
|
||||
showBiometricPrompt(bpBundle);
|
||||
launchedBiometric = true;
|
||||
} else {
|
||||
showConfirmCredentials();
|
||||
@@ -181,7 +208,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
if (isBiometricAllowed(effectiveUserId)) {
|
||||
// Don't need to check if biometrics / pin/pattern/pass are enrolled. It will go to
|
||||
// onAuthenticationError and do the right thing automatically.
|
||||
showBiometricPrompt();
|
||||
showBiometricPrompt(bpBundle);
|
||||
launchedBiometric = true;
|
||||
} else {
|
||||
showConfirmCredentials();
|
||||
@@ -216,6 +243,13 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
if (mBiometricFragment != null) {
|
||||
mBiometricFragment.cancel();
|
||||
}
|
||||
|
||||
if (mIsFallback && !mCCLaunched) {
|
||||
mBiometricManager.onConfirmDeviceCredentialError(
|
||||
BiometricConstants.BIOMETRIC_ERROR_CANCELED,
|
||||
getString(com.android.internal.R.string.biometric_error_user_canceled));
|
||||
}
|
||||
|
||||
finish();
|
||||
} else {
|
||||
mGoingToBackground = false;
|
||||
@@ -242,7 +276,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
&& !isBiometricDisabledByAdmin(effectiveUserId);
|
||||
}
|
||||
|
||||
private void showBiometricPrompt() {
|
||||
private void showBiometricPrompt(Bundle bundle) {
|
||||
mBiometricManager.setActiveUser(mUserId);
|
||||
|
||||
mBiometricFragment = (BiometricFragment) getSupportFragmentManager()
|
||||
@@ -250,13 +284,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
boolean newFragment = false;
|
||||
|
||||
if (mBiometricFragment == null) {
|
||||
final BiometricFragment.PromptInfo info = new BiometricFragment.PromptInfo.Builder()
|
||||
.setTitle(mTitle)
|
||||
.setSubtitle(mDetails)
|
||||
.setNegativeButtonText(getResources()
|
||||
.getString(R.string.confirm_device_credential_use_alternate_method))
|
||||
.build();
|
||||
mBiometricFragment = BiometricFragment.newInstance(info);
|
||||
mBiometricFragment = BiometricFragment.newInstance(bundle);
|
||||
newFragment = true;
|
||||
}
|
||||
mBiometricFragment.setCallbacks(mExecutor, mAuthenticationCallback);
|
||||
@@ -272,6 +300,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
|
||||
* Shows ConfirmDeviceCredentials for normal apps.
|
||||
*/
|
||||
private void showConfirmCredentials() {
|
||||
mCCLaunched = true;
|
||||
boolean launched = false;
|
||||
if (mCredentialMode == CREDENTIAL_MANAGED) {
|
||||
// We set the challenge as 0L, so it will force to unlock managed profile when it
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package com.android.settings.password;
|
||||
|
||||
import android.app.KeyguardManager;
|
||||
import android.hardware.biometrics.BiometricConstants;
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserManager;
|
||||
import android.view.MenuItem;
|
||||
@@ -45,6 +47,7 @@ public abstract class ConfirmDeviceCredentialBaseActivity extends SettingsActivi
|
||||
private boolean mFirstTimeVisible = true;
|
||||
private boolean mIsKeyguardLocked = false;
|
||||
private ConfirmCredentialTheme mConfirmCredentialTheme;
|
||||
private BiometricManager mBiometricManager;
|
||||
|
||||
private boolean isInternalActivity() {
|
||||
return (this instanceof ConfirmLockPassword.InternalActivity)
|
||||
@@ -68,6 +71,8 @@ public abstract class ConfirmDeviceCredentialBaseActivity extends SettingsActivi
|
||||
}
|
||||
super.onCreate(savedState);
|
||||
|
||||
mBiometricManager = getSystemService(BiometricManager.class);
|
||||
|
||||
if (mConfirmCredentialTheme == ConfirmCredentialTheme.NORMAL) {
|
||||
// Prevent the content parent from consuming the window insets because GlifLayout uses
|
||||
// it to show the status bar background.
|
||||
@@ -140,6 +145,17 @@ public abstract class ConfirmDeviceCredentialBaseActivity extends SettingsActivi
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
// TODO(b/123378871): Remove when moved.
|
||||
if (!isChangingConfigurations()) {
|
||||
mBiometricManager.onConfirmDeviceCredentialError(
|
||||
BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
|
||||
getString(com.android.internal.R.string.biometric_error_user_canceled));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
super.finish();
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.graphics.Point;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.UserManager;
|
||||
@@ -84,6 +85,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
||||
protected final Handler mHandler = new Handler();
|
||||
protected boolean mFrp;
|
||||
private CharSequence mFrpAlternateButtonText;
|
||||
protected BiometricManager mBiometricManager;
|
||||
|
||||
private boolean isInternalActivity() {
|
||||
return (getActivity() instanceof ConfirmLockPassword.InternalActivity)
|
||||
@@ -107,6 +109,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
|
||||
mLockPatternUtils = new LockPatternUtils(getActivity());
|
||||
mDevicePolicyManager = (DevicePolicyManager) getActivity().getSystemService(
|
||||
Context.DEVICE_POLICY_SERVICE);
|
||||
mBiometricManager = getActivity().getSystemService(BiometricManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -437,6 +437,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
|
||||
ConfirmDeviceCredentialUtils.reportSuccessfulAttempt(mLockPatternUtils,
|
||||
mUserManager, mEffectiveUserId);
|
||||
}
|
||||
mBiometricManager.onConfirmDeviceCredentialSuccess();
|
||||
startDisappearAnimation(intent);
|
||||
ConfirmDeviceCredentialUtils.checkForPendingIntent(getActivity());
|
||||
} else {
|
||||
|
||||
@@ -490,6 +490,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
|
||||
ConfirmDeviceCredentialUtils.reportSuccessfulAttempt(mLockPatternUtils,
|
||||
mUserManager, mEffectiveUserId);
|
||||
}
|
||||
mBiometricManager.onConfirmDeviceCredentialSuccess();
|
||||
startDisappearAnimation(intent);
|
||||
ConfirmDeviceCredentialUtils.checkForPendingIntent(getActivity());
|
||||
} else {
|
||||
|
||||
97
src/com/android/settings/password/PasswordUtils.java
Normal file
97
src/com/android/settings/password/PasswordUtils.java
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.password;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.IActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.Utils;
|
||||
|
||||
public final class PasswordUtils extends com.android.settingslib.Utils {
|
||||
|
||||
private static final String TAG = "Settings";
|
||||
|
||||
private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
|
||||
|
||||
/**
|
||||
* Returns whether the uid which the activity with {@code activityToken} is launched from has
|
||||
* been granted the {@code permission}.
|
||||
*/
|
||||
public static boolean isCallingAppPermitted(Context context, IBinder activityToken,
|
||||
String permission) {
|
||||
try {
|
||||
return context.checkPermission(permission, /* pid= */ -1,
|
||||
ActivityManager.getService().getLaunchedFromUid(activityToken))
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
} catch (RemoteException e) {
|
||||
Log.v(TAG, "Could not talk to activity manager.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label of the package which the activity with {@code activityToken} is launched
|
||||
* from or {@code null} if it is launched from the settings app itself.
|
||||
*/
|
||||
@Nullable
|
||||
public static CharSequence getCallingAppLabel(Context context, IBinder activityToken) {
|
||||
String pkg = getCallingAppPackageName(activityToken);
|
||||
if (pkg == null || pkg.equals(SETTINGS_PACKAGE_NAME)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Utils.getApplicationLabel(context, pkg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the package name which the activity with {@code activityToken} is launched from.
|
||||
*/
|
||||
@Nullable
|
||||
private static String getCallingAppPackageName(IBinder activityToken) {
|
||||
String pkg = null;
|
||||
try {
|
||||
pkg = ActivityManager.getService().getLaunchedFromPackage(activityToken);
|
||||
} catch (RemoteException e) {
|
||||
Log.v(TAG, "Could not talk to activity manager.", e);
|
||||
}
|
||||
return pkg;
|
||||
}
|
||||
|
||||
/** Crashes the calling application and provides it with {@code message}. */
|
||||
public static void crashCallingApplication(IBinder activityToken, String message) {
|
||||
IActivityManager am = ActivityManager.getService();
|
||||
try {
|
||||
int uid = am.getLaunchedFromUid(activityToken);
|
||||
int userId = UserHandle.getUserId(uid);
|
||||
am.crashApplication(
|
||||
uid,
|
||||
/* initialPid= */ -1,
|
||||
getCallingAppPackageName(activityToken),
|
||||
userId,
|
||||
message);
|
||||
} catch (RemoteException e) {
|
||||
Log.v(TAG, "Could not talk to activity manager.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,22 @@
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY;
|
||||
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PARENT_PROFILE_PASSWORD;
|
||||
import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
|
||||
import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
|
||||
|
||||
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
|
||||
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.admin.DevicePolicyManager.PasswordComplexity;
|
||||
import android.app.admin.PasswordMetrics;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.Utils;
|
||||
@@ -37,6 +46,21 @@ public class SetNewPasswordActivity extends Activity implements SetNewPasswordCo
|
||||
private String mNewPasswordAction;
|
||||
private SetNewPasswordController mSetNewPasswordController;
|
||||
|
||||
/**
|
||||
* From intent extra {@link DevicePolicyManager#EXTRA_PASSWORD_COMPLEXITY}.
|
||||
*
|
||||
* <p>This is used only if caller has the required permission and activity is launched by
|
||||
* {@link DevicePolicyManager#ACTION_SET_NEW_PASSWORD}.
|
||||
*/
|
||||
private @PasswordComplexity int mRequestedMinComplexity = PASSWORD_COMPLEXITY_NONE;
|
||||
|
||||
/**
|
||||
* Label of the app which launches this activity.
|
||||
*
|
||||
* <p>Value would be {@code null} if launched from settings app.
|
||||
*/
|
||||
private String mCallerAppName = null;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedState) {
|
||||
super.onCreate(savedState);
|
||||
@@ -48,6 +72,25 @@ public class SetNewPasswordActivity extends Activity implements SetNewPasswordCo
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
IBinder activityToken = getActivityToken();
|
||||
mCallerAppName = (String) PasswordUtils.getCallingAppLabel(this, activityToken);
|
||||
if (ACTION_SET_NEW_PASSWORD.equals(mNewPasswordAction)
|
||||
&& getIntent().hasExtra(EXTRA_PASSWORD_COMPLEXITY)) {
|
||||
boolean hasPermission = PasswordUtils.isCallingAppPermitted(
|
||||
this, activityToken, GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
|
||||
if (hasPermission) {
|
||||
mRequestedMinComplexity = PasswordMetrics.sanitizeComplexityLevel(getIntent()
|
||||
.getIntExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_NONE));
|
||||
} else {
|
||||
PasswordUtils.crashCallingApplication(activityToken,
|
||||
"Must have permission " + GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY
|
||||
+ " to use extra " + EXTRA_PASSWORD_COMPLEXITY);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mSetNewPasswordController = SetNewPasswordController.create(
|
||||
this, this, getIntent(), getActivityToken());
|
||||
mSetNewPasswordController.dispatchSetNewPasswordIntent();
|
||||
@@ -60,6 +103,12 @@ public class SetNewPasswordActivity extends Activity implements SetNewPasswordCo
|
||||
: new Intent(this, ChooseLockGeneric.class);
|
||||
intent.setAction(mNewPasswordAction);
|
||||
intent.putExtras(chooseLockFingerprintExtras);
|
||||
if (mCallerAppName != null) {
|
||||
intent.putExtra(EXTRA_KEY_CALLER_APP_NAME, mCallerAppName);
|
||||
}
|
||||
if (mRequestedMinComplexity != PASSWORD_COMPLEXITY_NONE) {
|
||||
intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, mRequestedMinComplexity);
|
||||
}
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
@@ -16,11 +16,17 @@
|
||||
|
||||
package com.android.settings.password;
|
||||
|
||||
import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY;
|
||||
import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY;
|
||||
|
||||
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.UserHandle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -48,6 +54,7 @@ import com.google.android.setupdesign.GlifPreferenceLayout;
|
||||
* Other changes should be done to ChooseLockGeneric class instead and let this class inherit
|
||||
* those changes.
|
||||
*/
|
||||
// TODO(b/123225425): Restrict SetupChooseLockGeneric to be accessible by SUW only
|
||||
public class SetupChooseLockGeneric extends ChooseLockGeneric {
|
||||
|
||||
private static final String KEY_UNLOCK_SET_DO_LATER = "unlock_set_do_later";
|
||||
@@ -71,6 +78,20 @@ public class SetupChooseLockGeneric extends ChooseLockGeneric {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstance) {
|
||||
super.onCreate(savedInstance);
|
||||
|
||||
if(getIntent().hasExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY)) {
|
||||
IBinder activityToken = getActivityToken();
|
||||
boolean hasPermission = PasswordUtils.isCallingAppPermitted(
|
||||
this, activityToken, GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY);
|
||||
if (!hasPermission) {
|
||||
PasswordUtils.crashCallingApplication(activityToken,
|
||||
"Must have permission " + GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY
|
||||
+ " to use extra " + EXTRA_PASSWORD_COMPLEXITY);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LinearLayout layout = (LinearLayout) findViewById(R.id.content_parent);
|
||||
layout.setFitsSystemWindows(false);
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ public class PermissionBarChartPreferenceController extends BasePreferenceContro
|
||||
.setEmptyText(R.string.permission_bar_chart_empty_text)
|
||||
.setDetailsOnClickListener((View v) -> {
|
||||
final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE);
|
||||
intent.putExtra(Intent.EXTRA_DURATION_MILLIS, DAYS.toMillis(1));
|
||||
mContext.startActivity(intent);
|
||||
})
|
||||
.build();
|
||||
@@ -102,7 +103,7 @@ public class PermissionBarChartPreferenceController extends BasePreferenceContro
|
||||
|
||||
private void retrievePermissionUsageData() {
|
||||
mContext.getSystemService(PermissionControllerManager.class).getPermissionUsages(
|
||||
false /* countSystem */, (int) DAYS.toSeconds(1),
|
||||
false /* countSystem */, (int) DAYS.toMillis(1),
|
||||
mContext.getMainExecutor() /* executor */, this /* callback */);
|
||||
}
|
||||
|
||||
@@ -111,6 +112,9 @@ public class PermissionBarChartPreferenceController extends BasePreferenceContro
|
||||
return null;
|
||||
}
|
||||
|
||||
// STOPSHIP: Ignore the STORAGE group since it's going away.
|
||||
usageInfos.removeIf(usage -> usage.getName().equals("android.permission-group.STORAGE"));
|
||||
|
||||
final BarViewInfo[] barViewInfos = new BarViewInfo[
|
||||
Math.min(BarChartPreference.MAXIMUM_BAR_VIEWS, usageInfos.size())];
|
||||
|
||||
|
||||
@@ -1,86 +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.security;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.android.settings.CredentialStorage;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
import com.android.settings.password.ChooseLockGeneric;
|
||||
|
||||
/**
|
||||
* Prompt for key guard configuration confirmation.
|
||||
*/
|
||||
public class ConfigureKeyGuardDialog extends InstrumentedDialogFragment
|
||||
implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
|
||||
|
||||
public static final String TAG = "ConfigureKeyGuardDialog";
|
||||
|
||||
private boolean mConfigureConfirmed;
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.CONFIGURE_KEYGUARD_DIALOG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setTitle(android.R.string.dialog_alert_title)
|
||||
.setMessage(R.string.credentials_configure_lock_screen_hint)
|
||||
.setPositiveButton(R.string.credentials_configure_lock_screen_button, this)
|
||||
.setNegativeButton(android.R.string.cancel, this)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int button) {
|
||||
mConfigureConfirmed = (button == DialogInterface.BUTTON_POSITIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
if (mConfigureConfirmed) {
|
||||
mConfigureConfirmed = false;
|
||||
startPasswordSetup();
|
||||
return;
|
||||
} else {
|
||||
final Activity activity = getActivity();
|
||||
if (activity != null) {
|
||||
activity.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void startPasswordSetup() {
|
||||
Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
|
||||
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
|
||||
CredentialStorage.MIN_PASSWORD_QUALITY);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings;
|
||||
package com.android.settings.security;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
@@ -44,8 +44,8 @@ import androidx.fragment.app.FragmentActivity;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.org.bouncycastle.asn1.ASN1InputStream;
|
||||
import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settings.security.ConfigureKeyGuardDialog;
|
||||
import com.android.settings.vpn2.VpnUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -55,46 +55,12 @@ import sun.security.util.ObjectIdentifier;
|
||||
import sun.security.x509.AlgorithmId;
|
||||
|
||||
/**
|
||||
* CredentialStorage handles KeyStore reset, unlock, and install.
|
||||
*
|
||||
* CredentialStorage has a pretty convoluted state machine to migrate
|
||||
* from the old style separate keystore password to a new key guard
|
||||
* based password, as well as to deal with setting up the key guard if
|
||||
* necessary.
|
||||
*
|
||||
* KeyStore: UNINITALIZED
|
||||
* KeyGuard: OFF
|
||||
* Action: set up key guard
|
||||
* Notes: factory state
|
||||
*
|
||||
* KeyStore: UNINITALIZED
|
||||
* KeyGuard: ON
|
||||
* Action: confirm key guard
|
||||
* Notes: user had key guard but no keystore and upgraded from pre-ICS
|
||||
* OR user had key guard and pre-ICS keystore password which was then reset
|
||||
*
|
||||
* KeyStore: LOCKED
|
||||
* KeyGuard: OFF/ON
|
||||
* Action: confirm key guard
|
||||
* Notes: request normal unlock to unlock the keystore.
|
||||
* if unlock, ensure key guard before install.
|
||||
* if reset, treat as UNINITALIZED/OFF
|
||||
*
|
||||
* KeyStore: UNLOCKED
|
||||
* KeyGuard: OFF
|
||||
* Action: set up key guard
|
||||
* Notes: ensure key guard, then proceed
|
||||
*
|
||||
* KeyStore: UNLOCKED
|
||||
* keyguard: ON
|
||||
* Action: normal unlock/install
|
||||
* Notes: this is the common case
|
||||
* CredentialStorage handles resetting and installing keys into KeyStore.
|
||||
*/
|
||||
public final class CredentialStorage extends FragmentActivity {
|
||||
|
||||
private static final String TAG = "CredentialStorage";
|
||||
|
||||
public static final String ACTION_UNLOCK = "com.android.credentials.UNLOCK";
|
||||
public static final String ACTION_INSTALL = "com.android.credentials.INSTALL";
|
||||
public static final String ACTION_RESET = "com.android.credentials.RESET";
|
||||
|
||||
@@ -102,8 +68,7 @@ public final class CredentialStorage extends FragmentActivity {
|
||||
// lower than this, keystore should not be activated.
|
||||
public static final int MIN_PASSWORD_QUALITY = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
|
||||
|
||||
private static final int CONFIRM_KEY_GUARD_REQUEST = 1;
|
||||
private static final int CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST = 2;
|
||||
private static final int CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST = 1;
|
||||
|
||||
private final KeyStore mKeyStore = KeyStore.getInstance();
|
||||
private LockPatternUtils mUtils;
|
||||
@@ -133,75 +98,26 @@ public final class CredentialStorage extends FragmentActivity {
|
||||
if (ACTION_INSTALL.equals(action) && checkCallerIsCertInstallerOrSelfInProfile()) {
|
||||
mInstallBundle = intent.getExtras();
|
||||
}
|
||||
// ACTION_UNLOCK also handled here in addition to ACTION_INSTALL
|
||||
handleUnlockOrInstall();
|
||||
handleInstall();
|
||||
}
|
||||
} else {
|
||||
// Users can set a screen lock if there is none even if they can't modify the
|
||||
// credentials store.
|
||||
if (ACTION_UNLOCK.equals(action) && mKeyStore.state() == KeyStore.State.UNINITIALIZED) {
|
||||
ensureKeyGuard();
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the current state of the KeyStore and key guard, try to
|
||||
* make progress on unlocking or installing to the keystore.
|
||||
* Install credentials from mInstallBundle into Keystore.
|
||||
*/
|
||||
private void handleUnlockOrInstall() {
|
||||
private void handleInstall() {
|
||||
// something already decided we are done, do not proceed
|
||||
if (isFinishing()) {
|
||||
return;
|
||||
}
|
||||
switch (mKeyStore.state()) {
|
||||
case UNINITIALIZED: {
|
||||
ensureKeyGuard();
|
||||
return;
|
||||
}
|
||||
case LOCKED: {
|
||||
// Force key guard confirmation
|
||||
confirmKeyGuard(CONFIRM_KEY_GUARD_REQUEST);
|
||||
return;
|
||||
}
|
||||
case UNLOCKED: {
|
||||
if (!mUtils.isSecure(UserHandle.myUserId())) {
|
||||
final ConfigureKeyGuardDialog dialog = new ConfigureKeyGuardDialog();
|
||||
dialog.show(getSupportFragmentManager(), ConfigureKeyGuardDialog.TAG);
|
||||
return;
|
||||
}
|
||||
if (installIfAvailable()) {
|
||||
finish();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (installIfAvailable()) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the user enters the key guard to set or change the
|
||||
* keystore password. This can be used in UNINITIALIZED to set the
|
||||
* keystore password or UNLOCKED to change the password (as is the
|
||||
* case after unlocking with an old-style password).
|
||||
*/
|
||||
private void ensureKeyGuard() {
|
||||
if (!mUtils.isSecure(UserHandle.myUserId())) {
|
||||
// key guard not setup, doing so will initialize keystore
|
||||
final ConfigureKeyGuardDialog dialog = new ConfigureKeyGuardDialog();
|
||||
dialog.show(getSupportFragmentManager(), ConfigureKeyGuardDialog.TAG);
|
||||
// will return to onResume after Activity
|
||||
return;
|
||||
}
|
||||
// force key guard confirmation
|
||||
if (confirmKeyGuard(CONFIRM_KEY_GUARD_REQUEST)) {
|
||||
// will return password value via onActivityResult
|
||||
return;
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
private boolean isHardwareBackedKey(byte[] keyData) {
|
||||
try {
|
||||
final ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(keyData));
|
||||
@@ -254,15 +170,7 @@ public final class CredentialStorage extends FragmentActivity {
|
||||
final String key = bundle.getString(Credentials.EXTRA_USER_PRIVATE_KEY_NAME);
|
||||
final byte[] value = bundle.getByteArray(Credentials.EXTRA_USER_PRIVATE_KEY_DATA);
|
||||
|
||||
int flags = KeyStore.FLAG_ENCRYPTED;
|
||||
if (uid == Process.WIFI_UID && isHardwareBackedKey(value)) {
|
||||
// Hardware backed keystore is secure enough to allow for WIFI stack
|
||||
// to enable access to secure networks without user intervention
|
||||
Log.d(TAG, "Saving private key with FLAG_NONE for WIFI_UID");
|
||||
flags = KeyStore.FLAG_NONE;
|
||||
}
|
||||
|
||||
if (!mKeyStore.importKey(key, value, uid, flags)) {
|
||||
if (!mKeyStore.importKey(key, value, uid, KeyStore.FLAG_NONE)) {
|
||||
Log.e(TAG, "Failed to install " + key + " as uid " + uid);
|
||||
return true;
|
||||
}
|
||||
@@ -475,20 +383,7 @@ public final class CredentialStorage extends FragmentActivity {
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
// Receive key guard password initiated by confirmKeyGuard.
|
||||
if (requestCode == CONFIRM_KEY_GUARD_REQUEST) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
final String password = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
if (!TextUtils.isEmpty(password)) {
|
||||
// success
|
||||
mKeyStore.unlock(password);
|
||||
// return to onResume
|
||||
return;
|
||||
}
|
||||
}
|
||||
// failed confirmation, bail
|
||||
finish();
|
||||
} else if (requestCode == CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST) {
|
||||
if (requestCode == CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
new ResetKeyStoreAndKeyChain().execute();
|
||||
return;
|
||||
7
src/com/android/settings/security/OWNERS
Normal file
7
src/com/android/settings/security/OWNERS
Normal file
@@ -0,0 +1,7 @@
|
||||
# Enterprise security reviewers
|
||||
eranm@google.com
|
||||
pgrafov@google.com
|
||||
rubinxu@google.com
|
||||
|
||||
# Emergency
|
||||
sandness@google.com
|
||||
@@ -154,6 +154,15 @@ public class CustomSliceRegistry {
|
||||
.appendEncodedPath(SettingsSlicesContract.PATH_SETTING_INTENT)
|
||||
.appendPath("low_storage")
|
||||
.build();
|
||||
/**
|
||||
* Backing Uri for NFC Slice
|
||||
*/
|
||||
public static final Uri NFC_SLICE_URI = new Uri.Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
|
||||
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
|
||||
.appendPath("toggle_nfc")
|
||||
.build();
|
||||
/**
|
||||
* Backing Uri for Notification channel Slice.
|
||||
*/
|
||||
|
||||
@@ -59,10 +59,6 @@ public abstract class SliceBackgroundWorker<E> implements Closeable {
|
||||
mUri = uri;
|
||||
}
|
||||
|
||||
protected Uri getUri() {
|
||||
return mUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the singleton instance of the {@link SliceBackgroundWorker} for specified {@link Uri}
|
||||
* if exists
|
||||
@@ -148,7 +144,14 @@ public abstract class SliceBackgroundWorker<E> implements Closeable {
|
||||
|
||||
if (needNotify) {
|
||||
mCachedResults = results;
|
||||
mContext.getContentResolver().notifyChange(mUri, null);
|
||||
notifySliceChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify that data was updated and attempt to sync changes to the Slice.
|
||||
*/
|
||||
protected void notifySliceChange() {
|
||||
mContext.getContentResolver().notifyChange(mUri, null);
|
||||
}
|
||||
}
|
||||
@@ -56,8 +56,6 @@ public class ConfigDialogFragment extends InstrumentedDialogFragment implements
|
||||
ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
|
||||
private Context mContext;
|
||||
|
||||
private boolean mUnlocking = false;
|
||||
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
@@ -84,27 +82,6 @@ public class ConfigDialogFragment extends InstrumentedDialogFragment implements
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Check KeyStore here, so others do not need to deal with it.
|
||||
if (!KeyStore.getInstance().isUnlocked()) {
|
||||
if (!mUnlocking) {
|
||||
// Let us unlock KeyStore. See you later!
|
||||
Credentials.getInstance().unlock(mContext);
|
||||
} else {
|
||||
// We already tried, but it is still not working!
|
||||
dismiss();
|
||||
}
|
||||
mUnlocking = !mUnlocking;
|
||||
return;
|
||||
}
|
||||
|
||||
// Now KeyStore is always unlocked. Reset the flag.
|
||||
mUnlocking = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
Bundle args = getArguments();
|
||||
|
||||
@@ -29,6 +29,8 @@ import androidx.annotation.VisibleForTesting;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
|
||||
public class WallpaperSuggestionActivity extends Activity {
|
||||
|
||||
@Override
|
||||
@@ -39,6 +41,10 @@ public class WallpaperSuggestionActivity extends Activity {
|
||||
.setClassName(getString(R.string.config_wallpaper_picker_package),
|
||||
getString(R.string.config_wallpaper_picker_class))
|
||||
.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
|
||||
|
||||
// passing the necessary extra to next page
|
||||
WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent);
|
||||
|
||||
if (pm.resolveActivity(intent, 0) != null) {
|
||||
startActivity(intent);
|
||||
} else {
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.app.Dialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.wifi.ScanResult;
|
||||
import android.net.wifi.WifiConfiguration;
|
||||
@@ -74,6 +75,9 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp
|
||||
/** Delayed time to stop scanning wifi. */
|
||||
private static final int DELAY_TIME_STOP_SCAN_MS = 30 * 1000;
|
||||
|
||||
@VisibleForTesting
|
||||
final static String EXTRA_APP_NAME = "com.android.settings.wifi.extra.APP_NAME";
|
||||
|
||||
private List<AccessPoint> mAccessPointList;
|
||||
private FilterWifiTracker mFilterWifiTracker;
|
||||
private AccessPointAdapter mDialogAdapter;
|
||||
@@ -93,7 +97,8 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp
|
||||
final View customTitle = inflater.inflate(R.layout.network_request_dialog_title, null);
|
||||
|
||||
final TextView title = customTitle.findViewById(R.id.network_request_title_text);
|
||||
title.setText(R.string.network_connection_request_dialog_title);
|
||||
title.setText(getTitle());
|
||||
|
||||
final ProgressBar progressBar = customTitle.findViewById(
|
||||
R.id.network_request_title_progress);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
@@ -115,6 +120,16 @@ public class NetworkRequestDialogFragment extends InstrumentedDialogFragment imp
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private String getTitle() {
|
||||
final Intent intent = getActivity().getIntent();
|
||||
String appName = "";
|
||||
if (intent != null) {
|
||||
appName = intent.getStringExtra(EXTRA_APP_NAME);
|
||||
}
|
||||
|
||||
return getString(R.string.network_connection_request_dialog_title, appName);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
List<AccessPoint> getAccessPointList() {
|
||||
// Initials list for adapter, in case of display crashing.
|
||||
|
||||
@@ -63,6 +63,7 @@ import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.ProxySelector;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.wifi.details.WifiPrivacyPreferenceController;
|
||||
import com.android.settingslib.Utils;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
import com.android.settingslib.wifi.AccessPoint;
|
||||
@@ -251,8 +252,6 @@ public class WifiConfigController implements TextWatcher,
|
||||
com.android.settings.core.FeatureFlags.WIFI_MAC_RANDOMIZATION)) {
|
||||
View privacySettingsLayout = mView.findViewById(R.id.privacy_settings_fields);
|
||||
privacySettingsLayout.setVisibility(View.VISIBLE);
|
||||
// Set default value
|
||||
mPrivacySettingsSpinner.setSelection(WifiConfiguration.RANDOMIZATION_PERSISTENT);
|
||||
}
|
||||
mHiddenSettingsSpinner.setOnItemSelectedListener(this);
|
||||
mHiddenWarningView = mView.findViewById(R.id.hidden_settings_warning);
|
||||
@@ -281,7 +280,12 @@ public class WifiConfigController implements TextWatcher,
|
||||
mHiddenSettingsSpinner.setSelection(config.hiddenSSID
|
||||
? HIDDEN_NETWORK
|
||||
: NOT_HIDDEN_NETWORK);
|
||||
mPrivacySettingsSpinner.setSelection(config.macRandomizationSetting);
|
||||
|
||||
final int prefMacValue =
|
||||
WifiPrivacyPreferenceController.translateMacRandomizedValueToPrefValue(
|
||||
config.macRandomizationSetting);
|
||||
mPrivacySettingsSpinner.setSelection(prefMacValue);
|
||||
|
||||
if (config.getIpAssignment() == IpAssignment.STATIC) {
|
||||
mIpSettingsSpinner.setSelection(STATIC_IP);
|
||||
showAdvancedFields = true;
|
||||
@@ -766,7 +770,10 @@ public class WifiConfigController implements TextWatcher,
|
||||
}
|
||||
|
||||
if (mPrivacySettingsSpinner != null) {
|
||||
config.macRandomizationSetting = mPrivacySettingsSpinner.getSelectedItemPosition();
|
||||
final int macValue =
|
||||
WifiPrivacyPreferenceController.translatePrefValueToMacRandomizedValue(
|
||||
mPrivacySettingsSpinner.getSelectedItemPosition());
|
||||
config.macRandomizationSetting = macValue;
|
||||
}
|
||||
|
||||
return config;
|
||||
|
||||
@@ -290,7 +290,8 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
|
||||
.setButton2OnClickListener(view -> signIntoNetwork())
|
||||
.setButton3Text(R.string.share)
|
||||
.setButton3Icon(R.drawable.ic_qrcode_24dp)
|
||||
.setButton3OnClickListener(view -> shareNetwork());
|
||||
.setButton3OnClickListener(view -> shareNetwork())
|
||||
.setButton3Visible(WifiDppUtils.isSuportConfigurator(mContext, mAccessPoint));
|
||||
|
||||
mSignalStrengthPref = screen.findPreference(KEY_SIGNAL_STRENGTH_PREF);
|
||||
mTxLinkSpeedPref = screen.findPreference(KEY_TX_LINK_SPEED);
|
||||
@@ -558,10 +559,15 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
|
||||
/**
|
||||
* Show QR code to share the network represented by this preference.
|
||||
*/
|
||||
public void launchQRCodeGenerator() {
|
||||
Intent intent = WifiDppUtils.getConfiguratorQrCodeGeneratorIntent(mContext, mWifiManager,
|
||||
public void launchWifiDppConfiguratorActivity() {
|
||||
final Intent intent = WifiDppUtils.getConfiguratorIntentOrNull(mContext, mWifiManager,
|
||||
mAccessPoint);
|
||||
mContext.startActivity(intent);
|
||||
|
||||
if (intent == null) {
|
||||
Log.e(TAG, "Launch Wi-Fi DPP configurator with a wrong Wi-Fi network!");
|
||||
} else {
|
||||
mContext.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -584,7 +590,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
|
||||
WifiNetworkDetailsFragment.REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS);
|
||||
}
|
||||
} else {
|
||||
launchQRCodeGenerator();
|
||||
launchWifiDppConfiguratorActivity();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ public class WifiNetworkDetailsFragment extends DashboardFragment {
|
||||
|
||||
if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS
|
||||
&& resultCode == Activity.RESULT_OK) {
|
||||
mWifiDetailPreferenceController.launchQRCodeGenerator();
|
||||
mWifiDetailPreferenceController.launchWifiDppConfiguratorActivity();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,15 +85,29 @@ public class WifiPrivacyPreferenceController extends BasePreferenceController im
|
||||
return WifiConfiguration.RANDOMIZATION_PERSISTENT;
|
||||
}
|
||||
|
||||
private final int PREF_RANDOMIZATION_PERSISTENT = 0;
|
||||
private final int PREF_RANDOMIZATION_NONE = 1;
|
||||
@VisibleForTesting
|
||||
protected int translateMacRandomizedValueToPrefValue(int macRandomized) {
|
||||
if (macRandomized == WifiConfiguration.RANDOMIZATION_PERSISTENT) {
|
||||
return PREF_RANDOMIZATION_PERSISTENT;
|
||||
} else {
|
||||
return PREF_RANDOMIZATION_NONE;
|
||||
}
|
||||
private static final int PREF_RANDOMIZATION_PERSISTENT = 0;
|
||||
private static final int PREF_RANDOMIZATION_NONE = 1;
|
||||
|
||||
/**
|
||||
* Returns preference index value.
|
||||
*
|
||||
* @param macRandomized is mac randomized value
|
||||
* @return index value of preference
|
||||
*/
|
||||
public static int translateMacRandomizedValueToPrefValue(int macRandomized) {
|
||||
return (macRandomized == WifiConfiguration.RANDOMIZATION_PERSISTENT)
|
||||
? PREF_RANDOMIZATION_PERSISTENT : PREF_RANDOMIZATION_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns mac randomized value.
|
||||
*
|
||||
* @param prefMacRandomized is preference index value
|
||||
* @return mac randomized value
|
||||
*/
|
||||
public static int translatePrefValueToMacRandomizedValue(int prefMacRandomized) {
|
||||
return (prefMacRandomized == PREF_RANDOMIZATION_PERSISTENT)
|
||||
? WifiConfiguration.RANDOMIZATION_PERSISTENT : WifiConfiguration.RANDOMIZATION_NONE;
|
||||
}
|
||||
|
||||
private void updateSummary(DropDownPreference preference, int macRandomized) {
|
||||
|
||||
@@ -84,9 +84,18 @@ 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_scan_24dp);
|
||||
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity();
|
||||
MenuItem menuItem;
|
||||
if (wifiNetworkConfig.isSupportConfiguratorQrCodeScanner(getActivity())) {
|
||||
menuItem = menu.add(0, Menu.FIRST, 0, R.string.next_label);
|
||||
menuItem.setIcon(R.drawable.ic_scan_24dp);
|
||||
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
} else {
|
||||
menuItem = menu.findItem(Menu.FIRST);
|
||||
if (menuItem != null) {
|
||||
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
|
||||
}
|
||||
}
|
||||
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
@@ -116,11 +125,7 @@ public class WifiDppQrCodeGeneratorFragment extends WifiDppQrCodeBaseFragment {
|
||||
mQrCodeView = view.findViewById(R.id.qrcode_view);
|
||||
|
||||
mHeaderIcon.setImageResource(R.drawable.ic_qrcode_24dp);
|
||||
WifiNetworkConfig wifiNetworkConfig = ((WifiNetworkConfig.Retriever) getActivity())
|
||||
.getWifiNetworkConfig();
|
||||
if (!WifiNetworkConfig.isValidConfig(wifiNetworkConfig)) {
|
||||
throw new IllegalStateException("Invalid Wi-Fi network for configuring");
|
||||
}
|
||||
final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity();
|
||||
mTitle.setText(R.string.wifi_dpp_share_wifi);
|
||||
mSummary.setText(getString(R.string.wifi_dpp_scan_qr_code_with_another_device,
|
||||
wifiNetworkConfig.getSsid()));
|
||||
@@ -139,4 +144,14 @@ public class WifiDppQrCodeGeneratorFragment extends WifiDppQrCodeBaseFragment {
|
||||
Log.e(TAG, "Error generatting QR code bitmap " + e);
|
||||
}
|
||||
}
|
||||
|
||||
WifiNetworkConfig getWifiNetworkConfigFromHostActivity() {
|
||||
final WifiNetworkConfig wifiNetworkConfig = ((WifiNetworkConfig.Retriever) getActivity())
|
||||
.getWifiNetworkConfig();
|
||||
if (!WifiNetworkConfig.isValidConfig(wifiNetworkConfig)) {
|
||||
throw new IllegalStateException("Invalid Wi-Fi network for configuring");
|
||||
}
|
||||
|
||||
return wifiNetworkConfig;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,26 +134,36 @@ public class WifiDppUtils {
|
||||
private static String getSecurityString(AccessPoint accessPoint) {
|
||||
switch(accessPoint.getSecurity()) {
|
||||
case AccessPoint.SECURITY_WEP:
|
||||
return "WEP";
|
||||
return WifiQrCode.SECURITY_WEP;
|
||||
case AccessPoint.SECURITY_PSK:
|
||||
return "WPA";
|
||||
return WifiQrCode.SECURITY_WPA;
|
||||
case AccessPoint.SECURITY_SAE:
|
||||
return WifiQrCode.SECURITY_SAE;
|
||||
default:
|
||||
return "nopass";
|
||||
return WifiQrCode.SECURITY_NO_PASSWORD;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an intent to launch QR code generator.
|
||||
* Returns an intent to launch QR code generator or scanner according to the Wi-Fi network
|
||||
* security. It may return null if the security is not supported by QR code generator nor
|
||||
* scanner.
|
||||
*
|
||||
* @param context The context to use for the content resolver
|
||||
* @param wifiManager An instance of {@link WifiManager}
|
||||
* @param accessPoint An instance of {@link AccessPoint}
|
||||
* @return Intent for launching QR code generator
|
||||
*/
|
||||
public static Intent getConfiguratorQrCodeGeneratorIntent(Context context,
|
||||
public static Intent getConfiguratorIntentOrNull(Context context,
|
||||
WifiManager wifiManager, AccessPoint accessPoint) {
|
||||
final Intent intent = new Intent(context, WifiDppConfiguratorActivity.class);
|
||||
intent.setAction(WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_GENERATOR);
|
||||
if (isSupportConfiguratorQrCodeGenerator(accessPoint)) {
|
||||
intent.setAction(WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_GENERATOR);
|
||||
} else if (isSupportConfiguratorQrCodeScanner(context, accessPoint)) {
|
||||
intent.setAction(WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_SCANNER);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
final WifiConfiguration wifiConfig = accessPoint.getConfig();
|
||||
final String ssid = removeFirstAndLastDoubleQuotes(wifiConfig.SSID);
|
||||
@@ -183,4 +193,43 @@ public class WifiDppUtils {
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Android Q supports Wi-Fi configurator by:
|
||||
*
|
||||
* 1. QR code generator of ZXing's Wi-Fi network config format.
|
||||
* and
|
||||
* 2. QR code scanner of Wi-Fi DPP QR code format.
|
||||
*/
|
||||
public static boolean isSuportConfigurator(Context context, AccessPoint accessPoint) {
|
||||
return isSupportConfiguratorQrCodeScanner(context, accessPoint) ||
|
||||
isSupportConfiguratorQrCodeGenerator(accessPoint);
|
||||
}
|
||||
|
||||
private static boolean isSupportConfiguratorQrCodeScanner(Context context,
|
||||
AccessPoint accessPoint) {
|
||||
if (!isWifiDppEnabled(context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// DPP 1.0 only supports SAE and PSK.
|
||||
final int security = accessPoint.getSecurity();
|
||||
if (security == AccessPoint.SECURITY_SAE || security == AccessPoint.SECURITY_PSK) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isSupportConfiguratorQrCodeGenerator(AccessPoint accessPoint) {
|
||||
// QR code generator produces QR code with ZXing's Wi-Fi network config format,
|
||||
// it supports PSK and WEP and non security
|
||||
final int security = accessPoint.getSecurity();
|
||||
if (security == AccessPoint.SECURITY_PSK || security == AccessPoint.SECURITY_WEP ||
|
||||
security == AccessPoint.SECURITY_NONE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.settings.wifi.dpp;
|
||||
|
||||
import static com.android.settings.wifi.dpp.WifiQrCode.SECURITY_NO_PASSWORD;
|
||||
import static com.android.settings.wifi.dpp.WifiQrCode.SECURITY_SAE;
|
||||
import static com.android.settings.wifi.dpp.WifiQrCode.SECURITY_WEP;
|
||||
import static com.android.settings.wifi.dpp.WifiQrCode.SECURITY_WPA;
|
||||
|
||||
@@ -208,6 +209,19 @@ public class WifiNetworkConfig {
|
||||
wifiManager.connect(wifiConfiguration, listener);
|
||||
}
|
||||
|
||||
public boolean isSupportConfiguratorQrCodeScanner(Context context) {
|
||||
if (!WifiDppUtils.isWifiDppEnabled(context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// DPP 1.0 only supports SAE and PSK.
|
||||
if (SECURITY_SAE.equals(mSecurity) || SECURITY_WPA.equals(mSecurity)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a simplified method from {@code WifiConfigController.getConfig()}
|
||||
*/
|
||||
|
||||
@@ -48,7 +48,6 @@ public class WifiNetworkListFragment extends SettingsPreferenceFragment implemen
|
||||
private static final String TAG = "WifiNetworkListFragment";
|
||||
|
||||
private static final String WIFI_CONFIG_KEY = "wifi_config_key";
|
||||
private static final String PREF_KEY_EMPTY_WIFI_LIST = "wifi_empty_list";
|
||||
private static final String PREF_KEY_ACCESS_POINTS = "access_points";
|
||||
|
||||
static final int ADD_NETWORK_REQUEST = 1;
|
||||
@@ -277,7 +276,6 @@ public class WifiNetworkListFragment extends SettingsPreferenceFragment implemen
|
||||
// AccessPoints are sorted by the WifiTracker
|
||||
final List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints();
|
||||
|
||||
boolean hasAvailableAccessPoints = false;
|
||||
mAccessPointsPreferenceCategory.setVisible(true);
|
||||
|
||||
cacheRemoveAllPrefs(mAccessPointsPreferenceCategory);
|
||||
@@ -299,7 +297,6 @@ public class WifiNetworkListFragment extends SettingsPreferenceFragment implemen
|
||||
return;
|
||||
}
|
||||
|
||||
hasAvailableAccessPoints = true;
|
||||
final AccessPointPreference pref = (AccessPointPreference) getCachedPreference(key);
|
||||
if (pref != null) {
|
||||
pref.setOrder(index);
|
||||
@@ -317,15 +314,6 @@ public class WifiNetworkListFragment extends SettingsPreferenceFragment implemen
|
||||
removeCachedPrefs(mAccessPointsPreferenceCategory);
|
||||
mAddPreference.setOrder(index);
|
||||
mAccessPointsPreferenceCategory.addPreference(mAddPreference);
|
||||
|
||||
if (!hasAvailableAccessPoints) {
|
||||
final Preference pref = new Preference(getPrefContext());
|
||||
pref.setSelectable(false);
|
||||
pref.setSummary(R.string.wifi_empty_list_wifi_on);
|
||||
pref.setOrder(index++);
|
||||
pref.setKey(PREF_KEY_EMPTY_WIFI_LIST);
|
||||
mAccessPointsPreferenceCategory.addPreference(pref);
|
||||
}
|
||||
}
|
||||
|
||||
private AccessPointPreference createAccessPointPreference(AccessPoint accessPoint) {
|
||||
|
||||
@@ -67,6 +67,7 @@ public class WifiQrCode {
|
||||
public static final String SECURITY_NO_PASSWORD = "nopass";
|
||||
public static final String SECURITY_WEP = "WEP";
|
||||
public static final String SECURITY_WPA = "WPA";
|
||||
public static final String SECURITY_SAE = "WPA3";
|
||||
|
||||
private String mQrCode;
|
||||
|
||||
|
||||
@@ -292,11 +292,12 @@ public class WifiSlice implements CustomSliceable {
|
||||
|
||||
@Override
|
||||
public void onWifiStateChanged(int state) {
|
||||
mContext.getContentResolver().notifyChange(getUri(), null);
|
||||
notifySliceChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectedChanged() {
|
||||
notifySliceChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.settingslib.RestrictedSwitchPreference;
|
||||
|
||||
@@ -45,6 +46,9 @@ import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.Shadows;
|
||||
import org.robolectric.shadows.ShadowDevicePolicyManager;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class CrossProfileCalendarPreferenceControllerTest {
|
||||
|
||||
@@ -123,7 +127,17 @@ public class CrossProfileCalendarPreferenceControllerTest {
|
||||
@Test
|
||||
public void updateState_somePackagesAllowed_preferenceShouldNotBeDisabled() throws Exception {
|
||||
dpm.setProfileOwner(TEST_COMPONENT_NAME);
|
||||
dpm.addCrossProfileCalendarPackage(TEST_COMPONENT_NAME, TEST_PACKAGE_NAME);
|
||||
dpm.setCrossProfileCalendarPackages(TEST_COMPONENT_NAME,
|
||||
Collections.singleton(TEST_PACKAGE_NAME));
|
||||
|
||||
mController.updateState(mPreference);
|
||||
verify(mPreference).setDisabledByAdmin(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_allPackagesAllowed_preferenceShouldNotBeDisabled() throws Exception {
|
||||
dpm.setProfileOwner(TEST_COMPONENT_NAME);
|
||||
dpm.setCrossProfileCalendarPackages(TEST_COMPONENT_NAME, null);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
verify(mPreference).setDisabledByAdmin(null);
|
||||
|
||||
@@ -38,6 +38,7 @@ import android.app.usage.UsageStatsManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ModuleInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.PowerManager;
|
||||
@@ -102,10 +103,13 @@ public class RecentAppsPreferenceControllerTest {
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
when(mContext.getApplicationContext()).thenReturn(mContext);
|
||||
ReflectionHelpers.setStaticField(ApplicationsState.class, "sInstance", mAppState);
|
||||
doReturn(mUsageStatsManager).when(mContext).getSystemService(Context.USAGE_STATS_SERVICE);
|
||||
doReturn(mUserManager).when(mContext).getSystemService(Context.USER_SERVICE);
|
||||
doReturn(mPackageManager).when(mContext).getPackageManager();
|
||||
doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
|
||||
when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[] {});
|
||||
|
||||
mController = new RecentAppsPreferenceController(mContext, mAppState, null);
|
||||
when(mScreen.findPreference(anyString())).thenReturn(mCategory);
|
||||
@@ -350,6 +354,57 @@ public class RecentAppsPreferenceControllerTest {
|
||||
verify(mCategory).addPreference(argThat(summaryMatches("0 minutes ago")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayPreference_shouldNotShowHiddenSystemModule() {
|
||||
final List<UsageStats> stats = new ArrayList<>();
|
||||
// Regular app.
|
||||
final UsageStats stat1 = new UsageStats();
|
||||
stat1.mLastTimeUsed = System.currentTimeMillis();
|
||||
stat1.mPackageName = "com.foo.bar";
|
||||
stats.add(stat1);
|
||||
|
||||
// Hidden system module.
|
||||
final UsageStats stat2 = new UsageStats();
|
||||
stat2.mLastTimeUsed = System.currentTimeMillis() + 200;
|
||||
stat2.mPackageName = "com.foo.hidden";
|
||||
stats.add(stat2);
|
||||
|
||||
ApplicationsState.AppEntry stat1Entry = mock(ApplicationsState.AppEntry.class);
|
||||
ApplicationsState.AppEntry stat2Entry = mock(ApplicationsState.AppEntry.class);
|
||||
stat1Entry.info = mApplicationInfo;
|
||||
stat2Entry.info = mApplicationInfo;
|
||||
|
||||
when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId())).thenReturn(stat1Entry);
|
||||
when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId())).thenReturn(stat2Entry);
|
||||
|
||||
final ModuleInfo moduleInfo1 = new ModuleInfo();
|
||||
moduleInfo1.setPackageName(stat1.mPackageName);
|
||||
moduleInfo1.setHidden(false);
|
||||
|
||||
final ModuleInfo moduleInfo2 = new ModuleInfo();
|
||||
moduleInfo2.setPackageName(stat2.mPackageName);
|
||||
moduleInfo2.setHidden(true);
|
||||
|
||||
ReflectionHelpers.setStaticField(ApplicationsState.class, "sInstance", null);
|
||||
final List<ModuleInfo> modules = new ArrayList<>();
|
||||
modules.add(moduleInfo2);
|
||||
when(mPackageManager.getInstalledModules(anyInt() /* flags */))
|
||||
.thenReturn(modules);
|
||||
|
||||
when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
|
||||
.thenReturn(new ResolveInfo());
|
||||
when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
|
||||
.thenReturn(stats);
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
// Only add stat1. stat2 is skipped because it is hidden module.
|
||||
final ArgumentCaptor<Preference> prefCaptor = ArgumentCaptor.forClass(Preference.class);
|
||||
verify(mCategory).addPreference(prefCaptor.capture());
|
||||
final Preference pref = prefCaptor.getValue();
|
||||
assertThat(pref.getKey()).isEqualTo(stat1.mPackageName);
|
||||
}
|
||||
|
||||
private static ArgumentMatcher<Preference> summaryMatches(String expected) {
|
||||
return preference -> TextUtils.equals(expected, preference.getSummary());
|
||||
}
|
||||
|
||||
@@ -20,11 +20,16 @@ import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEA
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ModuleInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
@@ -55,6 +60,8 @@ public class SpecialAppAccessPreferenceControllerTest {
|
||||
private ApplicationsState.Session mSession;
|
||||
@Mock
|
||||
private PreferenceScreen mScreen;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
private SpecialAppAccessPreferenceController mController;
|
||||
private Preference mPreference;
|
||||
@@ -62,8 +69,11 @@ public class SpecialAppAccessPreferenceControllerTest {
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
when(mContext.getApplicationContext()).thenReturn(mContext);
|
||||
ShadowUserManager.getShadow().setProfileIdsWithDisabled(new int[]{0});
|
||||
doReturn(mPackageManager).when(mContext).getPackageManager();
|
||||
doReturn(new ArrayList<ModuleInfo>()).when(mPackageManager).getInstalledModules(anyInt());
|
||||
mController = new SpecialAppAccessPreferenceController(mContext, "test_key");
|
||||
mPreference = new Preference(mContext);
|
||||
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user