Snap for 9253587 from 2552ae08ae to tm-qpr2-release
Change-Id: I27f90314524426eb601ce8676c5db14751951f13
This commit is contained in:
@@ -14,9 +14,11 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||||
|
android:insetTop="6dp"
|
||||||
|
android:insetBottom="6dp">
|
||||||
|
<shape
|
||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
<stroke
|
<stroke
|
||||||
android:color="?androidprv:attr/colorAccentPrimaryVariant"
|
android:color="?androidprv:attr/colorAccentPrimaryVariant"
|
||||||
@@ -29,3 +31,4 @@
|
|||||||
android:bottom="8dp" />
|
android:bottom="8dp" />
|
||||||
<solid android:color="@android:color/transparent" />
|
<solid android:color="@android:color/transparent" />
|
||||||
</shape>
|
</shape>
|
||||||
|
</inset>
|
||||||
@@ -15,9 +15,11 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||||
android:shape="rectangle">
|
android:insetTop="6dp"
|
||||||
|
android:insetBottom="6dp">
|
||||||
|
<shape android:shape="rectangle">
|
||||||
<stroke
|
<stroke
|
||||||
android:color="@android:color/transparent"
|
android:color="@android:color/transparent"
|
||||||
android:width="1dp"/>
|
android:width="1dp"/>
|
||||||
@@ -29,3 +31,4 @@
|
|||||||
android:bottom="8dp" />
|
android:bottom="8dp" />
|
||||||
<solid android:color="?androidprv:attr/colorAccentPrimary" />
|
<solid android:color="?androidprv:attr/colorAccentPrimary" />
|
||||||
</shape>
|
</shape>
|
||||||
|
</inset>
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
android:id="@+id/see_more"
|
android:id="@+id/see_more"
|
||||||
style="@style/PanelOptionRoundedOutlinedButton"
|
style="@style/PanelOptionRoundedOutlinedButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="36dp"
|
android:layout_height="wrap_content"
|
||||||
android:minWidth="0dp"
|
android:minWidth="0dp"
|
||||||
android:text="@string/settings_button"/>
|
android:text="@string/settings_button"/>
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
android:id="@+id/done"
|
android:id="@+id/done"
|
||||||
style="@style/PanelOptionRoundedSolidButton"
|
style="@style/PanelOptionRoundedSolidButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="36dp"
|
android:layout_height="wrap_content"
|
||||||
android:minWidth="0dp"
|
android:minWidth="0dp"
|
||||||
android:text="@string/done"/>
|
android:text="@string/done"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -55,13 +55,17 @@
|
|||||||
android:src="@drawable/sfps_enroll_finish" />
|
android:src="@drawable/sfps_enroll_finish" />
|
||||||
</com.google.android.setupdesign.view.FillContentLayout>
|
</com.google.android.setupdesign.view.FillContentLayout>
|
||||||
|
|
||||||
<!-- Added to align elements with fingerprint_enroll_enrolling_base -->
|
<Space
|
||||||
<TextView
|
android:layout_width="0dp"
|
||||||
style="@style/TextAppearance.ErrorText"
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
<com.android.settings.biometrics.fingerprint.FingerprintRequireScreenOnToAuthToggle
|
||||||
|
style="@style/SudSwitchStyle"
|
||||||
|
android:id="@+id/require_screen_on_to_auth_toggle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_horizontal|bottom"
|
android:layout_gravity="center_horizontal|bottom" />
|
||||||
android:visibility="invisible" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
79
res/layout/sfps_require_screen_on_to_auth_toggle.xml
Normal file
79
res/layout/sfps_require_screen_on_to_auth_toggle.xml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2022 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="?attr/fingerprint_layout_theme">
|
||||||
|
|
||||||
|
<!-- Top divider -->
|
||||||
|
<View
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?android:attr/listDivider" />
|
||||||
|
|
||||||
|
<!-- Title -->
|
||||||
|
<com.google.android.setupdesign.view.RichTextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
android:gravity="start"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_toStartOf="@+id/toggle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/security_settings_require_screen_on_to_auth_title"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textSize="@dimen/sud_description_text_size"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Subtitle -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/subtitle"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_toStartOf="@+id/toggle"
|
||||||
|
android:layout_below="@+id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/security_settings_require_screen_on_to_auth_description"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"/>
|
||||||
|
|
||||||
|
<!-- Vertical divider -->
|
||||||
|
<View
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_alignTop="@+id/toggle"
|
||||||
|
android:layout_alignBottom="@+id/toggle"
|
||||||
|
android:layout_toStartOf="@+id/toggle"
|
||||||
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/listDivider" />
|
||||||
|
|
||||||
|
<!-- Toggle -->
|
||||||
|
<Switch
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/toggle"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:checked="false"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
@@ -1012,6 +1012,8 @@
|
|||||||
<!-- Note: Update FingerprintEnrollParentalConsent.CONSENT_STRING_RESOURCES when any _consent_ strings are added or removed. -->
|
<!-- Note: Update FingerprintEnrollParentalConsent.CONSENT_STRING_RESOURCES when any _consent_ strings are added or removed. -->
|
||||||
<!-- Title shown for menu item that launches fingerprint settings or enrollment [CHAR LIMIT=22] -->
|
<!-- Title shown for menu item that launches fingerprint settings or enrollment [CHAR LIMIT=22] -->
|
||||||
<string name="security_settings_fingerprint_preference_title">Fingerprint</string>
|
<string name="security_settings_fingerprint_preference_title">Fingerprint</string>
|
||||||
|
<!-- Title for a category shown for the fingerprint settings page, followed by items that the user can toggle on/off to require/disable. [CHAR LIMIT=50] -->
|
||||||
|
<string name="security_settings_fingerprint_settings_preferences_category">When using Fingerprint Unlock</string>
|
||||||
<!-- Fingerprint enrollment and settings --><skip />
|
<!-- Fingerprint enrollment and settings --><skip />
|
||||||
<!-- Title shown for work menu item that launches fingerprint settings or enrollment [CHAR LIMIT=22] -->
|
<!-- Title shown for work menu item that launches fingerprint settings or enrollment [CHAR LIMIT=22] -->
|
||||||
<string name="security_settings_work_fingerprint_preference_title">Fingerprint for work</string>
|
<string name="security_settings_work_fingerprint_preference_title">Fingerprint for work</string>
|
||||||
@@ -1293,6 +1295,12 @@
|
|||||||
<string name="security_settings_fingerprint_enroll_finish_v2_message" product="device">Now you can use your fingerprint to unlock your device or verify it\u2019s you, like when you sign in to apps</string>
|
<string name="security_settings_fingerprint_enroll_finish_v2_message" product="device">Now you can use your fingerprint to unlock your device or verify it\u2019s you, like when you sign in to apps</string>
|
||||||
<!-- Message shown in fingerprint enrollment dialog once enrollment is completed (default) [CHAR LIMIT=NONE] -->
|
<!-- Message shown in fingerprint enrollment dialog once enrollment is completed (default) [CHAR LIMIT=NONE] -->
|
||||||
<string name="security_settings_fingerprint_enroll_finish_v2_message" product="default">Now you can use your fingerprint to unlock your phone or verify it\u2019s you, like when you sign in to apps</string>
|
<string name="security_settings_fingerprint_enroll_finish_v2_message" product="default">Now you can use your fingerprint to unlock your phone or verify it\u2019s you, like when you sign in to apps</string>
|
||||||
|
<!-- Title for require screen on to auth toggle shown in fingerprint enrollment dialog once enrollment is completed. [CHAR LIMIT=NONE] -->
|
||||||
|
<string name="security_settings_require_screen_on_to_auth_title">Unlock only when screen is on</string>
|
||||||
|
<!-- Description for require screen on to auth toggle shown in fingerprint enrollment dialog once enrollment is completed. [CHAR LIMIT=NONE] -->
|
||||||
|
<string name="security_settings_require_screen_on_to_auth_description">The screen must be on before you can unlock with your fingerprint. This makes accidental unlocking less likely.</string>
|
||||||
|
<!-- Description for require screen on to auth toggle shown in fingerprint enrollment dialog once enrollment is completed. [CHAR LIMIT=NONE] -->
|
||||||
|
<string name="security_settings_require_screen_on_to_auth_keywords">Screen, Unlock</string>
|
||||||
<!-- Button text to skip enrollment of fingerprint [CHAR LIMIT=40] -->
|
<!-- Button text to skip enrollment of fingerprint [CHAR LIMIT=40] -->
|
||||||
<string name="security_settings_fingerprint_enroll_enrolling_skip">Do it later</string>
|
<string name="security_settings_fingerprint_enroll_enrolling_skip">Do it later</string>
|
||||||
<!-- Accessibility message for fingerprint enrollment asking the user to place the tip of their finger on the fingerprint sensor [CHAR LIMIT=NONE] -->
|
<!-- Accessibility message for fingerprint enrollment asking the user to place the tip of their finger on the fingerprint sensor [CHAR LIMIT=NONE] -->
|
||||||
|
|||||||
@@ -16,5 +16,20 @@
|
|||||||
|
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:title="@string/security_settings_fingerprint_preference_title"/>
|
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:title="@string/security_settings_fingerprint_preference_title">
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="security_settings_fingerprint_unlock_category"
|
||||||
|
android:title="@string/security_settings_fingerprint_settings_preferences_category"
|
||||||
|
settings:controller="com.android.settings.biometrics.fingerprint.FingerprintUnlockCategoryPreferenceController">
|
||||||
|
|
||||||
|
<com.android.settingslib.RestrictedSwitchPreference
|
||||||
|
android:key="security_settings_require_screen_on_to_auth"
|
||||||
|
android:title="@string/security_settings_require_screen_on_to_auth_title"
|
||||||
|
android:summary="@string/security_settings_require_screen_on_to_auth_description"
|
||||||
|
settings:keywords="@string/security_settings_require_screen_on_to_auth_keywords"
|
||||||
|
settings:controller="com.android.settings.biometrics.fingerprint.FingerprintSettingsRequireScreenOnToAuthPreferenceController" />
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.CompoundButton;
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
@@ -44,16 +45,24 @@ import java.util.List;
|
|||||||
public class FingerprintEnrollFinish extends BiometricEnrollBase {
|
public class FingerprintEnrollFinish extends BiometricEnrollBase {
|
||||||
|
|
||||||
private static final String TAG = "FingerprintEnrollFinish";
|
private static final String TAG = "FingerprintEnrollFinish";
|
||||||
|
private static final String KEY_REQUIRE_SCREEN_ON_TO_AUTH = "require_screen_on_to_auth_toggle";
|
||||||
private static final String ACTION_FINGERPRINT_SETTINGS =
|
private static final String ACTION_FINGERPRINT_SETTINGS =
|
||||||
"android.settings.FINGERPRINT_SETTINGS";
|
"android.settings.FINGERPRINT_SETTINGS";
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final String FINGERPRINT_SUGGESTION_ACTIVITY =
|
static final String FINGERPRINT_SUGGESTION_ACTIVITY =
|
||||||
"com.android.settings.SetupFingerprintSuggestionActivity";
|
"com.android.settings.SetupFingerprintSuggestionActivity";
|
||||||
|
|
||||||
private FingerprintManager mFingerprintManager;
|
private FingerprintManager mFingerprintManager;
|
||||||
|
|
||||||
|
private FingerprintSettingsRequireScreenOnToAuthPreferenceController
|
||||||
|
mRequireScreenOnToAuthPreferenceController;
|
||||||
|
private FingerprintRequireScreenOnToAuthToggle mRequireScreenOnToAuthToggle;
|
||||||
private boolean mCanAssumeSfps;
|
private boolean mCanAssumeSfps;
|
||||||
|
|
||||||
private boolean mIsAddAnotherOrFinish;
|
private boolean mIsAddAnotherOrFinish;
|
||||||
|
|
||||||
|
private CompoundButton.OnCheckedChangeListener mRequireScreenOnToAuthToggleListener;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -63,6 +72,11 @@ public class FingerprintEnrollFinish extends BiometricEnrollBase {
|
|||||||
mCanAssumeSfps = props != null && props.size() == 1 && props.get(0).isAnySidefpsType();
|
mCanAssumeSfps = props != null && props.size() == 1 && props.get(0).isAnySidefpsType();
|
||||||
if (mCanAssumeSfps) {
|
if (mCanAssumeSfps) {
|
||||||
setContentView(R.layout.sfps_enroll_finish);
|
setContentView(R.layout.sfps_enroll_finish);
|
||||||
|
mRequireScreenOnToAuthPreferenceController =
|
||||||
|
new FingerprintSettingsRequireScreenOnToAuthPreferenceController(
|
||||||
|
getApplicationContext(),
|
||||||
|
KEY_REQUIRE_SCREEN_ON_TO_AUTH
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
setContentView(R.layout.fingerprint_enroll_finish);
|
setContentView(R.layout.fingerprint_enroll_finish);
|
||||||
}
|
}
|
||||||
@@ -90,6 +104,20 @@ public class FingerprintEnrollFinish extends BiometricEnrollBase {
|
|||||||
.setTheme(R.style.SudGlifButton_Primary)
|
.setTheme(R.style.SudGlifButton_Primary)
|
||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (mCanAssumeSfps) {
|
||||||
|
mRequireScreenOnToAuthToggleListener =
|
||||||
|
(buttonView, isChecked) -> {
|
||||||
|
mRequireScreenOnToAuthPreferenceController.setChecked(isChecked);
|
||||||
|
};
|
||||||
|
mRequireScreenOnToAuthToggle = findViewById(R.id.require_screen_on_to_auth_toggle);
|
||||||
|
mRequireScreenOnToAuthToggle.setChecked(
|
||||||
|
mRequireScreenOnToAuthPreferenceController.isChecked());
|
||||||
|
mRequireScreenOnToAuthToggle.setListener(mRequireScreenOnToAuthToggleListener);
|
||||||
|
mRequireScreenOnToAuthToggle.setOnClickListener(v -> {
|
||||||
|
mRequireScreenOnToAuthToggle.getSwitch().toggle();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -103,6 +131,12 @@ public class FingerprintEnrollFinish extends BiometricEnrollBase {
|
|||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
if (mCanAssumeSfps) {
|
||||||
|
mRequireScreenOnToAuthToggleListener.onCheckedChanged(
|
||||||
|
mRequireScreenOnToAuthToggle.getSwitch(),
|
||||||
|
mRequireScreenOnToAuthToggle.isChecked()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
FooterButton addButton = mFooterBarMixin.getSecondaryButton();
|
FooterButton addButton = mFooterBarMixin.getSecondaryButton();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.biometrics.fingerprint;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.widget.CompoundButton;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.Switch;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A layout that contains a start-justified title, and an end-justified switch.
|
||||||
|
*/
|
||||||
|
public class FingerprintRequireScreenOnToAuthToggle extends LinearLayout {
|
||||||
|
private Switch mSwitch;
|
||||||
|
|
||||||
|
public FingerprintRequireScreenOnToAuthToggle(Context context) {
|
||||||
|
this(context, null /* attrs */);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FingerprintRequireScreenOnToAuthToggle(Context context, AttributeSet attrs) {
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FingerprintRequireScreenOnToAuthToggle(
|
||||||
|
Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
|
||||||
|
LayoutInflater.from(context).inflate(R.layout.sfps_require_screen_on_to_auth_toggle,
|
||||||
|
this, true /* attachToRoot */);
|
||||||
|
|
||||||
|
mSwitch = findViewById(R.id.toggle);
|
||||||
|
mSwitch.setClickable(true);
|
||||||
|
mSwitch.setFocusable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isChecked() {
|
||||||
|
return mSwitch.isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param checked
|
||||||
|
*/
|
||||||
|
public void setChecked(boolean checked) {
|
||||||
|
mSwitch.setChecked(checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
public void setListener(CompoundButton.OnCheckedChangeListener listener) {
|
||||||
|
mSwitch.setOnCheckedChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Switch getSwitch() {
|
||||||
|
return mSwitch;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,21 +56,24 @@ import androidx.preference.Preference.OnPreferenceChangeListener;
|
|||||||
import androidx.preference.PreferenceGroup;
|
import androidx.preference.PreferenceGroup;
|
||||||
import androidx.preference.PreferenceScreen;
|
import androidx.preference.PreferenceScreen;
|
||||||
import androidx.preference.PreferenceViewHolder;
|
import androidx.preference.PreferenceViewHolder;
|
||||||
|
import androidx.preference.SwitchPreference;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.SettingsPreferenceFragment;
|
|
||||||
import com.android.settings.SubSettings;
|
import com.android.settings.SubSettings;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settings.biometrics.BiometricEnrollBase;
|
import com.android.settings.biometrics.BiometricEnrollBase;
|
||||||
import com.android.settings.biometrics.BiometricUtils;
|
import com.android.settings.biometrics.BiometricUtils;
|
||||||
import com.android.settings.core.SettingsBaseActivity;
|
import com.android.settings.core.SettingsBaseActivity;
|
||||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||||
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
import com.android.settings.password.ChooseLockGeneric;
|
import com.android.settings.password.ChooseLockGeneric;
|
||||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||||
import com.android.settingslib.HelpUtils;
|
import com.android.settingslib.HelpUtils;
|
||||||
import com.android.settingslib.RestrictedLockUtils;
|
import com.android.settingslib.RestrictedLockUtils;
|
||||||
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||||
|
import com.android.settingslib.RestrictedSwitchPreference;
|
||||||
|
import com.android.settingslib.core.AbstractPreferenceController;
|
||||||
import com.android.settingslib.transition.SettingsTransitionHelper;
|
import com.android.settingslib.transition.SettingsTransitionHelper;
|
||||||
import com.android.settingslib.widget.FooterPreference;
|
import com.android.settingslib.widget.FooterPreference;
|
||||||
import com.android.settingslib.widget.TwoTargetPreference;
|
import com.android.settingslib.widget.TwoTargetPreference;
|
||||||
@@ -115,7 +118,26 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
setTitle(msg);
|
setTitle(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class FingerprintSettingsFragment extends SettingsPreferenceFragment
|
/**
|
||||||
|
* @param context
|
||||||
|
* @return true if the Fingerprint hardware is detected.
|
||||||
|
*/
|
||||||
|
public static boolean isFingerprintHardwareDetected(Context context) {
|
||||||
|
FingerprintManager manager = Utils.getFingerprintManagerOrNull(context);
|
||||||
|
boolean isHardwareDetected = false;
|
||||||
|
if (manager == null) {
|
||||||
|
Log.d(TAG, "FingerprintManager is null");
|
||||||
|
} else {
|
||||||
|
isHardwareDetected = manager.isHardwareDetected();
|
||||||
|
Log.d(TAG, "FingerprintManager is not null. Hardware detected: " + isHardwareDetected);
|
||||||
|
}
|
||||||
|
return manager != null && isHardwareDetected;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static class FingerprintSettingsFragment extends DashboardFragment
|
||||||
implements OnPreferenceChangeListener, FingerprintPreference.OnDeleteClickListener {
|
implements OnPreferenceChangeListener, FingerprintPreference.OnDeleteClickListener {
|
||||||
|
|
||||||
private static class FooterColumn {
|
private static class FooterColumn {
|
||||||
@@ -134,6 +156,8 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
private static final String KEY_LAUNCHED_CONFIRM = "launched_confirm";
|
private static final String KEY_LAUNCHED_CONFIRM = "launched_confirm";
|
||||||
private static final String KEY_HAS_FIRST_ENROLLED = "has_first_enrolled";
|
private static final String KEY_HAS_FIRST_ENROLLED = "has_first_enrolled";
|
||||||
private static final String KEY_IS_ENROLLING = "is_enrolled";
|
private static final String KEY_IS_ENROLLING = "is_enrolled";
|
||||||
|
private static final String KEY_REQUIRE_SCREEN_ON_TO_AUTH =
|
||||||
|
"security_settings_require_screen_on_to_auth";
|
||||||
|
|
||||||
private static final int MSG_REFRESH_FINGERPRINT_TEMPLATES = 1000;
|
private static final int MSG_REFRESH_FINGERPRINT_TEMPLATES = 1000;
|
||||||
private static final int MSG_FINGER_AUTH_SUCCESS = 1001;
|
private static final int MSG_FINGER_AUTH_SUCCESS = 1001;
|
||||||
@@ -149,6 +173,11 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
|
|
||||||
protected static final boolean DEBUG = false;
|
protected static final boolean DEBUG = false;
|
||||||
|
|
||||||
|
private List<AbstractPreferenceController> mControllers;
|
||||||
|
private FingerprintSettingsRequireScreenOnToAuthPreferenceController
|
||||||
|
mRequireScreenOnToAuthPreferenceController;
|
||||||
|
private RestrictedSwitchPreference mRequireScreenOnToAuthPreference;
|
||||||
|
|
||||||
private FingerprintManager mFingerprintManager;
|
private FingerprintManager mFingerprintManager;
|
||||||
private FingerprintUpdater mFingerprintUpdater;
|
private FingerprintUpdater mFingerprintUpdater;
|
||||||
private List<FingerprintSensorPropertiesInternal> mSensorProperties;
|
private List<FingerprintSensorPropertiesInternal> mSensorProperties;
|
||||||
@@ -214,6 +243,7 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateDialog() {
|
private void updateDialog() {
|
||||||
|
setRequireScreenOnToAuthVisibility();
|
||||||
RenameDialog renameDialog = (RenameDialog) getFragmentManager().
|
RenameDialog renameDialog = (RenameDialog) getFragmentManager().
|
||||||
findFragmentByTag(RenameDialog.class.getName());
|
findFragmentByTag(RenameDialog.class.getName());
|
||||||
if (renameDialog != null) {
|
if (renameDialog != null) {
|
||||||
@@ -448,13 +478,36 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
if (root != null) {
|
if (root != null) {
|
||||||
root.removeAll();
|
root.removeAll();
|
||||||
}
|
}
|
||||||
addPreferencesFromResource(R.xml.security_settings_fingerprint);
|
|
||||||
root = getPreferenceScreen();
|
root = getPreferenceScreen();
|
||||||
addFingerprintItemPreferences(root);
|
addFingerprintItemPreferences(root);
|
||||||
|
addPreferencesFromResource(getPreferenceScreenResId());
|
||||||
|
mRequireScreenOnToAuthPreference = findPreference(KEY_REQUIRE_SCREEN_ON_TO_AUTH);
|
||||||
|
for (AbstractPreferenceController controller : mControllers) {
|
||||||
|
((FingerprintSettingsPreferenceController) controller).setUserId(mUserId);
|
||||||
|
}
|
||||||
|
mRequireScreenOnToAuthPreference.setChecked(
|
||||||
|
mRequireScreenOnToAuthPreferenceController.isChecked());
|
||||||
|
mRequireScreenOnToAuthPreference.setOnPreferenceChangeListener(
|
||||||
|
(preference, newValue) -> {
|
||||||
|
boolean isChecked = ((SwitchPreference) preference).isChecked();
|
||||||
|
mRequireScreenOnToAuthPreferenceController.setChecked(!isChecked);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
setPreferenceScreen(root);
|
setPreferenceScreen(root);
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setRequireScreenOnToAuthVisibility() {
|
||||||
|
int fingerprintsEnrolled = mFingerprintManager.getEnrolledFingerprints(mUserId).size();
|
||||||
|
final boolean removalInProgress = mRemovalSidecar.inProgress();
|
||||||
|
// Removing last remaining fingerprint
|
||||||
|
if (fingerprintsEnrolled == 0 && removalInProgress) {
|
||||||
|
mRequireScreenOnToAuthPreference.setVisible(false);
|
||||||
|
} else {
|
||||||
|
mRequireScreenOnToAuthPreference.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void addFingerprintItemPreferences(PreferenceGroup root) {
|
private void addFingerprintItemPreferences(PreferenceGroup root) {
|
||||||
root.removeAll();
|
root.removeAll();
|
||||||
final List<Fingerprint> items = mFingerprintManager.getEnrolledFingerprints(mUserId);
|
final List<Fingerprint> items = mFingerprintManager.getEnrolledFingerprints(mUserId);
|
||||||
@@ -477,6 +530,7 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
root.addPreference(pref);
|
root.addPreference(pref);
|
||||||
pref.setOnPreferenceChangeListener(this);
|
pref.setOnPreferenceChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Preference addPreference = new Preference(root.getContext());
|
Preference addPreference = new Preference(root.getContext());
|
||||||
addPreference.setKey(KEY_FINGERPRINT_ADD);
|
addPreference.setKey(KEY_FINGERPRINT_ADD);
|
||||||
addPreference.setTitle(R.string.fingerprint_add_title);
|
addPreference.setTitle(R.string.fingerprint_add_title);
|
||||||
@@ -568,6 +622,16 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getPreferenceScreenResId() {
|
||||||
|
return R.xml.security_settings_fingerprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getLogTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(final Bundle outState) {
|
public void onSaveInstanceState(final Bundle outState) {
|
||||||
outState.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
|
outState.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
|
||||||
@@ -662,6 +726,27 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
return R.string.help_url_fingerprint;
|
return R.string.help_url_fingerprint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
||||||
|
if (!isFingerprintHardwareDetected(context)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
mControllers = buildPreferenceControllers(context);
|
||||||
|
return mControllers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
|
||||||
|
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||||
|
mRequireScreenOnToAuthPreferenceController =
|
||||||
|
new FingerprintSettingsRequireScreenOnToAuthPreferenceController(
|
||||||
|
context,
|
||||||
|
KEY_REQUIRE_SCREEN_ON_TO_AUTH
|
||||||
|
);
|
||||||
|
controllers.add(mRequireScreenOnToAuthPreferenceController);
|
||||||
|
return controllers;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.biometrics.fingerprint;
|
||||||
|
|
||||||
|
import android.app.admin.DevicePolicyManager;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.android.settings.core.TogglePreferenceController;
|
||||||
|
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||||
|
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class for all fingerprint settings toggles.
|
||||||
|
*/
|
||||||
|
public abstract class FingerprintSettingsPreferenceController extends TogglePreferenceController {
|
||||||
|
|
||||||
|
private int mUserId;
|
||||||
|
|
||||||
|
public FingerprintSettingsPreferenceController(Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(int userId) {
|
||||||
|
mUserId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getUserId() {
|
||||||
|
return mUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected EnforcedAdmin getRestrictingAdmin() {
|
||||||
|
return RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
|
||||||
|
mContext, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT, mUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isSliceable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSliceHighlightMenuRes() {
|
||||||
|
// not needed since it's not sliceable
|
||||||
|
return NO_RES;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.biometrics.fingerprint;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.hardware.fingerprint.FingerprintManager;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.settings.Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preference controller that controls whether a SFPS device is required to be interactive for
|
||||||
|
* fingerprint authentication to unlock the device.
|
||||||
|
*/
|
||||||
|
public class FingerprintSettingsRequireScreenOnToAuthPreferenceController
|
||||||
|
extends FingerprintSettingsPreferenceController {
|
||||||
|
private static final String TAG =
|
||||||
|
"FingerprintSettingsRequireScreenOnToAuthPreferenceController";
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
protected FingerprintManager mFingerprintManager;
|
||||||
|
|
||||||
|
public FingerprintSettingsRequireScreenOnToAuthPreferenceController(
|
||||||
|
Context context, String prefKey) {
|
||||||
|
super(context, prefKey);
|
||||||
|
mFingerprintManager = Utils.getFingerprintManagerOrNull(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChecked() {
|
||||||
|
if (!FingerprintSettings.isFingerprintHardwareDetected(mContext)) {
|
||||||
|
return false;
|
||||||
|
} else if (getRestrictingAdmin() != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int defaultValue = mContext.getResources().getBoolean(
|
||||||
|
com.android.internal.R.bool.config_requireScreenOnToAuthEnabled) ? 1 : 0;
|
||||||
|
|
||||||
|
return Settings.Secure.getIntForUser(
|
||||||
|
mContext.getContentResolver(),
|
||||||
|
Settings.Secure.SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED,
|
||||||
|
defaultValue,
|
||||||
|
getUserHandle()) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setChecked(boolean isChecked) {
|
||||||
|
Settings.Secure.putIntForUser(
|
||||||
|
mContext.getContentResolver(),
|
||||||
|
Settings.Secure.SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED,
|
||||||
|
isChecked ? 1 : 0,
|
||||||
|
getUserHandle());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateState(Preference preference) {
|
||||||
|
super.updateState(preference);
|
||||||
|
if (!FingerprintSettings.isFingerprintHardwareDetected(mContext)) {
|
||||||
|
preference.setEnabled(false);
|
||||||
|
} else if (!mFingerprintManager.hasEnrolledTemplates(getUserId())) {
|
||||||
|
preference.setEnabled(false);
|
||||||
|
} else {
|
||||||
|
preference.setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAvailabilityStatus() {
|
||||||
|
if (mFingerprintManager != null
|
||||||
|
&& mFingerprintManager.isHardwareDetected()
|
||||||
|
&& mFingerprintManager.isPowerbuttonFps()) {
|
||||||
|
return mFingerprintManager.hasEnrolledTemplates(getUserId())
|
||||||
|
? AVAILABLE : DISABLED_DEPENDENT_SETTING;
|
||||||
|
} else {
|
||||||
|
return UNSUPPORTED_ON_DEVICE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getUserHandle() {
|
||||||
|
return UserHandle.of(getUserId()).getIdentifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -24,7 +24,6 @@ import android.bluetooth.BluetoothDevice;
|
|||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -126,15 +125,8 @@ public final class BluetoothPermissionRequest extends BroadcastReceiver {
|
|||||||
// Create an intent triggered by clicking on the
|
// Create an intent triggered by clicking on the
|
||||||
// "Clear All Notifications" button
|
// "Clear All Notifications" button
|
||||||
|
|
||||||
String bluetoothName;
|
|
||||||
try {
|
|
||||||
bluetoothName = Utils.findBluetoothPackageName(context);
|
|
||||||
} catch (NameNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Intent deleteIntent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
|
Intent deleteIntent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
|
||||||
deleteIntent.setPackage(bluetoothName);
|
deleteIntent.setPackage("com.android.bluetooth");
|
||||||
deleteIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
|
deleteIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
|
||||||
deleteIntent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
|
deleteIntent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
|
||||||
BluetoothDevice.CONNECTION_ACCESS_NO);
|
BluetoothDevice.CONNECTION_ACCESS_NO);
|
||||||
|
|||||||
51
src/com/android/settings/bluetooth/Utils.java
Normal file → Executable file
51
src/com/android/settings/bluetooth/Utils.java
Normal file → Executable file
@@ -16,18 +16,11 @@
|
|||||||
|
|
||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import static android.os.Process.BLUETOOTH_UID;
|
|
||||||
|
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothProfile;
|
import android.bluetooth.BluetoothProfile;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.pm.ActivityInfo;
|
|
||||||
import android.content.pm.PackageInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -196,48 +189,4 @@ public final class Utils {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the Bluetooth Package name
|
|
||||||
*/
|
|
||||||
public static String findBluetoothPackageName(Context context)
|
|
||||||
throws NameNotFoundException {
|
|
||||||
// this activity will always be in the package where the rest of Bluetooth lives
|
|
||||||
String sentinelActivity = "com.android.bluetooth.opp.BluetoothOppLauncherActivity";
|
|
||||||
PackageManager packageManager = context.createContextAsUser(UserHandle.SYSTEM, 0)
|
|
||||||
.getPackageManager();
|
|
||||||
String[] allPackages = packageManager.getPackagesForUid(BLUETOOTH_UID);
|
|
||||||
String matchedPackage = null;
|
|
||||||
for (String candidatePackage : allPackages) {
|
|
||||||
PackageInfo packageInfo;
|
|
||||||
try {
|
|
||||||
packageInfo =
|
|
||||||
packageManager.getPackageInfo(
|
|
||||||
candidatePackage,
|
|
||||||
PackageManager.GET_ACTIVITIES
|
|
||||||
| PackageManager.MATCH_ANY_USER
|
|
||||||
| PackageManager.MATCH_UNINSTALLED_PACKAGES
|
|
||||||
| PackageManager.MATCH_DISABLED_COMPONENTS);
|
|
||||||
} catch (NameNotFoundException e) {
|
|
||||||
// rethrow
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
if (packageInfo.activities == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (ActivityInfo activity : packageInfo.activities) {
|
|
||||||
if (sentinelActivity.equals(activity.name)) {
|
|
||||||
if (matchedPackage == null) {
|
|
||||||
matchedPackage = candidatePackage;
|
|
||||||
} else {
|
|
||||||
throw new NameNotFoundException("multiple main bluetooth packages found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (matchedPackage != null) {
|
|
||||||
return matchedPackage;
|
|
||||||
}
|
|
||||||
throw new NameNotFoundException("Could not find main bluetooth package");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,9 @@
|
|||||||
|
|
||||||
package com.android.settings.development;
|
package com.android.settings.development;
|
||||||
|
|
||||||
import android.bluetooth.BluetoothManager;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
@@ -41,15 +42,18 @@ public class BluetoothMaxConnectedAudioDevicesPreferenceController extends
|
|||||||
|
|
||||||
private int mDefaultMaxConnectedAudioDevices = 0;
|
private int mDefaultMaxConnectedAudioDevices = 0;
|
||||||
|
|
||||||
private final BluetoothManager mBluetoothManager;
|
|
||||||
|
|
||||||
public BluetoothMaxConnectedAudioDevicesPreferenceController(Context context) {
|
public BluetoothMaxConnectedAudioDevicesPreferenceController(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
mBluetoothManager = context.getSystemService(BluetoothManager.class);
|
try {
|
||||||
|
Resources res = context.getPackageManager().getResourcesForApplication(
|
||||||
mDefaultMaxConnectedAudioDevices =
|
"com.android.bluetooth");
|
||||||
mBluetoothManager.getAdapter().getMaxConnectedAudioDevices();
|
mDefaultMaxConnectedAudioDevices = res.getInteger(res.getIdentifier(
|
||||||
|
"config_bluetooth_max_connected_audio_devices",
|
||||||
|
"integer", "com.android.bluetooth"));
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.settings.notification;
|
package com.android.settings.notification;
|
||||||
|
|
||||||
|
import android.app.ActivityThread;
|
||||||
import android.app.INotificationManager;
|
import android.app.INotificationManager;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
@@ -29,26 +30,32 @@ import android.os.Looper;
|
|||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.os.Vibrator;
|
import android.os.Vibrator;
|
||||||
|
import android.provider.DeviceConfig;
|
||||||
import android.service.notification.NotificationListenerService;
|
import android.service.notification.NotificationListenerService;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.lifecycle.OnLifecycleEvent;
|
import androidx.lifecycle.OnLifecycleEvent;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update notification volume icon in Settings in response to user adjusting volume
|
* Update notification volume icon in Settings in response to user adjusting volume.
|
||||||
*/
|
*/
|
||||||
public class NotificationVolumePreferenceController extends VolumeSeekBarPreferenceController {
|
public class NotificationVolumePreferenceController extends VolumeSeekBarPreferenceController {
|
||||||
|
|
||||||
private static final String TAG = "NotificationVolumePreferenceController";
|
private static final String TAG = "NotificationVolumePreferenceController";
|
||||||
private static final String KEY_NOTIFICATION_VOLUME = "notification_volume";
|
private static final String KEY_NOTIFICATION_VOLUME = "notification_volume";
|
||||||
|
private static final boolean CONFIG_DEFAULT_VAL = false;
|
||||||
|
private boolean mSeparateNotification;
|
||||||
|
|
||||||
private Vibrator mVibrator;
|
private Vibrator mVibrator;
|
||||||
private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
|
private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
|
||||||
@@ -56,39 +63,74 @@ public class NotificationVolumePreferenceController extends VolumeSeekBarPrefere
|
|||||||
private final RingReceiver mReceiver = new RingReceiver();
|
private final RingReceiver mReceiver = new RingReceiver();
|
||||||
private final H mHandler = new H();
|
private final H mHandler = new H();
|
||||||
private INotificationManager mNoMan;
|
private INotificationManager mNoMan;
|
||||||
|
|
||||||
|
|
||||||
private int mMuteIcon;
|
private int mMuteIcon;
|
||||||
private final int mNormalIconId = R.drawable.ic_notifications;
|
private final int mNormalIconId = R.drawable.ic_notifications;
|
||||||
private final int mVibrateIconId = R.drawable.ic_volume_ringer_vibrate;
|
private final int mVibrateIconId = R.drawable.ic_volume_ringer_vibrate;
|
||||||
private final int mSilentIconId = R.drawable.ic_notifications_off_24dp;
|
private final int mSilentIconId = R.drawable.ic_notifications_off_24dp;
|
||||||
|
|
||||||
private final boolean mRingNotificationAliased;
|
|
||||||
|
|
||||||
|
|
||||||
public NotificationVolumePreferenceController(Context context) {
|
public NotificationVolumePreferenceController(Context context) {
|
||||||
this(context, KEY_NOTIFICATION_VOLUME);
|
this(context, KEY_NOTIFICATION_VOLUME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NotificationVolumePreferenceController(Context context, String key) {
|
public NotificationVolumePreferenceController(Context context, String key) {
|
||||||
super(context, key);
|
super(context, key);
|
||||||
|
|
||||||
mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
|
mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
if (mVibrator != null && !mVibrator.hasVibrator()) {
|
if (mVibrator != null && !mVibrator.hasVibrator()) {
|
||||||
mVibrator = null;
|
mVibrator = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
mRingNotificationAliased = mContext.getResources().getBoolean(
|
|
||||||
com.android.internal.R.bool.config_alias_ring_notif_stream_types);
|
|
||||||
updateRingerMode();
|
updateRingerMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow for notification slider to be enabled in the scenario where the config switches on
|
||||||
|
* while settings page is already on the screen by always configuring the preference, even if it
|
||||||
|
* is currently inactive.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void displayPreference(PreferenceScreen screen) {
|
||||||
|
super.displayPreference(screen);
|
||||||
|
if (mPreference == null) {
|
||||||
|
setupVolPreference(screen);
|
||||||
|
}
|
||||||
|
mSeparateNotification = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
|
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL);
|
||||||
|
if (mPreference != null) {
|
||||||
|
mPreference.setVisible(getAvailabilityStatus() == AVAILABLE);
|
||||||
|
}
|
||||||
|
updateEffectsSuppressor();
|
||||||
|
updatePreferenceIconAndSliderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only display the notification slider when the corresponding device config flag is set
|
||||||
|
*/
|
||||||
|
private void onDeviceConfigChange(DeviceConfig.Properties properties) {
|
||||||
|
Set<String> changeSet = properties.getKeyset();
|
||||||
|
|
||||||
|
if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) {
|
||||||
|
boolean newVal = properties.getBoolean(
|
||||||
|
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL);
|
||||||
|
if (newVal != mSeparateNotification) {
|
||||||
|
mSeparateNotification = newVal;
|
||||||
|
// manually hiding the preference because being unavailable does not do the job
|
||||||
|
if (mPreference != null) {
|
||||||
|
mPreference.setVisible(getAvailabilityStatus() == AVAILABLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
mReceiver.register(true);
|
mReceiver.register(true);
|
||||||
updateEffectsSuppressor();
|
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
updatePreferenceIconAndSliderState();
|
ActivityThread.currentApplication().getMainExecutor(),
|
||||||
|
this::onDeviceConfigChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
|
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
|
||||||
@@ -96,16 +138,17 @@ public class NotificationVolumePreferenceController extends VolumeSeekBarPrefere
|
|||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
mReceiver.register(false);
|
mReceiver.register(false);
|
||||||
|
DeviceConfig.removeOnPropertiesChangedListener(this::onDeviceConfigChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAvailabilityStatus() {
|
public int getAvailabilityStatus() {
|
||||||
|
boolean separateNotification = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
|
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false);
|
||||||
|
|
||||||
// Show separate notification slider if ring/notification are not aliased by AudioManager --
|
|
||||||
// if they are, notification volume is controlled by RingVolumePreferenceController.
|
|
||||||
return mContext.getResources().getBoolean(R.bool.config_show_notification_volume)
|
return mContext.getResources().getBoolean(R.bool.config_show_notification_volume)
|
||||||
&& (!mRingNotificationAliased || !Utils.isVoiceCapable(mContext))
|
|
||||||
&& !mHelper.isSingleVolume()
|
&& !mHelper.isSingleVolume()
|
||||||
|
&& (separateNotification || !Utils.isVoiceCapable(mContext))
|
||||||
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.settings.notification;
|
package com.android.settings.notification;
|
||||||
|
|
||||||
|
import android.app.ActivityThread;
|
||||||
import android.app.INotificationManager;
|
import android.app.INotificationManager;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
@@ -29,6 +30,7 @@ import android.os.Looper;
|
|||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.os.Vibrator;
|
import android.os.Vibrator;
|
||||||
|
import android.provider.DeviceConfig;
|
||||||
import android.service.notification.NotificationListenerService;
|
import android.service.notification.NotificationListenerService;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -36,11 +38,13 @@ import android.util.Log;
|
|||||||
import androidx.lifecycle.OnLifecycleEvent;
|
import androidx.lifecycle.OnLifecycleEvent;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This slider can represent both ring and notification, if the corresponding streams are aliased,
|
* This slider can represent both ring and notification, if the corresponding streams are aliased,
|
||||||
@@ -59,24 +63,21 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr
|
|||||||
|
|
||||||
private int mMuteIcon;
|
private int mMuteIcon;
|
||||||
|
|
||||||
/*
|
private int mNormalIconId;
|
||||||
* Whether ring and notification streams are aliased together by AudioManager.
|
|
||||||
* If they are, we'll present one volume control for both.
|
|
||||||
* If not, we'll present separate volume controls.
|
|
||||||
*/
|
|
||||||
private final boolean mRingAliasNotif;
|
|
||||||
|
|
||||||
private final int mNormalIconId;
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
final int mVibrateIconId;
|
int mVibrateIconId;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
final int mSilentIconId;
|
int mSilentIconId;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
final int mTitleId;
|
int mTitleId;
|
||||||
|
|
||||||
|
private boolean mSeparateNotification;
|
||||||
|
|
||||||
private INotificationManager mNoMan;
|
private INotificationManager mNoMan;
|
||||||
|
|
||||||
|
private static final boolean CONFIG_DEFAULT_VAL = false;
|
||||||
|
|
||||||
public RingVolumePreferenceController(Context context) {
|
public RingVolumePreferenceController(Context context) {
|
||||||
this(context, KEY_RING_VOLUME);
|
this(context, KEY_RING_VOLUME);
|
||||||
}
|
}
|
||||||
@@ -87,29 +88,56 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr
|
|||||||
if (mVibrator != null && !mVibrator.hasVibrator()) {
|
if (mVibrator != null && !mVibrator.hasVibrator()) {
|
||||||
mVibrator = null;
|
mVibrator = null;
|
||||||
}
|
}
|
||||||
|
mSeparateNotification = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
mRingAliasNotif = isRingAliasNotification();
|
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL);
|
||||||
if (mRingAliasNotif) {
|
loadPreferenceIconResources(mSeparateNotification);
|
||||||
mTitleId = R.string.ring_volume_option_title;
|
|
||||||
|
|
||||||
mNormalIconId = R.drawable.ic_notifications;
|
|
||||||
mSilentIconId = R.drawable.ic_notifications_off_24dp;
|
|
||||||
} else {
|
|
||||||
mTitleId = R.string.separate_ring_volume_option_title;
|
|
||||||
|
|
||||||
mNormalIconId = R.drawable.ic_ring_volume;
|
|
||||||
mSilentIconId = R.drawable.ic_ring_volume_off;
|
|
||||||
}
|
|
||||||
// todo: set a distinct vibrate icon for ring vs notification
|
|
||||||
mVibrateIconId = R.drawable.ic_volume_ringer_vibrate;
|
|
||||||
|
|
||||||
updateRingerMode();
|
updateRingerMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
private void loadPreferenceIconResources(boolean separateNotification) {
|
||||||
boolean isRingAliasNotification() {
|
if (separateNotification) {
|
||||||
return mContext.getResources().getBoolean(
|
mTitleId = R.string.separate_ring_volume_option_title;
|
||||||
com.android.internal.R.bool.config_alias_ring_notif_stream_types);
|
mNormalIconId = R.drawable.ic_ring_volume;
|
||||||
|
mSilentIconId = R.drawable.ic_ring_volume_off;
|
||||||
|
} else {
|
||||||
|
mTitleId = R.string.ring_volume_option_title;
|
||||||
|
mNormalIconId = R.drawable.ic_notifications;
|
||||||
|
mSilentIconId = R.drawable.ic_notifications_off_24dp;
|
||||||
|
}
|
||||||
|
// todo: set a distinct vibrate icon for ring vs notification
|
||||||
|
mVibrateIconId = R.drawable.ic_volume_ringer_vibrate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As the responsibility of this slider changes, so should its title & icon
|
||||||
|
*/
|
||||||
|
public void onDeviceConfigChange(DeviceConfig.Properties properties) {
|
||||||
|
Set<String> changeSet = properties.getKeyset();
|
||||||
|
if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) {
|
||||||
|
boolean valueUpdated = readSeparateNotificationVolumeConfig();
|
||||||
|
if (valueUpdated) {
|
||||||
|
updateEffectsSuppressor();
|
||||||
|
selectPreferenceIconState();
|
||||||
|
setPreferenceTitle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* side effect: updates the cached value of the config, and also the icon
|
||||||
|
* @return has the config changed?
|
||||||
|
*/
|
||||||
|
private boolean readSeparateNotificationVolumeConfig() {
|
||||||
|
boolean newVal = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
|
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, CONFIG_DEFAULT_VAL);
|
||||||
|
|
||||||
|
boolean valueUpdated = newVal != mSeparateNotification;
|
||||||
|
if (valueUpdated) {
|
||||||
|
mSeparateNotification = newVal;
|
||||||
|
loadPreferenceIconResources(newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
return valueUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||||
@@ -117,8 +145,11 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr
|
|||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
mReceiver.register(true);
|
mReceiver.register(true);
|
||||||
|
readSeparateNotificationVolumeConfig();
|
||||||
|
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
|
ActivityThread.currentApplication().getMainExecutor(), this::onDeviceConfigChange);
|
||||||
updateEffectsSuppressor();
|
updateEffectsSuppressor();
|
||||||
updatePreferenceIcon();
|
selectPreferenceIconState();
|
||||||
setPreferenceTitle();
|
setPreferenceTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +158,7 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr
|
|||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
mReceiver.register(false);
|
mReceiver.register(false);
|
||||||
|
DeviceConfig.removeOnPropertiesChangedListener(this::onDeviceConfigChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -170,7 +202,7 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr
|
|||||||
final int ringerMode = mHelper.getRingerModeInternal();
|
final int ringerMode = mHelper.getRingerModeInternal();
|
||||||
if (mRingerMode == ringerMode) return;
|
if (mRingerMode == ringerMode) return;
|
||||||
mRingerMode = ringerMode;
|
mRingerMode = ringerMode;
|
||||||
updatePreferenceIcon();
|
selectPreferenceIconState();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateEffectsSuppressor() {
|
private void updateEffectsSuppressor() {
|
||||||
@@ -190,7 +222,8 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hintsMatch(hints, mRingAliasNotif)) {
|
if (hintsMatch(hints, DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
|
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false))) {
|
||||||
mSuppressor = suppressor;
|
mSuppressor = suppressor;
|
||||||
if (mPreference != null) {
|
if (mPreference != null) {
|
||||||
final String text = SuppressorHelper.getSuppressionText(mContext, suppressor);
|
final String text = SuppressorHelper.getSuppressionText(mContext, suppressor);
|
||||||
@@ -200,11 +233,11 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean hintsMatch(int hints, boolean ringNotificationAliased) {
|
boolean hintsMatch(int hints, boolean notificationSeparated) {
|
||||||
return (hints & NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS) != 0
|
return (hints & NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS) != 0
|
||||||
|| (hints & NotificationListenerService.HINT_HOST_DISABLE_EFFECTS) != 0
|
|| (hints & NotificationListenerService.HINT_HOST_DISABLE_EFFECTS) != 0
|
||||||
|| ((hints & NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS)
|
|| ((hints & NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS)
|
||||||
!= 0 && ringNotificationAliased);
|
!= 0 && !notificationSeparated);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -217,7 +250,7 @@ public class RingVolumePreferenceController extends VolumeSeekBarPreferenceContr
|
|||||||
mVibrator = vibrator;
|
mVibrator = vibrator;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePreferenceIcon() {
|
private void selectPreferenceIconState() {
|
||||||
if (mPreference != null) {
|
if (mPreference != null) {
|
||||||
if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
|
if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
|
||||||
mPreference.showIcon(mNormalIconId);
|
mPreference.showIcon(mNormalIconId);
|
||||||
|
|||||||
@@ -55,12 +55,16 @@ public abstract class VolumeSeekBarPreferenceController extends
|
|||||||
public void displayPreference(PreferenceScreen screen) {
|
public void displayPreference(PreferenceScreen screen) {
|
||||||
super.displayPreference(screen);
|
super.displayPreference(screen);
|
||||||
if (isAvailable()) {
|
if (isAvailable()) {
|
||||||
|
setupVolPreference(screen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setupVolPreference(PreferenceScreen screen) {
|
||||||
mPreference = screen.findPreference(getPreferenceKey());
|
mPreference = screen.findPreference(getPreferenceKey());
|
||||||
mPreference.setCallback(mVolumePreferenceCallback);
|
mPreference.setCallback(mVolumePreferenceCallback);
|
||||||
mPreference.setStream(getAudioStream());
|
mPreference.setStream(getAudioStream());
|
||||||
mPreference.setMuteIcon(getMuteIcon());
|
mPreference.setMuteIcon(getMuteIcon());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.biometrics.fingerprint;
|
||||||
|
|
||||||
|
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||||
|
import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
|
||||||
|
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.Mockito.eq;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.hardware.fingerprint.FingerprintManager;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||||
|
import com.android.settingslib.RestrictedSwitchPreference;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
import org.robolectric.util.ReflectionHelpers;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(shadows = {ShadowUtils.class})
|
||||||
|
public class FingerprintSettingsRequireScreenOnToAuthPreferenceControllerTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private FingerprintManager mFingerprintManager;
|
||||||
|
@Mock
|
||||||
|
private PackageManager mPackageManager;
|
||||||
|
@Mock
|
||||||
|
private RestrictedSwitchPreference mPreference;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private FingerprintSettingsRequireScreenOnToAuthPreferenceController mController;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
mContext = spy(RuntimeEnvironment.application);
|
||||||
|
when(mContext.getSystemService(eq(Context.FINGERPRINT_SERVICE))).thenReturn(
|
||||||
|
mFingerprintManager);
|
||||||
|
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||||
|
|
||||||
|
mController = spy(new FingerprintSettingsRequireScreenOnToAuthPreferenceController(mContext,
|
||||||
|
"test_key"));
|
||||||
|
ReflectionHelpers.setField(mController, "mFingerprintManager", mFingerprintManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
ShadowUtils.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPreferenceChange_settingIsUpdated() {
|
||||||
|
boolean state = Settings.Secure.getInt(mContext.getContentResolver(),
|
||||||
|
Settings.Secure.SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED, 1) != 0;
|
||||||
|
|
||||||
|
assertThat(mController.isChecked()).isFalse();
|
||||||
|
assertThat(mController.onPreferenceChange(mPreference, !state)).isTrue();
|
||||||
|
boolean newState = Settings.Secure.getInt(mContext.getContentResolver(),
|
||||||
|
Settings.Secure.SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED, 1) != 0;
|
||||||
|
assertThat(newState).isEqualTo(!state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isAvailable_isEnabled_whenSfpsHardwareDetected_AndHasEnrolledFingerprints() {
|
||||||
|
assertThat(mController.isAvailable()).isEqualTo(false);
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||||
|
configure_hardwareDetected_isSfps_hasEnrolledTemplates(
|
||||||
|
true /* isHardwareDetected */,
|
||||||
|
true /* isPowerbuttonFps */,
|
||||||
|
true /* hasEnrolledTemplates */);
|
||||||
|
assertThat(mController.isAvailable()).isEqualTo(true);
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isAvailable_isDisabled_whenSfpsHardwareDetected_AndNoEnrolledFingerprints() {
|
||||||
|
assertThat(mController.isAvailable()).isEqualTo(false);
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||||
|
configure_hardwareDetected_isSfps_hasEnrolledTemplates(
|
||||||
|
true /* isHardwareDetected */,
|
||||||
|
true /* isPowerbuttonFps */,
|
||||||
|
false /* hasEnrolledTemplates */);
|
||||||
|
assertThat(mController.isAvailable()).isEqualTo(true);
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isUnavailable_whenHardwareNotDetected() {
|
||||||
|
assertThat(mController.isAvailable()).isFalse();
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||||
|
configure_hardwareDetected_isSfps_hasEnrolledTemplates(
|
||||||
|
false /* isHardwareDetected */,
|
||||||
|
true /* isPowerbuttonFps */,
|
||||||
|
true /* hasEnrolledTemplates */);
|
||||||
|
assertThat(mController.isAvailable()).isFalse();
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isUnavailable_onNonSfpsDevice() {
|
||||||
|
assertThat(mController.isAvailable()).isFalse();
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||||
|
configure_hardwareDetected_isSfps_hasEnrolledTemplates(
|
||||||
|
true /* isHardwareDetected */,
|
||||||
|
false /* isPowerbuttonFps */,
|
||||||
|
true /* hasEnrolledTemplates */);
|
||||||
|
assertThat(mController.isAvailable()).isFalse();
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configure_hardwareDetected_isSfps_hasEnrolledTemplates(
|
||||||
|
boolean isHardwareDetected, boolean isPowerbuttonFps, boolean hasEnrolledTemplates) {
|
||||||
|
when(mFingerprintManager.isHardwareDetected()).thenReturn(isHardwareDetected);
|
||||||
|
when(mFingerprintManager.isPowerbuttonFps()).thenReturn(isPowerbuttonFps);
|
||||||
|
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(hasEnrolledTemplates);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -24,9 +24,9 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
|
||||||
import android.bluetooth.BluetoothManager;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
|
|
||||||
@@ -54,11 +54,6 @@ public class BluetoothMaxConnectedAudioDevicesPreferenceControllerTest {
|
|||||||
@Spy
|
@Spy
|
||||||
private Context mSpyContext = RuntimeEnvironment.application;
|
private Context mSpyContext = RuntimeEnvironment.application;
|
||||||
|
|
||||||
@Mock
|
|
||||||
private BluetoothManager mBluetoothManager;
|
|
||||||
@Mock
|
|
||||||
private BluetoothAdapter mBluetoothAdapter;
|
|
||||||
|
|
||||||
private ListPreference mPreference;
|
private ListPreference mPreference;
|
||||||
private BluetoothMaxConnectedAudioDevicesPreferenceController mController;
|
private BluetoothMaxConnectedAudioDevicesPreferenceController mController;
|
||||||
|
|
||||||
@@ -68,15 +63,19 @@ public class BluetoothMaxConnectedAudioDevicesPreferenceControllerTest {
|
|||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
doReturn(mBluetoothManager).when(mSpyContext).getSystemService(BluetoothManager.class);
|
|
||||||
doReturn(mBluetoothAdapter).when(mBluetoothManager).getAdapter();
|
|
||||||
// Get XML values without mock
|
// Get XML values without mock
|
||||||
// Setup test list preference using XML values
|
// Setup test list preference using XML values
|
||||||
mPreference = new ListPreference(mSpyContext);
|
mPreference = new ListPreference(mSpyContext);
|
||||||
mPreference.setEntries(R.array.bluetooth_max_connected_audio_devices);
|
mPreference.setEntries(R.array.bluetooth_max_connected_audio_devices);
|
||||||
mPreference.setEntryValues(R.array.bluetooth_max_connected_audio_devices_values);
|
mPreference.setEntryValues(R.array.bluetooth_max_connected_audio_devices_values);
|
||||||
doReturn(TEST_MAX_CONNECTED_AUDIO_DEVICES).when(mBluetoothAdapter)
|
// Retrieve default max connected audio devices to a test controlled value
|
||||||
.getMaxConnectedAudioDevices();
|
try {
|
||||||
|
Resources res = mSpyContext.getPackageManager().getResourcesForApplication("com.android.bluetooth");
|
||||||
|
TEST_MAX_CONNECTED_AUDIO_DEVICES = res.getInteger(res.getIdentifier("config_bluetooth_max_connected_audio_devices", "integer", "com.android.bluetooth"));
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
// Init the actual controller
|
// Init the actual controller
|
||||||
mController = new BluetoothMaxConnectedAudioDevicesPreferenceController(mSpyContext);
|
mController = new BluetoothMaxConnectedAudioDevicesPreferenceController(mSpyContext);
|
||||||
// Construct preference in the controller via a mocked preference screen object
|
// Construct preference in the controller via a mocked preference screen object
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.android.settings.notification;
|
|||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@@ -25,10 +26,17 @@ import android.content.Context;
|
|||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.os.Vibrator;
|
import android.os.Vibrator;
|
||||||
|
import android.provider.DeviceConfig;
|
||||||
import android.service.notification.NotificationListenerService;
|
import android.service.notification.NotificationListenerService;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
|
|
||||||
import com.android.internal.R;
|
import androidx.preference.PreferenceManager;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
|
||||||
|
import com.android.settings.core.BasePreferenceController;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -37,11 +45,12 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
import org.robolectric.Shadows;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(shadows = {ShadowDeviceConfig.class})
|
||||||
public class NotificationVolumePreferenceControllerTest {
|
public class NotificationVolumePreferenceControllerTest {
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private AudioHelper mHelper;
|
private AudioHelper mHelper;
|
||||||
@Mock
|
@Mock
|
||||||
@@ -52,6 +61,11 @@ public class NotificationVolumePreferenceControllerTest {
|
|||||||
private Vibrator mVibrator;
|
private Vibrator mVibrator;
|
||||||
@Mock
|
@Mock
|
||||||
private Resources mResources;
|
private Resources mResources;
|
||||||
|
@Mock
|
||||||
|
private PreferenceManager mPreferenceManager;
|
||||||
|
|
||||||
|
private static final String READ_DEVICE_CONFIG_PERMISSION =
|
||||||
|
"android.permission.READ_DEVICE_CONFIG";
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private NotificationVolumePreferenceController mController;
|
private NotificationVolumePreferenceController mController;
|
||||||
@@ -87,7 +101,9 @@ public class NotificationVolumePreferenceControllerTest {
|
|||||||
public void isAvailable_voiceCapable_aliasedWithRing_shouldReturnFalse() {
|
public void isAvailable_voiceCapable_aliasedWithRing_shouldReturnFalse() {
|
||||||
when(mResources.getBoolean(
|
when(mResources.getBoolean(
|
||||||
com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true);
|
com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true);
|
||||||
when(mResources.getBoolean(R.bool.config_alias_ring_notif_stream_types)).thenReturn(true);
|
|
||||||
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
|
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
|
||||||
|
|
||||||
NotificationVolumePreferenceController controller =
|
NotificationVolumePreferenceController controller =
|
||||||
new NotificationVolumePreferenceController(mContext);
|
new NotificationVolumePreferenceController(mContext);
|
||||||
@@ -105,7 +121,9 @@ public class NotificationVolumePreferenceControllerTest {
|
|||||||
public void isAvailable_voiceCapable_separatedFromRing_shouldReturnTrue() {
|
public void isAvailable_voiceCapable_separatedFromRing_shouldReturnTrue() {
|
||||||
when(mResources.getBoolean(
|
when(mResources.getBoolean(
|
||||||
com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true);
|
com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true);
|
||||||
when(mResources.getBoolean(R.bool.config_alias_ring_notif_stream_types)).thenReturn(false);
|
|
||||||
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
|
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false);
|
||||||
|
|
||||||
NotificationVolumePreferenceController controller =
|
NotificationVolumePreferenceController controller =
|
||||||
new NotificationVolumePreferenceController(mContext);
|
new NotificationVolumePreferenceController(mContext);
|
||||||
@@ -170,4 +188,70 @@ public class NotificationVolumePreferenceControllerTest {
|
|||||||
.isTrue();
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enableSeparateNotificationConfig_controllerBecomesAvailable() {
|
||||||
|
PreferenceScreen screen = spy(new PreferenceScreen(mContext, null));
|
||||||
|
VolumeSeekBarPreference volumeSeekBarPreference = mock(VolumeSeekBarPreference.class);
|
||||||
|
when(screen.getPreferenceManager()).thenReturn(mPreferenceManager);
|
||||||
|
when(screen.getContext()).thenReturn(mContext);
|
||||||
|
when(mResources.getBoolean(
|
||||||
|
com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true);
|
||||||
|
// block the alternative condition to enable controller
|
||||||
|
when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
|
||||||
|
|
||||||
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
|
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
|
||||||
|
|
||||||
|
NotificationVolumePreferenceController controller =
|
||||||
|
new NotificationVolumePreferenceController(mContext);
|
||||||
|
when(screen.findPreference(controller.getPreferenceKey()))
|
||||||
|
.thenReturn(volumeSeekBarPreference);
|
||||||
|
|
||||||
|
// allow the controller to subscribe
|
||||||
|
Shadows.shadowOf((android.app.Application) ApplicationProvider.getApplicationContext())
|
||||||
|
.grantPermissions(READ_DEVICE_CONFIG_PERMISSION);
|
||||||
|
controller.onResume();
|
||||||
|
controller.displayPreference(screen);
|
||||||
|
|
||||||
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
|
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, Boolean.toString(true),
|
||||||
|
false);
|
||||||
|
|
||||||
|
assertThat(controller.getAvailabilityStatus()
|
||||||
|
== BasePreferenceController.AVAILABLE).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void disableSeparateNotificationConfig_controllerBecomesUnavailable() {
|
||||||
|
PreferenceScreen screen = spy(new PreferenceScreen(mContext, null));
|
||||||
|
VolumeSeekBarPreference volumeSeekBarPreference = mock(VolumeSeekBarPreference.class);
|
||||||
|
when(screen.getPreferenceManager()).thenReturn(mPreferenceManager);
|
||||||
|
when(screen.getContext()).thenReturn(mContext);
|
||||||
|
when(mResources.getBoolean(
|
||||||
|
com.android.settings.R.bool.config_show_notification_volume)).thenReturn(true);
|
||||||
|
|
||||||
|
// block the alternative condition to enable controller
|
||||||
|
when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
|
||||||
|
|
||||||
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
|
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false);
|
||||||
|
|
||||||
|
NotificationVolumePreferenceController controller =
|
||||||
|
new NotificationVolumePreferenceController(mContext);
|
||||||
|
|
||||||
|
when(screen.findPreference(controller.getPreferenceKey()))
|
||||||
|
.thenReturn(volumeSeekBarPreference);
|
||||||
|
|
||||||
|
Shadows.shadowOf((android.app.Application) ApplicationProvider.getApplicationContext())
|
||||||
|
.grantPermissions(READ_DEVICE_CONFIG_PERMISSION);
|
||||||
|
controller.onResume();
|
||||||
|
controller.displayPreference(screen);
|
||||||
|
|
||||||
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
|
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
|
||||||
|
|
||||||
|
assertThat(controller.getAvailabilityStatus()
|
||||||
|
== BasePreferenceController.UNSUPPORTED_ON_DEVICE).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,10 +27,13 @@ import android.content.Context;
|
|||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.os.Vibrator;
|
import android.os.Vibrator;
|
||||||
|
import android.provider.DeviceConfig;
|
||||||
import android.service.notification.NotificationListenerService;
|
import android.service.notification.NotificationListenerService;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
|
|
||||||
|
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -39,9 +42,11 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
import org.robolectric.shadows.ShadowApplication;
|
import org.robolectric.shadows.ShadowApplication;
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(shadows = {ShadowDeviceConfig.class})
|
||||||
public class RingVolumePreferenceControllerTest {
|
public class RingVolumePreferenceControllerTest {
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
@@ -124,9 +129,10 @@ public class RingVolumePreferenceControllerTest {
|
|||||||
// todo: verify that the title change is displayed, by examining the underlying preference
|
// todo: verify that the title change is displayed, by examining the underlying preference
|
||||||
@Test
|
@Test
|
||||||
public void ringNotificationStreamsNotAliased_sliderTitleSetToRingOnly() {
|
public void ringNotificationStreamsNotAliased_sliderTitleSetToRingOnly() {
|
||||||
when(mResources.getBoolean(
|
|
||||||
com.android.internal.R.bool.config_alias_ring_notif_stream_types))
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
.thenReturn(false);
|
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false);
|
||||||
|
|
||||||
final RingVolumePreferenceController controller =
|
final RingVolumePreferenceController controller =
|
||||||
new RingVolumePreferenceController(mContext);
|
new RingVolumePreferenceController(mContext);
|
||||||
|
|
||||||
@@ -138,8 +144,9 @@ public class RingVolumePreferenceControllerTest {
|
|||||||
@Test
|
@Test
|
||||||
public void ringNotificationStreamsAliased_sliderTitleIncludesBothRingNotification() {
|
public void ringNotificationStreamsAliased_sliderTitleIncludesBothRingNotification() {
|
||||||
|
|
||||||
when(mResources.getBoolean(
|
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
com.android.internal.R.bool.config_alias_ring_notif_stream_types)).thenReturn(true);
|
SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
|
||||||
|
|
||||||
final RingVolumePreferenceController control = new RingVolumePreferenceController(mContext);
|
final RingVolumePreferenceController control = new RingVolumePreferenceController(mContext);
|
||||||
|
|
||||||
int expectedTitleId = R.string.ring_volume_option_title;
|
int expectedTitleId = R.string.ring_volume_option_title;
|
||||||
@@ -150,39 +157,39 @@ public class RingVolumePreferenceControllerTest {
|
|||||||
@Test
|
@Test
|
||||||
public void setHintsRing_aliased_Matches() {
|
public void setHintsRing_aliased_Matches() {
|
||||||
assertThat(mController.hintsMatch(
|
assertThat(mController.hintsMatch(
|
||||||
NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS, true)).isTrue();
|
NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS, false)).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setHintsRingNotification_aliased_Matches() {
|
public void setHintsRingNotification_aliased_Matches() {
|
||||||
assertThat(mController.hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_EFFECTS,
|
assertThat(mController.hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_EFFECTS,
|
||||||
true)).isTrue();
|
false)).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setHintNotification_aliased_Matches() {
|
public void setHintNotification_aliased_Matches() {
|
||||||
assertThat(mController
|
assertThat(mController
|
||||||
.hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS,
|
.hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS,
|
||||||
true)).isTrue();
|
false)).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setHintsRing_unaliased_Matches() {
|
public void setHintsRing_unaliased_Matches() {
|
||||||
assertThat(mController.hintsMatch(
|
assertThat(mController.hintsMatch(
|
||||||
NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS, false)).isTrue();
|
NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS, true)).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setHintsRingNotification_unaliased_Matches() {
|
public void setHintsRingNotification_unaliased_Matches() {
|
||||||
assertThat(mController.hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_EFFECTS,
|
assertThat(mController.hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_EFFECTS,
|
||||||
false)).isTrue();
|
true)).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setHintNotification_unaliased_doesNotMatch() {
|
public void setHintNotification_unaliased_doesNotMatch() {
|
||||||
assertThat(mController
|
assertThat(mController
|
||||||
.hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS,
|
.hintsMatch(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS,
|
||||||
false)).isFalse();
|
true)).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user