Snap for 12361654 from f37570e46e
to 24Q4-release
Change-Id: I8eb0b17ae5eea2c3bb31fc4f2c7a2cf16a31b2e6
This commit is contained in:
@@ -783,9 +783,13 @@
|
||||
|
||||
<activity android:name="Settings$CombinedBiometricSettingsActivity"
|
||||
android:label="@string/security_settings_biometric_preference_title"
|
||||
android:exported="false"
|
||||
android:exported="true"
|
||||
android:enableOnBackInvokedCallback="false"
|
||||
android:taskAffinity="com.android.settings.root">
|
||||
<intent-filter>
|
||||
<action android:name="android.settings.COMBINED_BIOMETRICS_SETTINGS" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||
android:value="com.android.settings.biometrics.combination.CombinedBiometricSettings" />
|
||||
<meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
|
||||
|
35
res/drawable/ic_pointer_and_touchpad.xml
Normal file
35
res/drawable/ic_pointer_and_touchpad.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<!--
|
||||
Copyright 2024 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.
|
||||
-->
|
||||
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<com.android.settingslib.widget.AdaptiveIconShapeDrawable
|
||||
android:width="@dimen/accessibility_icon_size"
|
||||
android:height="@dimen/accessibility_icon_size"
|
||||
android:color="@color/accessibility_feature_background"/>
|
||||
</item>
|
||||
<item android:gravity="center">
|
||||
<vector
|
||||
android:height="32dp"
|
||||
android:width="32dp"
|
||||
android:viewportHeight="32"
|
||||
android:viewportWidth="32">
|
||||
<path
|
||||
android:fillColor="#fff"
|
||||
android:pathData="M19.15,22.35a2.6,2.6 0,0 0,1.91 -0.79,2.6 2.6,0 0,0 0.79,-1.91v-0.9h-5.4v0.9c0,0.75 0.26,1.39 0.79,1.91a2.6,2.6 0,0 0,1.91 0.79ZM16.49,17.4h1.98v-2.17a2.6,2.6 0,0 0,-1.98 2.17ZM19.82,17.4h2a2.6,2.6 0,0 0,-0.68 -1.39,2.62 2.62,0 0,0 -1.32,-0.78v2.17ZM19.15,23.7a3.96,3.96 0,0 1,-4.05 -4.05v-1.8c0,-1.14 0.39,-2.1 1.16,-2.87a3.93,3.93 0,0 1,2.89 -1.18c1.14,0 2.1,0.4 2.87,1.18a3.87,3.87 0,0 1,1.18 2.87v1.8c0,1.14 -0.4,2.1 -1.18,2.89a3.9,3.9 0,0 1,-2.87 1.16ZM10.15,20.55v-8.1,8.1ZM10.15,21.9c-0.38,0 -0.7,-0.13 -0.96,-0.4s-0.39,-0.59 -0.39,-0.95v-8.1c0,-0.36 0.13,-0.67 0.4,-0.94 0.26,-0.27 0.57,-0.41 0.95,-0.41h11.7c0.37,0 0.7,0.14 0.96,0.41 0.26,0.27 0.39,0.58 0.39,0.94L10.15,12.45v8.1h3.6v1.35h-3.6Z"/>
|
||||
</vector>
|
||||
</item>
|
||||
</layer-list>
|
@@ -19,21 +19,5 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/setup_wizard_layout"
|
||||
android:icon="@drawable/ic_delete_accent"
|
||||
app:sucHeaderText="@string/main_clear_confirm_title">
|
||||
|
||||
<LinearLayout
|
||||
style="@style/SudContentFrame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sud_layout_subtitle"
|
||||
style="@style/TextAppearance.PreferenceTitle.SettingsLib"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/main_clear_final_desc"/>
|
||||
</LinearLayout>
|
||||
</com.google.android.setupdesign.GlifLayout>
|
||||
app:sucHeaderText="@string/main_clear_confirm_title" />
|
||||
|
@@ -4532,8 +4532,8 @@
|
||||
<string name="trackpad_bottom_right_tap_summary">Click in the bottom right corner of the touchpad for more options</string>
|
||||
<!-- Title text for 'Pointer speed'. [CHAR LIMIT=35] -->
|
||||
<string name="trackpad_pointer_speed">Pointer speed</string>
|
||||
<!-- Title text for mouse pointer fill style. [CHAR LIMIT=35] -->
|
||||
<string name="pointer_fill_style">Pointer fill style</string>
|
||||
<!-- Title text for mouse pointer color. [CHAR LIMIT=35] -->
|
||||
<string name="pointer_fill_style">Pointer color</string>
|
||||
<!-- Content description for black pointer fill style. [CHAR LIMIT=60] -->
|
||||
<string name="pointer_fill_style_black_button">Change pointer fill style to black</string>
|
||||
<!-- Content description for green pointer fill style. [CHAR LIMIT=60] -->
|
||||
@@ -4810,6 +4810,12 @@
|
||||
<string name="display_category_title">Display</string>
|
||||
<!-- Title for the accessibility color and motion page. [CHAR LIMIT=50] -->
|
||||
<string name="accessibility_color_and_motion_title">Color and motion</string>
|
||||
<!-- Title for the accessibility pointer and touchpad page. [CHAR LIMIT=50] -->
|
||||
<string name="accessibility_pointer_and_touchpad_title">Pointer & touchpad accessibility</string>
|
||||
<!-- Summary for the accessibility pointer and touchpad page. [CHAR LIMIT=50] -->
|
||||
<string name="accessibility_pointer_and_touchpad_summary">Pointer color, pointer size & more</string>
|
||||
<!-- Title for the accessibility pointer color customization page. [CHAR LIMIT=50] -->
|
||||
<string name="accessibility_pointer_color_customization_title">Pointer color customization</string>
|
||||
<!-- Title for the accessibility color contrast page. [CHAR LIMIT=50] -->
|
||||
<string name="accessibility_color_contrast_title">Color contrast</string>
|
||||
<!-- Intro for the accessibility color contrast page. [CHAR LIMIT=NONE] -->
|
||||
|
@@ -72,17 +72,6 @@
|
||||
android:title="@string/accessibility_toggle_large_pointer_icon_title"
|
||||
settings:controller="com.android.settings.accessibility.LargePointerIconPreferenceController"/>
|
||||
|
||||
<com.android.settings.widget.LabeledSeekBarPreference
|
||||
android:key="large_pointer_scale"
|
||||
android:title="@string/accessibility_toggle_large_pointer_icon_title"
|
||||
android:summary="@string/accessibility_toggle_large_pointer_icon_summary"
|
||||
android:max="@integer/pointer_scale_seek_bar_end"
|
||||
settings:iconStart="@drawable/ic_remove_24dp"
|
||||
settings:iconStartContentDescription="@string/pointer_scale_decrease_content_description"
|
||||
settings:iconEnd="@drawable/ic_add_24dp"
|
||||
settings:iconEndContentDescription="@string/pointer_scale_increase_content_description"
|
||||
settings:controller="com.android.settings.inputmethod.PointerScaleSeekBarController" />
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="experimental_category"
|
||||
android:persistent="false"
|
||||
|
48
res/xml/accessibility_pointer_and_touchpad.xml
Normal file
48
res/xml/accessibility_pointer_and_touchpad.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 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.
|
||||
-->
|
||||
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:key="accessibility_pointer_and_touchpad"
|
||||
android:persistent="false"
|
||||
android:title="@string/accessibility_pointer_and_touchpad_title">
|
||||
|
||||
<com.android.settings.widget.LabeledSeekBarPreference
|
||||
android:key="pointer_scale_preference"
|
||||
android:title="@string/pointer_scale"
|
||||
android:max="@integer/pointer_scale_seek_bar_end"
|
||||
settings:iconStart="@drawable/ic_remove_24dp"
|
||||
settings:iconStartContentDescription="@string/pointer_scale_decrease_content_description"
|
||||
settings:iconEnd="@drawable/ic_add_24dp"
|
||||
settings:iconEndContentDescription="@string/pointer_scale_increase_content_description"
|
||||
settings:controller="com.android.settings.inputmethod.PointerScaleSeekBarController" />
|
||||
|
||||
<Preference
|
||||
android:fragment="com.android.settings.inputmethod.PointerColorCustomizationFragment"
|
||||
android:key="pointer_color_customization_preference"
|
||||
android:persistent="false"
|
||||
android:title="@string/accessibility_pointer_color_customization_title"/>
|
||||
|
||||
<Preference
|
||||
android:fragment="com.android.settings.accessibility.ToggleAutoclickPreferenceFragment"
|
||||
android:key="autoclick_preference"
|
||||
android:persistent="false"
|
||||
android:title="@string/accessibility_autoclick_preference_title"
|
||||
settings:keywords="@string/keywords_auto_click"
|
||||
settings:controller="com.android.settings.accessibility.AutoclickPreferenceController"/>
|
||||
|
||||
</PreferenceScreen>
|
34
res/xml/accessibility_pointer_color_customization.xml
Normal file
34
res/xml/accessibility_pointer_color_customization.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 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.
|
||||
-->
|
||||
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:key="accessibility_pointer_color_customization"
|
||||
android:persistent="false"
|
||||
android:title="@string/accessibility_pointer_color_customization_title">
|
||||
|
||||
<com.android.settings.inputmethod.PointerFillStylePreference
|
||||
android:key="pointer_fill_style"
|
||||
android:title="@string/pointer_fill_style"
|
||||
settings:controller="com.android.settings.inputmethod.PointerFillStylePreferenceController"/>
|
||||
|
||||
<com.android.settings.inputmethod.PointerStrokeStylePreference
|
||||
android:key="pointer_stroke_style"
|
||||
android:title="@string/pointer_stroke_style"
|
||||
settings:controller="com.android.settings.inputmethod.PointerStrokeStylePreferenceController"/>
|
||||
|
||||
</PreferenceScreen>
|
@@ -110,6 +110,16 @@
|
||||
settings:keywords="@string/keywords_vibration"
|
||||
android:summary="@string/accessibility_vibration_settings_summary"/>
|
||||
|
||||
<Preference
|
||||
android:fragment="com.android.settings.inputmethod.PointerTouchpadFragment"
|
||||
android:key="pointer_and_touchpad"
|
||||
android:icon="@drawable/ic_pointer_and_touchpad"
|
||||
android:persistent="false"
|
||||
android:title="@string/accessibility_pointer_and_touchpad_title"
|
||||
android:summary="@string/accessibility_pointer_and_touchpad_summary"
|
||||
settings:controller="com.android.settings.inputmethod.PointerTouchpadPreferenceController"
|
||||
settings:searchable="true"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
|
@@ -37,12 +37,4 @@
|
||||
android:title="@string/accessibility_setting_item_control_timeout_title"
|
||||
settings:controller="com.android.settings.accessibility.AccessibilityTimeoutPreferenceController"
|
||||
settings:keywords="@string/keywords_accessibility_timeout"/>
|
||||
|
||||
<Preference
|
||||
android:fragment="com.android.settings.accessibility.ToggleAutoclickPreferenceFragment"
|
||||
android:key="autoclick_preference"
|
||||
android:persistent="false"
|
||||
android:title="@string/accessibility_autoclick_preference_title"
|
||||
settings:keywords="@string/keywords_auto_click"
|
||||
settings:controller="com.android.settings.accessibility.AutoclickPreferenceController"/>
|
||||
</PreferenceScreen>
|
@@ -62,29 +62,14 @@
|
||||
android:selectable="false"
|
||||
settings:controller="com.android.settings.inputmethod.TrackpadPointerSpeedPreferenceController"/>
|
||||
|
||||
<com.android.settings.inputmethod.PointerFillStylePreference
|
||||
android:key="pointer_fill_style"
|
||||
android:title="@string/pointer_fill_style"
|
||||
<Preference
|
||||
android:fragment="com.android.settings.inputmethod.PointerTouchpadFragment"
|
||||
android:key="pointer_and_touchpad"
|
||||
android:order="50"
|
||||
settings:controller="com.android.settings.inputmethod.PointerFillStylePreferenceController"/>
|
||||
|
||||
<com.android.settings.inputmethod.PointerStrokeStylePreference
|
||||
android:key="pointer_stroke_style"
|
||||
android:title="@string/pointer_stroke_style"
|
||||
android:order="60"
|
||||
settings:controller="com.android.settings.inputmethod.PointerStrokeStylePreferenceController"/>
|
||||
|
||||
<com.android.settings.widget.LabeledSeekBarPreference
|
||||
android:key="pointer_scale"
|
||||
android:title="@string/pointer_scale"
|
||||
android:order="70"
|
||||
android:max="@integer/pointer_scale_seek_bar_end"
|
||||
settings:iconStart="@drawable/ic_remove_24dp"
|
||||
settings:searchable="false"
|
||||
settings:iconStartContentDescription="@string/pointer_scale_decrease_content_description"
|
||||
settings:iconEnd="@drawable/ic_add_24dp"
|
||||
settings:iconEndContentDescription="@string/pointer_scale_increase_content_description"
|
||||
settings:controller="com.android.settings.inputmethod.PointerScaleSeekBarController" />
|
||||
android:persistent="false"
|
||||
android:title="@string/accessibility_pointer_and_touchpad_title"
|
||||
android:summary="@string/accessibility_pointer_and_touchpad_summary"
|
||||
settings:searchable="true"/>
|
||||
|
||||
<com.android.settingslib.widget.ButtonPreference
|
||||
android:key="trackpad_touch_gesture"
|
||||
|
@@ -179,6 +179,8 @@ public class FallbackHome extends Activity {
|
||||
SystemClock.uptimeMillis(), false);
|
||||
finish();
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "User not yet unlocked");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -19,8 +19,6 @@ package com.android.settings;
|
||||
|
||||
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.admin.FactoryResetProtectionPolicy;
|
||||
@@ -28,7 +26,6 @@ import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.Color;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemProperties;
|
||||
@@ -36,12 +33,10 @@ import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.service.oemlock.OemLockManager;
|
||||
import android.service.persistentdata.PersistentDataBlockManager;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
@@ -62,7 +57,7 @@ import com.google.android.setupdesign.GlifLayout;
|
||||
* has defined one, followed by a final strongly-worded "THIS WILL ERASE EVERYTHING
|
||||
* ON THE PHONE" prompt. If at any time the phone is allowed to go to sleep, is
|
||||
* locked, et cetera, then the confirmation sequence is abandoned.
|
||||
*
|
||||
* <p>
|
||||
* This is the confirmation screen.
|
||||
*/
|
||||
public class MainClearConfirm extends InstrumentedFragment {
|
||||
@@ -70,9 +65,11 @@ public class MainClearConfirm extends InstrumentedFragment {
|
||||
|
||||
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
|
||||
|
||||
@VisibleForTesting View mContentView;
|
||||
@VisibleForTesting
|
||||
GlifLayout mContentView;
|
||||
private boolean mEraseSdCard;
|
||||
@VisibleForTesting boolean mEraseEsims;
|
||||
@VisibleForTesting
|
||||
boolean mEraseEsims;
|
||||
|
||||
/**
|
||||
* The user has gone through the multiple confirmation, so now we go ahead
|
||||
@@ -215,9 +212,7 @@ public class MainClearConfirm extends InstrumentedFragment {
|
||||
* Configure the UI for the final confirmation interaction
|
||||
*/
|
||||
private void establishFinalConfirmationState() {
|
||||
final GlifLayout layout = mContentView.findViewById(R.id.setup_wizard_layout);
|
||||
|
||||
final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
|
||||
final FooterBarMixin mixin = mContentView.getMixin(FooterBarMixin.class);
|
||||
mixin.setPrimaryButton(
|
||||
new FooterButton.Builder(getActivity())
|
||||
.setText(R.string.main_clear_button_text)
|
||||
@@ -228,21 +223,6 @@ public class MainClearConfirm extends InstrumentedFragment {
|
||||
);
|
||||
}
|
||||
|
||||
private void setUpActionBarAndTitle() {
|
||||
final Activity activity = getActivity();
|
||||
if (activity == null) {
|
||||
Log.e(TAG, "No activity attached, skipping setUpActionBarAndTitle");
|
||||
return;
|
||||
}
|
||||
final ActionBar actionBar = activity.getActionBar();
|
||||
if (actionBar == null) {
|
||||
Log.e(TAG, "No actionbar, skipping setUpActionBarAndTitle");
|
||||
return;
|
||||
}
|
||||
actionBar.hide();
|
||||
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -258,32 +238,26 @@ public class MainClearConfirm extends InstrumentedFragment {
|
||||
.show();
|
||||
return new View(getActivity());
|
||||
}
|
||||
mContentView = inflater.inflate(R.layout.main_clear_confirm, null);
|
||||
setUpActionBarAndTitle();
|
||||
mContentView = (GlifLayout) inflater.inflate(R.layout.main_clear_confirm, null);
|
||||
establishFinalConfirmationState();
|
||||
setAccessibilityTitle();
|
||||
setSubtitle();
|
||||
setAccessibilityTitle();
|
||||
return mContentView;
|
||||
}
|
||||
|
||||
private void setAccessibilityTitle() {
|
||||
CharSequence currentTitle = getActivity().getTitle();
|
||||
TextView confirmationMessage = mContentView.findViewById(R.id.sud_layout_description);
|
||||
CharSequence confirmationMessage = mContentView.getDescriptionText();
|
||||
if (confirmationMessage != null) {
|
||||
String accessibleText = new StringBuilder(currentTitle).append(",").append(
|
||||
confirmationMessage.getText()).toString();
|
||||
String accessibleText = currentTitle + "," + confirmationMessage;
|
||||
getActivity().setTitle(Utils.createAccessibleSequence(currentTitle, accessibleText));
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setSubtitle() {
|
||||
if (mEraseEsims) {
|
||||
TextView confirmationMessage = mContentView.findViewById(R.id.sud_layout_description);
|
||||
if (confirmationMessage != null) {
|
||||
confirmationMessage.setText(R.string.main_clear_final_desc_esim);
|
||||
}
|
||||
}
|
||||
mContentView.setDescriptionText(
|
||||
mEraseEsims ? R.string.main_clear_final_desc_esim : R.string.main_clear_final_desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -67,14 +67,11 @@ public class FloatingMenuFadePreferenceController extends BasePreferenceControll
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
if (mPreference != null) {
|
||||
return mPreference.isEnabled()
|
||||
? "%s"
|
||||
: mContext.getString(
|
||||
R.string.accessibility_button_disabled_button_mode_summary);
|
||||
} else {
|
||||
return "%s";
|
||||
int rId = R.string.accessibility_button_fade_summary;
|
||||
if (mPreference != null && !mPreference.isEnabled()) {
|
||||
rId = R.string.accessibility_button_disabled_button_mode_summary;
|
||||
}
|
||||
return mContext.getString(rId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -44,7 +44,7 @@ public class AudioSharingLoadingStateDialogFragment extends InstrumentedDialogFr
|
||||
private static final String TAG = "AudioSharingLoadingDlg";
|
||||
|
||||
private static final String BUNDLE_KEY_MESSAGE = "bundle_key_message";
|
||||
private static final long AUTO_DISMISS_TIME_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(10);
|
||||
private static final long AUTO_DISMISS_TIME_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(15);
|
||||
private static final int AUTO_DISMISS_MESSAGE_ID = R.id.message;
|
||||
|
||||
private static String sMessage = "";
|
||||
@@ -74,14 +74,16 @@ public class AudioSharingLoadingStateDialogFragment extends InstrumentedDialogFr
|
||||
}
|
||||
AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG);
|
||||
if (dialog != null) {
|
||||
if (sMessage.equals(message)) {
|
||||
Log.d(TAG, "Dialog is showing with same message, return.");
|
||||
return;
|
||||
} else {
|
||||
Log.d(TAG, "Dialog is showing with different message, dismiss and reshow.");
|
||||
dialog.dismiss();
|
||||
if (!sMessage.equals(message)) {
|
||||
Log.d(TAG, "Update dialog message.");
|
||||
TextView messageView = dialog.findViewById(R.id.message);
|
||||
if (messageView != null) {
|
||||
messageView.setText(message);
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "Dialog is showing, return.");
|
||||
return;
|
||||
}
|
||||
sMessage = message;
|
||||
Log.d(TAG, "Show up the loading dialog.");
|
||||
Bundle args = new Bundle();
|
||||
@@ -113,8 +115,10 @@ public class AudioSharingLoadingStateDialogFragment extends InstrumentedDialogFr
|
||||
@NonNull
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
mHandler.postDelayed(() -> dismiss(), AUTO_DISMISS_MESSAGE_ID,
|
||||
AUTO_DISMISS_TIME_THRESHOLD_MS);
|
||||
mHandler.postDelayed(() -> {
|
||||
Log.d(TAG, "Auto dismiss dialog after timeout");
|
||||
dismiss();
|
||||
}, AUTO_DISMISS_MESSAGE_ID, AUTO_DISMISS_TIME_THRESHOLD_MS);
|
||||
Bundle args = requireArguments();
|
||||
String message = args.getString(BUNDLE_KEY_MESSAGE, "");
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
@@ -132,6 +136,7 @@ public class AudioSharingLoadingStateDialogFragment extends InstrumentedDialogFr
|
||||
public void onDismiss(@NonNull DialogInterface dialog) {
|
||||
super.onDismiss(dialog);
|
||||
if (mHandler != null) {
|
||||
Log.d(TAG, "Dialog dismissed, remove auto dismiss task");
|
||||
mHandler.removeMessages(AUTO_DISMISS_MESSAGE_ID);
|
||||
}
|
||||
}
|
||||
|
@@ -70,6 +70,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -112,12 +113,13 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||
private final OnAudioSharingStateChangedListener mListener;
|
||||
private Map<Integer, List<BluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
|
||||
private List<BluetoothDevice> mTargetActiveSinks = new ArrayList<>();
|
||||
@Nullable private AudioSharingDeviceItem mTargetActiveItem;
|
||||
private List<AudioSharingDeviceItem> mDeviceItemsForSharing = new ArrayList<>();
|
||||
@VisibleForTesting IntentFilter mIntentFilter;
|
||||
private final AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false);
|
||||
private AtomicInteger mIntentHandleStage =
|
||||
new AtomicInteger(StartIntentHandleStage.TO_HANDLE.ordinal());
|
||||
private CopyOnWriteArrayList<BluetoothDevice> mSinksInAdding = new CopyOnWriteArrayList<>();
|
||||
|
||||
@VisibleForTesting
|
||||
BroadcastReceiver mReceiver =
|
||||
@@ -294,7 +296,16 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
||||
public void onReceiveStateChanged(
|
||||
@NonNull BluetoothDevice sink,
|
||||
int sourceId,
|
||||
@NonNull BluetoothLeBroadcastReceiveState state) {}
|
||||
@NonNull BluetoothLeBroadcastReceiveState state) {
|
||||
if (BluetoothUtils.isConnected(state)) {
|
||||
if (mSinksInAdding.contains(sink)) {
|
||||
mSinksInAdding.remove(sink);
|
||||
}
|
||||
dismissLoadingStateDialogIfNeeded();
|
||||
Log.d(TAG, "onReceiveStateChanged() connected, sink = " + sink
|
||||
+ ", remaining sinks = " + mSinksInAdding);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AudioSharingSwitchBarController(
|
||||
@@ -506,17 +517,20 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
||||
mBtManager, mGroupedConnectedDevices, /* filterByInSharing= */ false);
|
||||
// deviceItems is ordered. The active device is the first place if exits.
|
||||
mDeviceItemsForSharing = new ArrayList<>(deviceItems);
|
||||
mTargetActiveSinks = new ArrayList<>();
|
||||
mTargetActiveItem = null;
|
||||
if (!deviceItems.isEmpty() && deviceItems.get(0).isActive()) {
|
||||
// If active device exists for audio sharing, share to it
|
||||
// automatically once the broadcast is started.
|
||||
mTargetActiveSinks =
|
||||
mGroupedConnectedDevices.getOrDefault(
|
||||
deviceItems.get(0).getGroupId(), ImmutableList.of());
|
||||
mTargetActiveItem = deviceItems.get(0);
|
||||
mDeviceItemsForSharing.remove(0);
|
||||
}
|
||||
if (mBroadcast != null) {
|
||||
mBroadcast.startPrivateBroadcast();
|
||||
mSinksInAdding.clear();
|
||||
// TODO: use string res once finalized.
|
||||
AudioSharingUtils.postOnMainThread(mContext,
|
||||
() -> AudioSharingLoadingStateDialogFragment.show(mFragment,
|
||||
"Starting audio stream..."));
|
||||
mMetricsFeatureProvider.action(
|
||||
mContext,
|
||||
SettingsEnums.ACTION_AUDIO_SHARING_MAIN_SWITCH_ON,
|
||||
@@ -580,27 +594,30 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
||||
}
|
||||
|
||||
private void handleOnBroadcastReady() {
|
||||
List<BluetoothDevice> targetActiveSinks = mTargetActiveItem == null ? ImmutableList.of()
|
||||
: mGroupedConnectedDevices.getOrDefault(
|
||||
mTargetActiveItem.getGroupId(), ImmutableList.of());
|
||||
Pair<Integer, Object>[] eventData =
|
||||
AudioSharingUtils.buildAudioSharingDialogEventData(
|
||||
SettingsEnums.AUDIO_SHARING_SETTINGS,
|
||||
SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE,
|
||||
/* userTriggered= */ false,
|
||||
/* deviceCountInSharing= */ mTargetActiveSinks.isEmpty() ? 0 : 1,
|
||||
/* deviceCountInSharing= */ targetActiveSinks.isEmpty() ? 0 : 1,
|
||||
/* candidateDeviceCount= */ mDeviceItemsForSharing.size());
|
||||
if (!mTargetActiveSinks.isEmpty()) {
|
||||
if (!targetActiveSinks.isEmpty() && mTargetActiveItem != null) {
|
||||
Log.d(TAG, "handleOnBroadcastReady: automatically add source to active sinks.");
|
||||
AudioSharingUtils.addSourceToTargetSinks(mTargetActiveSinks, mBtManager);
|
||||
addSourceToTargetSinks(targetActiveSinks, mTargetActiveItem.getName());
|
||||
mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING);
|
||||
mTargetActiveSinks.clear();
|
||||
mTargetActiveItem = null;
|
||||
if (mIntentHandleStage.compareAndSet(
|
||||
StartIntentHandleStage.HANDLE_AUTO_ADD.ordinal(),
|
||||
StartIntentHandleStage.HANDLED.ordinal())
|
||||
&& mDeviceItemsForSharing.size() == 1) {
|
||||
Log.d(TAG, "handleOnBroadcastReady: auto add source to the second device");
|
||||
AudioSharingUtils.addSourceToTargetSinks(
|
||||
mGroupedConnectedDevices.getOrDefault(
|
||||
mDeviceItemsForSharing.get(0).getGroupId(), ImmutableList.of()),
|
||||
mBtManager);
|
||||
AudioSharingDeviceItem target = mDeviceItemsForSharing.get(0);
|
||||
List<BluetoothDevice> targetSinks = mGroupedConnectedDevices.getOrDefault(
|
||||
target.getGroupId(), ImmutableList.of());
|
||||
addSourceToTargetSinks(targetSinks, target.getName());
|
||||
cleanUp();
|
||||
// TODO: Add metric for auto add by intent
|
||||
return;
|
||||
@@ -611,6 +628,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
||||
StartIntentHandleStage.HANDLED.ordinal());
|
||||
if (mFragment == null) {
|
||||
Log.d(TAG, "handleOnBroadcastReady: dialog fail to show due to null fragment.");
|
||||
dismissLoadingStateDialogIfNeeded();
|
||||
cleanUp();
|
||||
return;
|
||||
}
|
||||
@@ -622,15 +640,15 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
||||
new AudioSharingDialogFragment.DialogEventListener() {
|
||||
@Override
|
||||
public void onItemClick(@NonNull AudioSharingDeviceItem item) {
|
||||
AudioSharingUtils.addSourceToTargetSinks(
|
||||
mGroupedConnectedDevices.getOrDefault(
|
||||
item.getGroupId(), ImmutableList.of()),
|
||||
mBtManager);
|
||||
List<BluetoothDevice> targetSinks = mGroupedConnectedDevices.getOrDefault(
|
||||
item.getGroupId(), ImmutableList.of());
|
||||
addSourceToTargetSinks(targetSinks, item.getName());
|
||||
cleanUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelClick() {
|
||||
dismissLoadingStateDialogIfNeeded();
|
||||
cleanUp();
|
||||
}
|
||||
};
|
||||
@@ -700,6 +718,27 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
||||
});
|
||||
}
|
||||
|
||||
private void addSourceToTargetSinks(List<BluetoothDevice> targetActiveSinks,
|
||||
@NonNull String sinkName) {
|
||||
mSinksInAdding.addAll(targetActiveSinks);
|
||||
AudioSharingUtils.addSourceToTargetSinks(targetActiveSinks, mBtManager);
|
||||
// TODO: move to res once finalized
|
||||
String loadingMessage = "Sharing with " + sinkName + "...";
|
||||
showLoadingStateDialog(loadingMessage);
|
||||
}
|
||||
|
||||
private void showLoadingStateDialog(@NonNull String loadingMessage) {
|
||||
AudioSharingUtils.postOnMainThread(mContext,
|
||||
() -> AudioSharingLoadingStateDialogFragment.show(mFragment, loadingMessage));
|
||||
}
|
||||
|
||||
private void dismissLoadingStateDialogIfNeeded() {
|
||||
if (mSinksInAdding.isEmpty()) {
|
||||
AudioSharingUtils.postOnMainThread(mContext,
|
||||
() -> AudioSharingLoadingStateDialogFragment.dismiss(mFragment));
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanUp() {
|
||||
mGroupedConnectedDevices.clear();
|
||||
mDeviceItemsForSharing.clear();
|
||||
|
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.inputmethod;
|
||||
|
||||
import static com.android.settings.inputmethod.NewKeyboardSettingsUtils.isMouse;
|
||||
import static com.android.settings.inputmethod.NewKeyboardSettingsUtils.isTouchpad;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
|
||||
/** Settings for pointer and touchpad. */
|
||||
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
||||
public class PointerColorCustomizationFragment extends DashboardFragment {
|
||||
|
||||
private static final String TAG = "PointerColorCustomizationFragment";
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.ACCESSIBILITY_POINTER_COLOR_CUSTOMIZATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.accessibility_pointer_color_customization;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.accessibility_pointer_color_customization) {
|
||||
@Override
|
||||
protected boolean isPageSearchEnabled(Context context) {
|
||||
return isTouchpad() || isMouse();
|
||||
}
|
||||
};
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.inputmethod;
|
||||
|
||||
import static com.android.settings.inputmethod.NewKeyboardSettingsUtils.isMouse;
|
||||
import static com.android.settings.inputmethod.NewKeyboardSettingsUtils.isTouchpad;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
|
||||
/** Accessibility settings for pointer and touchpad. */
|
||||
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
||||
public class PointerTouchpadFragment extends DashboardFragment {
|
||||
|
||||
private static final String TAG = "PointerTouchpadFragment";
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.ACCESSIBILITY_POINTER_TOUCHPAD;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.accessibility_pointer_and_touchpad;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.accessibility_pointer_and_touchpad) {
|
||||
@Override
|
||||
protected boolean isPageSearchEnabled(Context context) {
|
||||
return isTouchpad() || isMouse();
|
||||
}
|
||||
};
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.inputmethod;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
/** Controller that shows and updates the pointer touchpad preference. */
|
||||
public class PointerTouchpadPreferenceController extends BasePreferenceController {
|
||||
|
||||
public PointerTouchpadPreferenceController(@NonNull Context context,
|
||||
@NonNull String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
boolean isTouchpad = NewKeyboardSettingsUtils.isTouchpad();
|
||||
boolean isMouse = NewKeyboardSettingsUtils.isMouse();
|
||||
return (isTouchpad || isMouse) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
|
||||
}
|
||||
}
|
@@ -20,7 +20,7 @@ import android.app.Application
|
||||
import android.telephony.SubscriptionManager
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.android.settings.network.telephony.getSelectableSubscriptionInfoList
|
||||
import com.android.settings.network.telephony.SubscriptionRepository
|
||||
import com.android.settings.network.telephony.subscriptionsChangedFlow
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
@@ -45,7 +45,8 @@ class SubscriptionInfoListViewModel(application: Application) : AndroidViewModel
|
||||
* Getting the Selectable SubscriptionInfo List from the SubscriptionRepository's
|
||||
* getAvailableSubscriptionInfoList
|
||||
*/
|
||||
val selectableSubscriptionInfoListFlow = application.subscriptionsChangedFlow().map {
|
||||
application.getSelectableSubscriptionInfoList()
|
||||
}.stateIn(scope, SharingStarted.Eagerly, initialValue = emptyList())
|
||||
val selectableSubscriptionInfoListFlow =
|
||||
SubscriptionRepository(application)
|
||||
.selectableSubscriptionInfoListFlow()
|
||||
.stateIn(scope, SharingStarted.Eagerly, initialValue = emptyList())
|
||||
}
|
||||
|
@@ -52,7 +52,7 @@ import com.android.settings.network.helper.SelectableSubscriptions;
|
||||
import com.android.settings.network.helper.SubscriptionAnnotation;
|
||||
import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity;
|
||||
import com.android.settings.network.telephony.EuiccRacConnectivityDialogActivity;
|
||||
import com.android.settings.network.telephony.SubscriptionRepositoryKt;
|
||||
import com.android.settings.network.telephony.SubscriptionRepository;
|
||||
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -508,7 +508,7 @@ public class SubscriptionUtil {
|
||||
* @return list of user selectable subscriptions.
|
||||
*/
|
||||
public static List<SubscriptionInfo> getSelectableSubscriptionInfoList(Context context) {
|
||||
return SubscriptionRepositoryKt.getSelectableSubscriptionInfoList(context);
|
||||
return new SubscriptionRepository(context).getSelectableSubscriptionInfoList();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -42,13 +42,48 @@ private const val TAG = "SubscriptionRepository"
|
||||
class SubscriptionRepository(private val context: Context) {
|
||||
private val subscriptionManager = context.requireSubscriptionManager()
|
||||
|
||||
/** A cold flow of a list of subscriptions that are available and visible to the user. */
|
||||
fun selectableSubscriptionInfoListFlow(): Flow<List<SubscriptionInfo>> =
|
||||
context
|
||||
.subscriptionsChangedFlow()
|
||||
.map { getSelectableSubscriptionInfoList() }
|
||||
.conflate()
|
||||
.flowOn(Dispatchers.Default)
|
||||
|
||||
/**
|
||||
* Return a list of subscriptions that are available and visible to the user.
|
||||
*
|
||||
* @return list of user selectable subscriptions.
|
||||
*/
|
||||
fun getSelectableSubscriptionInfoList(): List<SubscriptionInfo> =
|
||||
context.getSelectableSubscriptionInfoList()
|
||||
fun getSelectableSubscriptionInfoList(): List<SubscriptionInfo> {
|
||||
val availableList =
|
||||
subscriptionManager.getAvailableSubscriptionInfoList() ?: return emptyList()
|
||||
val visibleList =
|
||||
availableList.filter { subInfo ->
|
||||
// Opportunistic subscriptions are considered invisible to users so they should
|
||||
// never be returned.
|
||||
SubscriptionUtil.isSubscriptionVisible(subscriptionManager, context, subInfo)
|
||||
}
|
||||
return visibleList
|
||||
.groupBy { it.groupUuid }
|
||||
.flatMap { (groupUuid, subInfos) ->
|
||||
if (groupUuid == null) {
|
||||
subInfos
|
||||
} else {
|
||||
// Multiple subscriptions in a group should only have one representative.
|
||||
// It should be the current active primary subscription if any, or the primary
|
||||
// subscription with minimum subscription id.
|
||||
subInfos
|
||||
.filter { it.simSlotIndex != SubscriptionManager.INVALID_SIM_SLOT_INDEX }
|
||||
.ifEmpty { subInfos.sortedBy { it.subscriptionId } }
|
||||
.take(1)
|
||||
}
|
||||
}
|
||||
// Matching the sorting order in
|
||||
// SubscriptionManagerService.getAvailableSubscriptionInfoList
|
||||
.sortedWith(compareBy({ it.sortableSimSlotIndex }, { it.subscriptionId }))
|
||||
.also { Log.d(TAG, "getSelectableSubscriptionInfoList: $it") }
|
||||
}
|
||||
|
||||
/** Flow of whether the subscription visible for the given [subId]. */
|
||||
fun isSubscriptionVisibleFlow(subId: Int): Flow<Boolean> {
|
||||
@@ -154,38 +189,6 @@ fun Context.phoneNumberFlow(subscriptionInfo: SubscriptionInfo): Flow<String?> =
|
||||
fun Context.subscriptionsChangedFlow(): Flow<Unit> =
|
||||
SubscriptionRepository(this).subscriptionsChangedFlow()
|
||||
|
||||
/**
|
||||
* Return a list of subscriptions that are available and visible to the user.
|
||||
*
|
||||
* @return list of user selectable subscriptions.
|
||||
*/
|
||||
fun Context.getSelectableSubscriptionInfoList(): List<SubscriptionInfo> {
|
||||
val subscriptionManager = requireSubscriptionManager()
|
||||
val availableList = subscriptionManager.getAvailableSubscriptionInfoList() ?: return emptyList()
|
||||
val visibleList = availableList.filter { subInfo ->
|
||||
// Opportunistic subscriptions are considered invisible
|
||||
// to users so they should never be returned.
|
||||
SubscriptionUtil.isSubscriptionVisible(subscriptionManager, this, subInfo)
|
||||
}
|
||||
return visibleList
|
||||
.groupBy { it.groupUuid }
|
||||
.flatMap { (groupUuid, subInfos) ->
|
||||
if (groupUuid == null) {
|
||||
subInfos
|
||||
} else {
|
||||
// Multiple subscriptions in a group should only have one representative.
|
||||
// It should be the current active primary subscription if any, or the primary
|
||||
// subscription with minimum subscription id.
|
||||
subInfos.filter { it.simSlotIndex != SubscriptionManager.INVALID_SIM_SLOT_INDEX }
|
||||
.ifEmpty { subInfos.sortedBy { it.subscriptionId } }
|
||||
.take(1)
|
||||
}
|
||||
}
|
||||
// Matching the sorting order in SubscriptionManagerService.getAvailableSubscriptionInfoList
|
||||
.sortedWith(compareBy({ it.sortableSimSlotIndex }, { it.subscriptionId }))
|
||||
.also { Log.d(TAG, "getSelectableSubscriptionInfoList: $it") }
|
||||
}
|
||||
|
||||
/** Subscription with invalid sim slot index has lowest sort order. */
|
||||
private val SubscriptionInfo.sortableSimSlotIndex: Int
|
||||
get() = if (simSlotIndex != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
|
||||
|
@@ -58,6 +58,5 @@ class ZenModeTriggerAddPreferenceController extends AbstractZenModePreferenceCon
|
||||
conditionId -> saveMode(mode -> {
|
||||
mode.setCustomModeConditionId(mContext, conditionId);
|
||||
return mode;
|
||||
// TODO: b/342156843 - Maybe jump to the corresponding schedule editing screen?
|
||||
});
|
||||
}
|
||||
|
@@ -142,9 +142,6 @@ public class ZenModesListFragment extends ZenModesFragmentBase {
|
||||
@Override
|
||||
public List<String> getNonIndexableKeys(Context context) {
|
||||
final List<String> keys = super.getNonIndexableKeys(context);
|
||||
// TODO: b/332937523 - determine if this should be removed once the preference
|
||||
// controller adds dynamic data to index
|
||||
keys.add(ZenModesListPreferenceController.KEY);
|
||||
return keys;
|
||||
}
|
||||
|
||||
|
@@ -20,6 +20,7 @@ import static com.android.internal.widget.LockPatternUtils.MIN_AUTO_PIN_REQUIREM
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.TwoStatePreference;
|
||||
|
||||
@@ -28,7 +29,6 @@ import com.android.settings.R;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import com.android.settingslib.core.lifecycle.ObservablePreferenceFragment;
|
||||
|
||||
/**
|
||||
* Preference controller for the pin_auto_confirm setting.
|
||||
@@ -40,11 +40,10 @@ public class AutoPinConfirmPreferenceController extends AbstractPreferenceContro
|
||||
|
||||
private final int mUserId;
|
||||
private final LockPatternUtils mLockPatternUtils;
|
||||
private final ObservablePreferenceFragment mParentFragment;
|
||||
private final Fragment mParentFragment;
|
||||
|
||||
public AutoPinConfirmPreferenceController(Context context, int userId,
|
||||
LockPatternUtils lockPatternUtils,
|
||||
ObservablePreferenceFragment parentFragment) {
|
||||
LockPatternUtils lockPatternUtils, Fragment parentFragment) {
|
||||
super(context);
|
||||
mUserId = userId;
|
||||
mLockPatternUtils = lockPatternUtils;
|
||||
|
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.support.actionbar;
|
||||
|
||||
import static com.android.settings.support.actionbar.HelpResourceProvider.HELP_URI_RESOURCE_KEY;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.android.settingslib.HelpUtils;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.ObservableFragment;
|
||||
import com.android.settingslib.core.lifecycle.ObservablePreferenceFragment;
|
||||
import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
|
||||
|
||||
/**
|
||||
* A controller that adds help menu to any Settings page.
|
||||
*/
|
||||
public class HelpMenuController implements LifecycleObserver, OnCreateOptionsMenu {
|
||||
|
||||
private final Fragment mHost;
|
||||
|
||||
public static void init(@NonNull ObservablePreferenceFragment host) {
|
||||
host.getSettingsLifecycle().addObserver(new HelpMenuController(host));
|
||||
}
|
||||
|
||||
public static void init(@NonNull ObservableFragment host) {
|
||||
host.getSettingsLifecycle().addObserver(new HelpMenuController(host));
|
||||
}
|
||||
|
||||
private HelpMenuController(@NonNull Fragment host) {
|
||||
mHost = host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
final Bundle arguments = mHost.getArguments();
|
||||
int helpResourceId = 0;
|
||||
if (arguments != null && arguments.containsKey(HELP_URI_RESOURCE_KEY)) {
|
||||
helpResourceId = arguments.getInt(HELP_URI_RESOURCE_KEY);
|
||||
} else if (mHost instanceof HelpResourceProvider) {
|
||||
helpResourceId = ((HelpResourceProvider) mHost).getHelpResource();
|
||||
}
|
||||
|
||||
String helpUri = null;
|
||||
if (helpResourceId != 0) {
|
||||
helpUri = mHost.getContext().getString(helpResourceId);
|
||||
}
|
||||
final Activity activity = mHost.getActivity();
|
||||
if (helpUri != null && activity != null) {
|
||||
HelpUtils.prepareHelpMenuItem(activity, menu, helpUri, mHost.getClass().getName());
|
||||
}
|
||||
}
|
||||
}
|
@@ -31,10 +31,11 @@ import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.security.Flags;
|
||||
import android.service.persistentdata.PersistentDataBlockManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -85,12 +86,12 @@ public class MainClearConfirmTest {
|
||||
MainClearConfirm mainClearConfirm = new MainClearConfirm();
|
||||
mainClearConfirm.mEraseEsims = true;
|
||||
mainClearConfirm.mContentView =
|
||||
LayoutInflater.from(mActivity).inflate(R.layout.main_clear_confirm, null);
|
||||
(GlifLayout) LayoutInflater.from(mActivity)
|
||||
.inflate(R.layout.main_clear_confirm, null);
|
||||
|
||||
mainClearConfirm.setSubtitle();
|
||||
|
||||
assertThat(((TextView) mainClearConfirm.mContentView
|
||||
.findViewById(R.id.sud_layout_description)).getText())
|
||||
assertThat(mainClearConfirm.mContentView.getDescriptionText())
|
||||
.isEqualTo(mActivity.getString(R.string.main_clear_final_desc_esim));
|
||||
}
|
||||
|
||||
@@ -99,12 +100,12 @@ public class MainClearConfirmTest {
|
||||
MainClearConfirm mainClearConfirm = new MainClearConfirm();
|
||||
mainClearConfirm.mEraseEsims = false;
|
||||
mainClearConfirm.mContentView =
|
||||
LayoutInflater.from(mActivity).inflate(R.layout.main_clear_confirm, null);
|
||||
(GlifLayout) LayoutInflater.from(mActivity)
|
||||
.inflate(R.layout.main_clear_confirm, null);
|
||||
|
||||
mainClearConfirm.setSubtitle();
|
||||
|
||||
assertThat(((TextView) mainClearConfirm.mContentView
|
||||
.findViewById(R.id.sud_layout_description)).getText())
|
||||
assertThat(mainClearConfirm.mContentView.getDescriptionText())
|
||||
.isEqualTo(mActivity.getString(R.string.main_clear_final_desc));
|
||||
}
|
||||
|
||||
|
@@ -150,7 +150,7 @@ public class AudioSharingLoadingStateDialogFragmentTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void showDialog_newMessage_dismissAndShowNewDialog() {
|
||||
public void showDialog_newMessage_keepAndUpdateDialog() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||
AudioSharingLoadingStateDialogFragment.show(mParent, TEST_MESSAGE1);
|
||||
shadowMainLooper().idle();
|
||||
@@ -163,12 +163,7 @@ public class AudioSharingLoadingStateDialogFragmentTest {
|
||||
|
||||
AudioSharingLoadingStateDialogFragment.show(mParent, TEST_MESSAGE2);
|
||||
shadowMainLooper().idle();
|
||||
assertThat(dialog.isShowing()).isFalse();
|
||||
AlertDialog newDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
assertThat(newDialog).isNotNull();
|
||||
assertThat(newDialog.isShowing()).isTrue();
|
||||
view = newDialog.findViewById(R.id.message);
|
||||
assertThat(view).isNotNull();
|
||||
assertThat(dialog.isShowing()).isTrue();
|
||||
assertThat(view.getText().toString()).isEqualTo(TEST_MESSAGE2);
|
||||
}
|
||||
}
|
||||
|
@@ -57,7 +57,9 @@ import android.util.Pair;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.Fragment;
|
||||
@@ -235,6 +237,7 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
ShadowAlertDialogCompat.reset();
|
||||
ShadowBluetoothUtils.reset();
|
||||
ShadowThreadUtils.reset();
|
||||
}
|
||||
@@ -426,6 +429,8 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
assertThat(childFragments)
|
||||
.comparingElementsUsing(CLAZZNAME_EQUALS)
|
||||
.containsExactly(AudioSharingConfirmDialogFragment.class.getName());
|
||||
|
||||
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -490,14 +495,21 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
public void onAudioSharingProfilesConnected() {}
|
||||
});
|
||||
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
verify(mBroadcast).startPrivateBroadcast();
|
||||
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||
// No loading state dialog.
|
||||
assertThat(childFragments).isEmpty();
|
||||
|
||||
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
verify(mFeatureFactory.metricsFeatureProvider)
|
||||
.action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING));
|
||||
|
||||
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||
childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||
// No audio sharing dialog.
|
||||
assertThat(childFragments).isEmpty();
|
||||
}
|
||||
|
||||
@@ -514,7 +526,13 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
|
||||
doNothing().when(mBroadcast).startPrivateBroadcast();
|
||||
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
verify(mBroadcast).startPrivateBroadcast();
|
||||
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
|
||||
AudioSharingLoadingStateDialogFragment.class.getName());
|
||||
|
||||
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
@@ -522,8 +540,12 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
verify(mFeatureFactory.metricsFeatureProvider, never())
|
||||
.action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING));
|
||||
|
||||
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||
assertThat(childFragments).isEmpty();
|
||||
childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||
// No audio sharing dialog.
|
||||
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).doesNotContain(
|
||||
AudioSharingDialogFragment.class.getName());
|
||||
|
||||
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -534,23 +556,42 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
|
||||
when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
|
||||
doNothing().when(mBroadcast).startPrivateBroadcast();
|
||||
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
|
||||
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
|
||||
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
verify(mBroadcast).startPrivateBroadcast();
|
||||
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
|
||||
AudioSharingLoadingStateDialogFragment.class.getName());
|
||||
AudioSharingLoadingStateDialogFragment loadingFragment =
|
||||
(AudioSharingLoadingStateDialogFragment) Iterables.getOnlyElement(childFragments);
|
||||
// TODO: use string res once finalized
|
||||
String expectedMessage = "Starting audio stream...";
|
||||
checkLoadingStateDialogMessage(loadingFragment, expectedMessage);
|
||||
|
||||
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
verify(mFeatureFactory.metricsFeatureProvider)
|
||||
.action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING));
|
||||
// TODO: use string res once finalized
|
||||
expectedMessage = "Sharing with " + TEST_DEVICE_NAME2 + "...";
|
||||
checkLoadingStateDialogMessage(loadingFragment, expectedMessage);
|
||||
|
||||
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||
childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||
assertThat(childFragments)
|
||||
.comparingElementsUsing(CLAZZNAME_EQUALS)
|
||||
.containsExactly(AudioSharingDialogFragment.class.getName());
|
||||
.containsExactly(AudioSharingDialogFragment.class.getName(),
|
||||
AudioSharingLoadingStateDialogFragment.class.getName());
|
||||
|
||||
AudioSharingDialogFragment fragment =
|
||||
(AudioSharingDialogFragment) Iterables.getOnlyElement(childFragments);
|
||||
Pair<Integer, Object>[] eventData = fragment.getEventData();
|
||||
Pair<Integer, Object>[] eventData = new Pair[0];
|
||||
for (Fragment fragment : childFragments) {
|
||||
if (fragment instanceof AudioSharingDialogFragment) {
|
||||
eventData = ((AudioSharingDialogFragment) fragment).getEventData();
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertThat(eventData)
|
||||
.asList()
|
||||
.containsExactly(
|
||||
@@ -570,6 +611,8 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT
|
||||
.ordinal(),
|
||||
1));
|
||||
|
||||
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -582,6 +625,8 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
|
||||
doNothing().when(mBroadcast).startPrivateBroadcast();
|
||||
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
verify(mBroadcast).startPrivateBroadcast();
|
||||
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
@@ -597,6 +642,17 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
|
||||
verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
|
||||
assertThat(dialog.isShowing()).isFalse();
|
||||
// Loading state dialog shows sharing state for the user chosen sink.
|
||||
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
|
||||
AudioSharingLoadingStateDialogFragment.class.getName());
|
||||
AudioSharingLoadingStateDialogFragment loadingFragment =
|
||||
(AudioSharingLoadingStateDialogFragment) Iterables.getOnlyElement(childFragments);
|
||||
// TODO: use string res once finalized
|
||||
String expectedMessage = "Sharing with " + TEST_DEVICE_NAME1 + "...";
|
||||
checkLoadingStateDialogMessage(loadingFragment, expectedMessage);
|
||||
|
||||
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -609,6 +665,8 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
|
||||
doNothing().when(mBroadcast).startPrivateBroadcast();
|
||||
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
verify(mBroadcast).startPrivateBroadcast();
|
||||
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
@@ -624,10 +682,21 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
|
||||
verify(mAssistant, never()).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
|
||||
assertThat(dialog.isShowing()).isFalse();
|
||||
// Loading state dialog shows sharing state for the auto add active sink.
|
||||
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
|
||||
AudioSharingLoadingStateDialogFragment.class.getName());
|
||||
AudioSharingLoadingStateDialogFragment loadingFragment =
|
||||
(AudioSharingLoadingStateDialogFragment) Iterables.getOnlyElement(childFragments);
|
||||
// TODO: use string res once finalized
|
||||
String expectedMessage = "Sharing with " + TEST_DEVICE_NAME2 + "...";
|
||||
checkLoadingStateDialogMessage(loadingFragment, expectedMessage);
|
||||
|
||||
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBluetoothLeBroadcastCallbacks_updateSwitch() {
|
||||
public void testBroadcastCallbacks_updateSwitch() {
|
||||
mOnAudioSharingStateChanged = false;
|
||||
mSwitchBar.setChecked(false);
|
||||
when(mBroadcast.isEnabled(any())).thenReturn(false);
|
||||
@@ -673,7 +742,7 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBluetoothLeBroadcastCallbacks_doNothing() {
|
||||
public void testBroadcastCallbacks_doNothing() {
|
||||
mController.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, mMetadata);
|
||||
mController.mBroadcastCallback.onBroadcastUpdated(/* reason= */ 1, /* broadcastId= */ 1);
|
||||
mController.mBroadcastCallback.onPlaybackStarted(/* reason= */ 1, /* broadcastId= */ 1);
|
||||
@@ -685,7 +754,7 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBluetoothLeBroadcastAssistantCallbacks_logAction() {
|
||||
public void testAssistantCallbacks_onSourceAddFailed_logAction() {
|
||||
mController.mBroadcastAssistantCallback.onSourceAddFailed(
|
||||
mDevice1, mMetadata, /* reason= */ 1);
|
||||
verify(mFeatureFactory.metricsFeatureProvider)
|
||||
@@ -696,7 +765,24 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBluetoothLeBroadcastAssistantCallbacks_doNothing() {
|
||||
public void testAssistantCallbacks_onReceiveStateChanged_dismissLoadingDialog() {
|
||||
AudioSharingLoadingStateDialogFragment.show(mParentFragment, TEST_DEVICE_NAME1);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
|
||||
AudioSharingLoadingStateDialogFragment.class.getName());
|
||||
|
||||
BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
|
||||
when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L));
|
||||
mController.mBroadcastAssistantCallback.onReceiveStateChanged(mDevice1, /* sourceId= */ 1,
|
||||
state);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||
assertThat(childFragments).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssistantCallbacks_doNothing() {
|
||||
BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
|
||||
|
||||
// Do nothing
|
||||
@@ -784,7 +870,7 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
@Test
|
||||
public void handleStartAudioSharingFromIntent_flagOff_doNothing() {
|
||||
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||
setUpStartSharingIntent();
|
||||
var unused = setUpFragmentWithStartSharingIntent();
|
||||
mController.onStart(mLifecycleOwner);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
@@ -795,7 +881,7 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
public void handleStartAudioSharingFromIntent_profileNotReady_doNothing() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||
when(mAssistant.isProfileReady()).thenReturn(false);
|
||||
setUpStartSharingIntent();
|
||||
var unused = setUpFragmentWithStartSharingIntent();
|
||||
mController.onServiceConnected();
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
@@ -817,13 +903,16 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
when(mBtnView.isEnabled()).thenReturn(true);
|
||||
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
|
||||
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
|
||||
setUpStartSharingIntent();
|
||||
Fragment parentFragment = setUpFragmentWithStartSharingIntent();
|
||||
mController.onServiceConnected();
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
verify(mSwitchBar).setChecked(true);
|
||||
doNothing().when(mBroadcast).startPrivateBroadcast();
|
||||
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
verify(mBroadcast).startPrivateBroadcast();
|
||||
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
@@ -831,11 +920,21 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
.action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING));
|
||||
verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
|
||||
verify(mAssistant).addSource(mDevice2, mMetadata, /* isGroupOp= */ false);
|
||||
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||
assertThat(childFragments).isEmpty();
|
||||
List<Fragment> childFragments = parentFragment.getChildFragmentManager().getFragments();
|
||||
// Skip audio sharing dialog.
|
||||
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
|
||||
AudioSharingLoadingStateDialogFragment.class.getName());
|
||||
// The loading state dialog shows sharing state for the auto add second sink.
|
||||
AudioSharingLoadingStateDialogFragment loadingFragment =
|
||||
(AudioSharingLoadingStateDialogFragment) Iterables.getOnlyElement(childFragments);
|
||||
// TODO: use string res once finalized
|
||||
String expectedMessage = "Sharing with " + TEST_DEVICE_NAME1 + "...";
|
||||
checkLoadingStateDialogMessage(loadingFragment, expectedMessage);
|
||||
|
||||
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
|
||||
}
|
||||
|
||||
private void setUpStartSharingIntent() {
|
||||
private Fragment setUpFragmentWithStartSharingIntent() {
|
||||
Bundle args = new Bundle();
|
||||
args.putBoolean(EXTRA_START_LE_AUDIO_SHARING, true);
|
||||
Intent intent = new Intent();
|
||||
@@ -849,5 +948,15 @@ public class AudioSharingSwitchBarControllerTest {
|
||||
.get();
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
mController.init(fragment);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
private void checkLoadingStateDialogMessage(
|
||||
@NonNull AudioSharingLoadingStateDialogFragment fragment,
|
||||
@NonNull String expectedMessage) {
|
||||
TextView loadingMessage = fragment.getDialog() == null ? null
|
||||
: fragment.getDialog().findViewById(R.id.message);
|
||||
assertThat(loadingMessage).isNotNull();
|
||||
assertThat(loadingMessage.getText().toString()).isEqualTo(expectedMessage);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.inputmethod;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class PointerTouchpadFragmentTest {
|
||||
|
||||
private PointerTouchpadFragment mFragment;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mFragment = new PointerTouchpadFragment();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPreferenceScreenResId_isPointerTouchpad() {
|
||||
assertThat(mFragment.getPreferenceScreenResId())
|
||||
.isEqualTo(R.xml.accessibility_pointer_and_touchpad);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMetricsCategory_returnsCorrectCategory() {
|
||||
assertThat(mFragment.getMetricsCategory()).isEqualTo(
|
||||
SettingsEnums.ACCESSIBILITY_POINTER_TOUCHPAD);
|
||||
}
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.inputmethod;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.InputDevice;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.testutils.shadow.ShadowInputDevice;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
/** Tests for {@link PointerTouchpadPreferenceController}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {
|
||||
ShadowInputDevice.class,
|
||||
})
|
||||
public final class PointerTouchpadPreferenceControllerTest {
|
||||
@Rule public MockitoRule rule = MockitoJUnit.rule();
|
||||
|
||||
private PointerTouchpadPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void initObjects() {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
mController = new PointerTouchpadPreferenceController(context, "pointer_touchpad");
|
||||
ShadowInputDevice.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailableStatus_noTouchpadOrMouseConditionallyUnavailable() {
|
||||
int deviceId = 1;
|
||||
ShadowInputDevice.sDeviceIds = new int[]{deviceId};
|
||||
InputDevice device = ShadowInputDevice.makeInputDevicebyIdWithSources(deviceId,
|
||||
InputDevice.SOURCE_BLUETOOTH_STYLUS);
|
||||
ShadowInputDevice.addDevice(deviceId, device);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_isTouchpadAvailable() {
|
||||
int deviceId = 1;
|
||||
ShadowInputDevice.sDeviceIds = new int[]{deviceId};
|
||||
InputDevice device = ShadowInputDevice.makeInputDevicebyIdWithSources(deviceId,
|
||||
InputDevice.SOURCE_TOUCHPAD);
|
||||
ShadowInputDevice.addDevice(deviceId, device);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus())
|
||||
.isEqualTo(BasePreferenceController.AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_isMouseAvailable() {
|
||||
int deviceId = 1;
|
||||
ShadowInputDevice.sDeviceIds = new int[]{deviceId};
|
||||
InputDevice device = ShadowInputDevice.makeInputDevicebyIdWithSources(deviceId,
|
||||
InputDevice.SOURCE_MOUSE);
|
||||
ShadowInputDevice.addDevice(deviceId, device);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus())
|
||||
.isEqualTo(BasePreferenceController.AVAILABLE);
|
||||
}
|
||||
}
|
@@ -22,11 +22,11 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settingslib.core.lifecycle.ObservablePreferenceFragment;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -41,7 +41,7 @@ public class AutoPinConfirmPreferenceControllerTest {
|
||||
@Mock
|
||||
private LockPatternUtils mLockPatternUtils;
|
||||
@Mock
|
||||
private ObservablePreferenceFragment mParentFragment;
|
||||
private Fragment mParentFragment;
|
||||
private AutoPinConfirmPreferenceController mController;
|
||||
private SwitchPreference mPreference;
|
||||
|
||||
|
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.support.actionbar;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.android.settingslib.core.lifecycle.ObservablePreferenceFragment;
|
||||
|
||||
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;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class HelpMenuControllerTest {
|
||||
|
||||
@Mock
|
||||
private Context mContext;
|
||||
private TestFragment mHost;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mHost = spy(new TestFragment());
|
||||
doReturn(mContext).when(mHost).getContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCreateOptionsMenu_withArgumentOverride_shouldPrepareHelpUsingOverride() {
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putInt(HelpResourceProvider.HELP_URI_RESOURCE_KEY, 123);
|
||||
mHost.setArguments(bundle);
|
||||
|
||||
HelpMenuController.init(mHost);
|
||||
|
||||
mHost.getSettingsLifecycle().onCreateOptionsMenu(null /* menu */, null /* inflater */);
|
||||
|
||||
verify(mContext).getString(123);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCreateOptionsMenu_noArgumentOverride_shouldPrepareHelpUsingProvider() {
|
||||
HelpMenuController.init(mHost);
|
||||
|
||||
mHost.getSettingsLifecycle().onCreateOptionsMenu(null /* menu */, null /* inflater */);
|
||||
|
||||
verify(mContext).getString(mHost.getHelpResource());
|
||||
}
|
||||
|
||||
private static class TestFragment extends ObservablePreferenceFragment
|
||||
implements HelpResourceProvider {
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
}
|
||||
}
|
||||
}
|
@@ -120,7 +120,7 @@ class SubscriptionRepositoryTest {
|
||||
)
|
||||
}
|
||||
|
||||
val subInfos = context.getSelectableSubscriptionInfoList()
|
||||
val subInfos = repository.getSelectableSubscriptionInfoList()
|
||||
|
||||
assertThat(subInfos.map { it.simSlotIndex })
|
||||
.containsExactly(SIM_SLOT_INDEX_0, SIM_SLOT_INDEX_1).inOrder()
|
||||
@@ -141,7 +141,7 @@ class SubscriptionRepositoryTest {
|
||||
)
|
||||
}
|
||||
|
||||
val subInfos = context.getSelectableSubscriptionInfoList()
|
||||
val subInfos = repository.getSelectableSubscriptionInfoList()
|
||||
|
||||
assertThat(subInfos.map { it.simSlotIndex })
|
||||
.containsExactly(SIM_SLOT_INDEX_1, SubscriptionManager.INVALID_SIM_SLOT_INDEX).inOrder()
|
||||
@@ -164,7 +164,7 @@ class SubscriptionRepositoryTest {
|
||||
)
|
||||
}
|
||||
|
||||
val subInfos = context.getSelectableSubscriptionInfoList()
|
||||
val subInfos = repository.getSelectableSubscriptionInfoList()
|
||||
|
||||
assertThat(subInfos.map { it.subscriptionId }).containsExactly(SUB_ID_IN_SLOT_0)
|
||||
}
|
||||
@@ -184,7 +184,7 @@ class SubscriptionRepositoryTest {
|
||||
)
|
||||
}
|
||||
|
||||
val subInfos = context.getSelectableSubscriptionInfoList()
|
||||
val subInfos = repository.getSelectableSubscriptionInfoList()
|
||||
|
||||
assertThat(subInfos.map { it.subscriptionId }).containsExactly(SUB_ID_3_NOT_IN_SLOT)
|
||||
}
|
||||
|
Reference in New Issue
Block a user