Snap for 12809477 from 7b7f72d902 to 25Q2-release
Change-Id: Id0edeea642b5a121f56fb57fd8ee45fceacc0666
This commit is contained in:
@@ -36,8 +36,7 @@
|
|||||||
android:id="@+id/battery_chart_group"
|
android:id="@+id/battery_chart_group"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical">
|
||||||
android:alpha="0">
|
|
||||||
<com.android.settings.fuelgauge.batteryusage.BatteryChartView
|
<com.android.settings.fuelgauge.batteryusage.BatteryChartView
|
||||||
android:id="@+id/daily_battery_chart"
|
android:id="@+id/daily_battery_chart"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -53,7 +52,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/chartview_layout_height"
|
android:layout_height="@dimen/chartview_layout_height"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:visibility="visible"
|
android:visibility="gone"
|
||||||
android:contentDescription="@string/hourly_battery_usage_chart"
|
android:contentDescription="@string/hourly_battery_usage_chart"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
settings:textColor="?android:attr/textColorSecondary" />
|
settings:textColor="?android:attr/textColorSecondary" />
|
||||||
|
|||||||
@@ -16,8 +16,9 @@
|
|||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/display_topology_pane_holder"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
android:layout_height="160dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:paddingHorizontal="@dimen/display_topology_pane_margin"
|
android:paddingHorizontal="@dimen/display_topology_pane_margin"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
@@ -27,14 +28,14 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:src="@drawable/display_topology_background"/>
|
android:src="@drawable/display_topology_background"/>
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/display_topology_container"
|
android:id="@+id/display_topology_pane_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"/>
|
android:layout_height="match_parent"/>
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/topology_hint"
|
android:id="@+id/topology_hint"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_gravity="top|center_horizontal"
|
android:layout_gravity="top|center_horizontal"
|
||||||
android:paddingTop="10dp"
|
android:paddingBottom="10dp"
|
||||||
android:text="@string/external_display_topology_hint"/>
|
android:paddingTop="10dp" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|||||||
@@ -96,17 +96,6 @@ public class AccessibilityHearingAidsFragment extends AccessibilityShortcutPrefe
|
|||||||
return mFeatureName;
|
return mFeatureName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ComponentName getTileComponentName() {
|
|
||||||
return AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_TILE_COMPONENT_NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected CharSequence getTileTooltipContent(int type) {
|
|
||||||
// No tooltip to be shown
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean showGeneralCategory() {
|
protected boolean showGeneralCategory() {
|
||||||
// Have static preference under dynamically created PreferenceCategory KEY_GENERAL_CATEGORY.
|
// Have static preference under dynamically created PreferenceCategory KEY_GENERAL_CATEGORY.
|
||||||
|
|||||||
@@ -20,10 +20,8 @@ import static com.android.internal.accessibility.common.ShortcutConstants.UserSh
|
|||||||
import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
|
import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
|
||||||
import static com.android.settings.accessibility.AccessibilityUtil.getShortcutSummaryList;
|
import static com.android.settings.accessibility.AccessibilityUtil.getShortcutSummaryList;
|
||||||
import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_GENERAL_CATEGORY;
|
import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_GENERAL_CATEGORY;
|
||||||
import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_SAVED_QS_TOOLTIP_TYPE;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
@@ -44,7 +42,6 @@ import androidx.preference.PreferenceScreen;
|
|||||||
|
|
||||||
import com.android.internal.accessibility.common.ShortcutConstants;
|
import com.android.internal.accessibility.common.ShortcutConstants;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
|
|
||||||
import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
|
import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
|
||||||
import com.android.settings.dashboard.RestrictedDashboardFragment;
|
import com.android.settings.dashboard.RestrictedDashboardFragment;
|
||||||
|
|
||||||
@@ -60,16 +57,12 @@ import java.util.Set;
|
|||||||
public abstract class AccessibilityShortcutPreferenceFragment extends RestrictedDashboardFragment
|
public abstract class AccessibilityShortcutPreferenceFragment extends RestrictedDashboardFragment
|
||||||
implements ShortcutPreference.OnClickCallback {
|
implements ShortcutPreference.OnClickCallback {
|
||||||
private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
|
private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
|
||||||
protected static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
|
|
||||||
|
|
||||||
protected ShortcutPreference mShortcutPreference;
|
protected ShortcutPreference mShortcutPreference;
|
||||||
protected Dialog mDialog;
|
protected Dialog mDialog;
|
||||||
private AccessibilityManager.TouchExplorationStateChangeListener
|
private AccessibilityManager.TouchExplorationStateChangeListener
|
||||||
mTouchExplorationStateChangeListener;
|
mTouchExplorationStateChangeListener;
|
||||||
private AccessibilitySettingsContentObserver mSettingsContentObserver;
|
private AccessibilitySettingsContentObserver mSettingsContentObserver;
|
||||||
private AccessibilityQuickSettingsTooltipWindow mTooltipWindow;
|
|
||||||
private boolean mNeedsQSTooltipReshow = false;
|
|
||||||
private int mNeedsQSTooltipType = QuickSettingsTooltipType.GUIDE_TO_EDIT;
|
|
||||||
|
|
||||||
public AccessibilityShortcutPreferenceFragment(String restrictionKey) {
|
public AccessibilityShortcutPreferenceFragment(String restrictionKey) {
|
||||||
super(restrictionKey);
|
super(restrictionKey);
|
||||||
@@ -81,26 +74,10 @@ public abstract class AccessibilityShortcutPreferenceFragment extends Restricted
|
|||||||
/** Returns the accessibility feature name. */
|
/** Returns the accessibility feature name. */
|
||||||
protected abstract CharSequence getLabelName();
|
protected abstract CharSequence getLabelName();
|
||||||
|
|
||||||
/** Returns the accessibility tile component name. */
|
|
||||||
protected abstract ComponentName getTileComponentName();
|
|
||||||
|
|
||||||
/** Returns the accessibility tile tooltip content. */
|
|
||||||
protected abstract CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Restore the user shortcut type and tooltip.
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
|
|
||||||
mNeedsQSTooltipReshow = savedInstanceState.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW);
|
|
||||||
}
|
|
||||||
if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_TYPE)) {
|
|
||||||
mNeedsQSTooltipType = savedInstanceState.getInt(KEY_SAVED_QS_TOOLTIP_TYPE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final int resId = getPreferenceScreenResId();
|
final int resId = getPreferenceScreenResId();
|
||||||
if (resId <= 0) {
|
if (resId <= 0) {
|
||||||
final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(
|
final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(
|
||||||
@@ -141,21 +118,6 @@ public abstract class AccessibilityShortcutPreferenceFragment extends Restricted
|
|||||||
return super.onCreateView(inflater, container, savedInstanceState);
|
return super.onCreateView(inflater, container, savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
|
|
||||||
// Reshow tooltip when activity recreate, such as rotate device.
|
|
||||||
if (mNeedsQSTooltipReshow) {
|
|
||||||
view.post(() -> {
|
|
||||||
final Activity activity = getActivity();
|
|
||||||
if (activity != null && !activity.isFinishing()) {
|
|
||||||
showQuickSettingsTooltipIfNeeded();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
@@ -177,16 +139,6 @@ public abstract class AccessibilityShortcutPreferenceFragment extends Restricted
|
|||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
|
|
||||||
if (mNeedsQSTooltipReshow || isTooltipWindowShowing) {
|
|
||||||
outState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true);
|
|
||||||
outState.putInt(KEY_SAVED_QS_TOOLTIP_TYPE, mNeedsQSTooltipType);
|
|
||||||
}
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(int dialogId) {
|
public Dialog onCreateDialog(int dialogId) {
|
||||||
switch (dialogId) {
|
switch (dialogId) {
|
||||||
@@ -289,7 +241,6 @@ public abstract class AccessibilityShortcutPreferenceFragment extends Restricted
|
|||||||
*/
|
*/
|
||||||
private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) {
|
private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) {
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
showQuickSettingsTooltipIfNeeded();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -362,26 +313,6 @@ public abstract class AccessibilityShortcutPreferenceFragment extends Restricted
|
|||||||
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
|
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows the quick settings tooltip if the quick settings feature is assigned. The tooltip only
|
|
||||||
* shows once.
|
|
||||||
*
|
|
||||||
* @param type The quick settings tooltip type
|
|
||||||
*/
|
|
||||||
protected void showQuickSettingsTooltipIfNeeded(@QuickSettingsTooltipType int type) {
|
|
||||||
mNeedsQSTooltipType = type;
|
|
||||||
showQuickSettingsTooltipIfNeeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated made obsolete by quick settings rollout.
|
|
||||||
*
|
|
||||||
* (TODO 367414968: finish removal.)
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
private void showQuickSettingsTooltipIfNeeded() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the user preferred shortcut types or the default shortcut types if not set
|
* Returns the user preferred shortcut types or the default shortcut types if not set
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -71,11 +71,6 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onProcessArguments(Bundle arguments) {
|
protected void onProcessArguments(Bundle arguments) {
|
||||||
super.onProcessArguments(arguments);
|
super.onProcessArguments(arguments);
|
||||||
|
|||||||
@@ -107,10 +107,6 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
|
|||||||
if (enabled == isEnabled) {
|
if (enabled == isEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enabled) {
|
|
||||||
showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
|
|
||||||
}
|
|
||||||
logAccessibilityServiceEnabled(mComponentName, enabled);
|
logAccessibilityServiceEnabled(mComponentName, enabled);
|
||||||
Settings.Secure.putInt(getContentResolver(), ENABLED, enabled ? ON : OFF);
|
Settings.Secure.putInt(getContentResolver(), ENABLED, enabled ? ON : OFF);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,10 +158,6 @@ public class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePreferenceF
|
|||||||
if (enabled == isEnabled) {
|
if (enabled == isEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enabled) {
|
|
||||||
showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
|
|
||||||
}
|
|
||||||
logAccessibilityServiceEnabled(mComponentName, enabled);
|
logAccessibilityServiceEnabled(mComponentName, enabled);
|
||||||
Settings.Secure.putInt(getContentResolver(), ENABLED, enabled ? ON : OFF);
|
Settings.Secure.putInt(getContentResolver(), ENABLED, enabled ? ON : OFF);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import static com.android.internal.accessibility.common.ShortcutConstants.UserSh
|
|||||||
import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
|
import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
|
||||||
import static com.android.settings.accessibility.AccessibilityUtil.getShortcutSummaryList;
|
import static com.android.settings.accessibility.AccessibilityUtil.getShortcutSummaryList;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
@@ -85,8 +84,6 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
|
|||||||
public static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
|
public static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
|
||||||
protected static final String KEY_TOP_INTRO_PREFERENCE = "top_intro";
|
protected static final String KEY_TOP_INTRO_PREFERENCE = "top_intro";
|
||||||
protected static final String KEY_HTML_DESCRIPTION_PREFERENCE = "html_description";
|
protected static final String KEY_HTML_DESCRIPTION_PREFERENCE = "html_description";
|
||||||
protected static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
|
|
||||||
protected static final String KEY_SAVED_QS_TOOLTIP_TYPE = "qs_tooltip_type";
|
|
||||||
protected static final String KEY_ANIMATED_IMAGE = "animated_image";
|
protected static final String KEY_ANIMATED_IMAGE = "animated_image";
|
||||||
// For html description of accessibility service, must follow the rule, such as
|
// For html description of accessibility service, must follow the rule, such as
|
||||||
// <img src="R.drawable.fileName"/>, a11y settings will get the resources successfully.
|
// <img src="R.drawable.fileName"/>, a11y settings will get the resources successfully.
|
||||||
@@ -112,10 +109,6 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
|
|||||||
private CharSequence mDescription;
|
private CharSequence mDescription;
|
||||||
private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
|
private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
|
||||||
private AccessibilitySettingsContentObserver mSettingsContentObserver;
|
private AccessibilitySettingsContentObserver mSettingsContentObserver;
|
||||||
|
|
||||||
private AccessibilityQuickSettingsTooltipWindow mTooltipWindow;
|
|
||||||
private boolean mNeedsQSTooltipReshow = false;
|
|
||||||
private int mNeedsQSTooltipType = QuickSettingsTooltipType.GUIDE_TO_EDIT;
|
|
||||||
private ImageView mImageGetterCacheView;
|
private ImageView mImageGetterCacheView;
|
||||||
protected final Html.ImageGetter mImageGetter = (String str) -> {
|
protected final Html.ImageGetter mImageGetter = (String str) -> {
|
||||||
if (str != null && str.startsWith(IMG_PREFIX)) {
|
if (str != null && str.startsWith(IMG_PREFIX)) {
|
||||||
@@ -133,16 +126,6 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
onProcessArguments(getArguments());
|
onProcessArguments(getArguments());
|
||||||
// Restore the user shortcut type and tooltip.
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
|
|
||||||
mNeedsQSTooltipReshow = savedInstanceState.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW);
|
|
||||||
}
|
|
||||||
if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_TYPE)) {
|
|
||||||
mNeedsQSTooltipType = savedInstanceState.getInt(KEY_SAVED_QS_TOOLTIP_TYPE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final int resId = getPreferenceScreenResId();
|
final int resId = getPreferenceScreenResId();
|
||||||
if (resId <= 0) {
|
if (resId <= 0) {
|
||||||
final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(
|
final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(
|
||||||
@@ -227,16 +210,6 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
|
|||||||
final SettingsMainSwitchBar switchBar = settingsActivity.getSwitchBar();
|
final SettingsMainSwitchBar switchBar = settingsActivity.getSwitchBar();
|
||||||
switchBar.hide();
|
switchBar.hide();
|
||||||
|
|
||||||
// Reshow tooltip when activity recreate, such as rotate device.
|
|
||||||
if (mNeedsQSTooltipReshow) {
|
|
||||||
view.post(() -> {
|
|
||||||
final Activity activity = getActivity();
|
|
||||||
if (activity != null && !activity.isFinishing()) {
|
|
||||||
showQuickSettingsTooltipIfNeeded();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
writeConfigDefaultAccessibilityServiceIntoShortcutTargetServiceIfNeeded(getContext());
|
writeConfigDefaultAccessibilityServiceIntoShortcutTargetServiceIfNeeded(getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,24 +234,10 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
|
|||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
|
|
||||||
if (mNeedsQSTooltipReshow || isTooltipWindowShowing) {
|
|
||||||
outState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true);
|
|
||||||
outState.putInt(KEY_SAVED_QS_TOOLTIP_TYPE, mNeedsQSTooltipType);
|
|
||||||
}
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
removeActionBarToggleSwitch();
|
removeActionBarToggleSwitch();
|
||||||
final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
|
|
||||||
if (isTooltipWindowShowing) {
|
|
||||||
mTooltipWindow.dismiss();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -314,7 +273,12 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
|
|||||||
/** Returns the accessibility tile component name. */
|
/** Returns the accessibility tile component name. */
|
||||||
abstract ComponentName getTileComponentName();
|
abstract ComponentName getTileComponentName();
|
||||||
|
|
||||||
/** Returns the accessibility tile tooltip content. */
|
/** Returns the accessibility tile component name.
|
||||||
|
*
|
||||||
|
* @deprecated unused, as this class no longer displays tile tooltips.
|
||||||
|
*
|
||||||
|
* (TODO 367414968: finish removal.)*/
|
||||||
|
@Deprecated
|
||||||
abstract CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type);
|
abstract CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type);
|
||||||
|
|
||||||
protected void updateToggleServiceTitle(SettingsMainSwitchPreference switchPreference) {
|
protected void updateToggleServiceTitle(SettingsMainSwitchPreference switchPreference) {
|
||||||
@@ -332,9 +296,6 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
|
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
|
||||||
if (enabled) {
|
|
||||||
showQuickSettingsTooltipIfNeeded();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onInstallSwitchPreferenceToggleSwitch() {
|
protected void onInstallSwitchPreferenceToggleSwitch() {
|
||||||
@@ -646,7 +607,6 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
|
|||||||
*/
|
*/
|
||||||
private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) {
|
private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) {
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
showQuickSettingsTooltipIfNeeded();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateShortcutPreferenceData() {
|
protected void updateShortcutPreferenceData() {
|
||||||
@@ -737,26 +697,6 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows the quick settings tooltip if the quick settings feature is assigned. The tooltip only
|
|
||||||
* shows once.
|
|
||||||
*
|
|
||||||
* @param type The quick settings tooltip type
|
|
||||||
*/
|
|
||||||
protected void showQuickSettingsTooltipIfNeeded(@QuickSettingsTooltipType int type) {
|
|
||||||
mNeedsQSTooltipType = type;
|
|
||||||
showQuickSettingsTooltipIfNeeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated made obsolete by quick settings rollout.
|
|
||||||
*
|
|
||||||
* (TODO 367414968: finish removal.)
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
private void showQuickSettingsTooltipIfNeeded() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns user visible name of the tile by given {@link ComponentName}. */
|
/** Returns user visible name of the tile by given {@link ComponentName}. */
|
||||||
protected CharSequence loadTileLabel(Context context, ComponentName componentName) {
|
protected CharSequence loadTileLabel(Context context, ComponentName componentName) {
|
||||||
final PackageManager packageManager = context.getPackageManager();
|
final PackageManager packageManager = context.getPackageManager();
|
||||||
|
|||||||
@@ -149,9 +149,6 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
|
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
|
||||||
if (enabled) {
|
|
||||||
showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
|
|
||||||
}
|
|
||||||
logAccessibilityServiceEnabled(mComponentName, enabled);
|
logAccessibilityServiceEnabled(mComponentName, enabled);
|
||||||
mColorDisplayManager.setReduceBrightColorsActivated(enabled);
|
mColorDisplayManager.setReduceBrightColorsActivated(enabled);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,32 @@
|
|||||||
|
|
||||||
package com.android.settings.connecteddevice.display
|
package com.android.settings.connecteddevice.display
|
||||||
|
|
||||||
|
import android.app.WallpaperManager
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.Color
|
||||||
import android.graphics.Point
|
import android.graphics.Point
|
||||||
import android.graphics.PointF
|
import android.graphics.PointF
|
||||||
import android.graphics.RectF
|
import android.graphics.RectF
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.hardware.display.DisplayManager
|
||||||
|
import android.hardware.display.DisplayTopology
|
||||||
|
import android.hardware.display.DisplayTopology.TreeNode.POSITION_BOTTOM
|
||||||
|
import android.hardware.display.DisplayTopology.TreeNode.POSITION_LEFT
|
||||||
|
import android.hardware.display.DisplayTopology.TreeNode.POSITION_RIGHT
|
||||||
|
import android.hardware.display.DisplayTopology.TreeNode.POSITION_TOP
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.ViewTreeObserver
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceViewHolder
|
||||||
|
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
@@ -143,11 +161,27 @@ class TopologyScale(
|
|||||||
|
|
||||||
const val PREFERENCE_KEY = "display_topology_preference"
|
const val PREFERENCE_KEY = "display_topology_preference"
|
||||||
|
|
||||||
|
/** dp of padding on each side of a display block. */
|
||||||
|
const val BLOCK_PADDING = 2
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DisplayTopologyPreference allows the user to change the display topology
|
* DisplayTopologyPreference allows the user to change the display topology
|
||||||
* when there is one or more extended display attached.
|
* when there is one or more extended display attached.
|
||||||
*/
|
*/
|
||||||
class DisplayTopologyPreference(context : Context) : Preference(context) {
|
class DisplayTopologyPreference(context : Context)
|
||||||
|
: Preference(context), ViewTreeObserver.OnGlobalLayoutListener {
|
||||||
|
@VisibleForTesting lateinit var mPaneContent : FrameLayout
|
||||||
|
@VisibleForTesting lateinit var mPaneHolder : FrameLayout
|
||||||
|
@VisibleForTesting lateinit var mTopologyHint : TextView
|
||||||
|
|
||||||
|
@VisibleForTesting var injector : Injector
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is needed to prevent a repopulation of the pane causing another
|
||||||
|
* relayout and vice-versa ad infinitum.
|
||||||
|
*/
|
||||||
|
private var mPaneNeedsRefresh = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
layoutResource = R.layout.display_topology_preference
|
layoutResource = R.layout.display_topology_preference
|
||||||
|
|
||||||
@@ -155,5 +189,108 @@ class DisplayTopologyPreference(context : Context) : Preference(context) {
|
|||||||
isSelectable = false
|
isSelectable = false
|
||||||
|
|
||||||
key = PREFERENCE_KEY
|
key = PREFERENCE_KEY
|
||||||
|
|
||||||
|
injector = Injector()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: PreferenceViewHolder) {
|
||||||
|
super.onBindViewHolder(holder)
|
||||||
|
|
||||||
|
val newPane = holder.findViewById(R.id.display_topology_pane_content) as FrameLayout
|
||||||
|
if (this::mPaneContent.isInitialized) {
|
||||||
|
if (newPane == mPaneContent) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mPaneContent.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||||
|
}
|
||||||
|
mPaneContent = newPane
|
||||||
|
mPaneHolder = holder.itemView as FrameLayout
|
||||||
|
mTopologyHint = holder.findViewById(R.id.topology_hint) as TextView
|
||||||
|
mPaneContent.viewTreeObserver.addOnGlobalLayoutListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttached() {
|
||||||
|
// We don't know if topology changes happened when we were detached, as it is impossible to
|
||||||
|
// listen at that time (we must remove listeners when detaching). Setting this flag makes
|
||||||
|
// the following onGlobalLayout call refresh the pane.
|
||||||
|
mPaneNeedsRefresh = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onGlobalLayout() {
|
||||||
|
if (mPaneNeedsRefresh) {
|
||||||
|
mPaneNeedsRefresh = false
|
||||||
|
refreshPane()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class Injector {
|
||||||
|
open fun displayTopology(context : Context) : DisplayTopology? {
|
||||||
|
val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||||
|
return displayManager.displayTopology
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun wallpaper(context : Context) : Drawable {
|
||||||
|
return WallpaperManager.getInstance(context).drawable ?: ColorDrawable(Color.BLACK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun calcAbsRects(
|
||||||
|
dest : MutableMap<Int, RectF>, n : DisplayTopology.TreeNode, x : Float, y : Float) {
|
||||||
|
dest.put(n.displayId, RectF(x, y, x + n.width, y + n.height))
|
||||||
|
|
||||||
|
for (c in n.children) {
|
||||||
|
val (xoff, yoff) = when (c.position) {
|
||||||
|
POSITION_LEFT -> Pair(-c.width, +c.offset)
|
||||||
|
POSITION_RIGHT -> Pair(+n.width, +c.offset)
|
||||||
|
POSITION_TOP -> Pair(+c.offset, -c.height)
|
||||||
|
POSITION_BOTTOM -> Pair(+c.offset, +n.height)
|
||||||
|
else -> throw IllegalStateException("invalid position for display: ${c}")
|
||||||
|
}
|
||||||
|
calcAbsRects(dest, c, x + xoff, y + yoff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshPane() {
|
||||||
|
mPaneContent.removeAllViews()
|
||||||
|
|
||||||
|
val root = injector.displayTopology(context)?.root
|
||||||
|
if (root == null) {
|
||||||
|
// This occurs when no topology is active.
|
||||||
|
// TODO(b/352648432): show main display or mirrored displays rather than an empty pane.
|
||||||
|
mTopologyHint.text = ""
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mTopologyHint.text = context.getString(R.string.external_display_topology_hint)
|
||||||
|
|
||||||
|
val blocksPos = buildMap { calcAbsRects(this, root, x = 0f, y = 0f) }
|
||||||
|
|
||||||
|
val scaling = TopologyScale(
|
||||||
|
mPaneContent.width, minEdgeLength = 60, maxBlockRatio = 0.12f, blocksPos.values)
|
||||||
|
mPaneHolder.layoutParams.let {
|
||||||
|
if (it.height != scaling.paneHeight) {
|
||||||
|
it.height = scaling.paneHeight
|
||||||
|
mPaneHolder.layoutParams = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val wallpaper = injector.wallpaper(context)
|
||||||
|
blocksPos.values.forEach { p ->
|
||||||
|
Button(context).apply {
|
||||||
|
isScrollContainer = false
|
||||||
|
isVerticalScrollBarEnabled = false
|
||||||
|
isHorizontalScrollBarEnabled = false
|
||||||
|
background = wallpaper
|
||||||
|
val topLeft = scaling.displayToPaneCoor(PointF(p.left, p.top))
|
||||||
|
val bottomRight = scaling.displayToPaneCoor(PointF(p.right, p.bottom))
|
||||||
|
|
||||||
|
mPaneContent.addView(this)
|
||||||
|
|
||||||
|
val layout = layoutParams
|
||||||
|
layout.width = bottomRight.x - topLeft.x - BLOCK_PADDING * 2
|
||||||
|
layout.height = bottomRight.y - topLeft.y - BLOCK_PADDING * 2
|
||||||
|
layoutParams = layout
|
||||||
|
x = (topLeft.x + BLOCK_PADDING).toFloat()
|
||||||
|
y = (topLeft.y + BLOCK_PADDING).toFloat()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -274,7 +274,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
}
|
}
|
||||||
if (mDailyChartView != dailyChartView || mHourlyChartView != hourlyChartView) {
|
if (mDailyChartView != dailyChartView || mHourlyChartView != hourlyChartView) {
|
||||||
mHandler.post(() -> setBatteryChartViewInner(dailyChartView, hourlyChartView));
|
mHandler.post(() -> setBatteryChartViewInner(dailyChartView, hourlyChartView));
|
||||||
animateBatteryChartViewGroup();
|
|
||||||
}
|
}
|
||||||
if (mBatteryChartViewGroup != null) {
|
if (mBatteryChartViewGroup != null) {
|
||||||
final View grandparentView = (View) mBatteryChartViewGroup.getParent();
|
final View grandparentView = (View) mBatteryChartViewGroup.getParent();
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ import android.content.ComponentName;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.CompoundButton;
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
@@ -33,7 +33,6 @@ import com.android.internal.annotations.VisibleForTesting;
|
|||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.accessibility.AccessibilityFragmentUtils;
|
import com.android.settings.accessibility.AccessibilityFragmentUtils;
|
||||||
import com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment;
|
import com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment;
|
||||||
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
|
|
||||||
import com.android.settings.search.BaseSearchIndexProvider;
|
import com.android.settings.search.BaseSearchIndexProvider;
|
||||||
import com.android.settingslib.search.SearchIndexable;
|
import com.android.settingslib.search.SearchIndexable;
|
||||||
import com.android.settingslib.search.SearchIndexableRaw;
|
import com.android.settingslib.search.SearchIndexableRaw;
|
||||||
@@ -82,12 +81,7 @@ public class OneHandedSettings extends AccessibilityShortcutPreferenceFragment {
|
|||||||
|
|
||||||
final MainSwitchPreference mainSwitchPreference =
|
final MainSwitchPreference mainSwitchPreference =
|
||||||
getPreferenceScreen().findPreference(ONE_HANDED_MAIN_SWITCH_KEY);
|
getPreferenceScreen().findPreference(ONE_HANDED_MAIN_SWITCH_KEY);
|
||||||
mainSwitchPreference.addOnSwitchChangeListener((switchView, isChecked) -> {
|
mainSwitchPreference.addOnSwitchChangeListener(CompoundButton::setChecked);
|
||||||
switchView.setChecked(isChecked);
|
|
||||||
if (isChecked) {
|
|
||||||
showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -145,24 +139,6 @@ public class OneHandedSettings extends AccessibilityShortcutPreferenceFragment {
|
|||||||
return mFeatureName;
|
return mFeatureName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ComponentName getTileComponentName() {
|
|
||||||
return AccessibilityShortcutController.ONE_HANDED_TILE_COMPONENT_NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type) {
|
|
||||||
final Context context = getContext();
|
|
||||||
if (context == null) {
|
|
||||||
Log.w(TAG, "OneHandedSettings not attached to a context.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return type == QuickSettingsTooltipType.GUIDE_TO_EDIT
|
|
||||||
? context.getText(R.string.accessibility_one_handed_mode_qs_tooltip_content)
|
|
||||||
: context.getText(
|
|
||||||
R.string.accessibility_one_handed_mode_auto_added_qs_tooltip_content);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getPreferenceScreenResId() {
|
protected int getPreferenceScreenResId() {
|
||||||
return R.xml.one_handed_settings;
|
return R.xml.one_handed_settings;
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
import kotlinx.coroutines.flow.shareIn
|
import kotlinx.coroutines.flow.shareIn
|
||||||
|
import java.util.stream.Collectors
|
||||||
|
|
||||||
private const val TAG = "SubscriptionRepository"
|
private const val TAG = "SubscriptionRepository"
|
||||||
|
|
||||||
@@ -154,6 +155,18 @@ class SubscriptionRepository(private val context: Context) {
|
|||||||
.conflate()
|
.conflate()
|
||||||
.flowOn(Dispatchers.Default)
|
.flowOn(Dispatchers.Default)
|
||||||
|
|
||||||
|
fun removableSubscriptionInfoListFlow(): Flow<List<SubscriptionInfo>> {
|
||||||
|
return subscriptionsChangedFlow()
|
||||||
|
.map {
|
||||||
|
subscriptionManager.availableSubscriptionInfoList?.stream()
|
||||||
|
?.filter { sub: SubscriptionInfo -> !sub.isEmbedded }
|
||||||
|
?.collect(Collectors.toList()) ?: emptyList()
|
||||||
|
}
|
||||||
|
.conflate()
|
||||||
|
.onEach { Log.d(TAG, "getRemovableSubscriptionVisibleFlow: $it") }
|
||||||
|
.flowOn(Dispatchers.Default)
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
fun phoneNumberFlow(subId: Int): Flow<String?> =
|
fun phoneNumberFlow(subId: Int): Flow<String?> =
|
||||||
activeSubscriptionInfoFlow(subId).flatMapLatest { subInfo ->
|
activeSubscriptionInfoFlow(subId).flatMapLatest { subInfo ->
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package com.android.settings.service
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.os.Binder
|
import android.os.Binder
|
||||||
import android.os.OutcomeReceiver
|
import android.os.OutcomeReceiver
|
||||||
import android.os.Process
|
|
||||||
import android.service.settings.preferences.GetValueRequest
|
import android.service.settings.preferences.GetValueRequest
|
||||||
import android.service.settings.preferences.GetValueResult
|
import android.service.settings.preferences.GetValueResult
|
||||||
import android.service.settings.preferences.MetadataRequest
|
import android.service.settings.preferences.MetadataRequest
|
||||||
@@ -37,7 +36,6 @@ import kotlinx.coroutines.CoroutineScope
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.lang.Exception
|
|
||||||
|
|
||||||
class PreferenceService : SettingsPreferenceService() {
|
class PreferenceService : SettingsPreferenceService() {
|
||||||
|
|
||||||
@@ -49,14 +47,22 @@ class PreferenceService : SettingsPreferenceService() {
|
|||||||
|
|
||||||
override fun onGetAllPreferenceMetadata(
|
override fun onGetAllPreferenceMetadata(
|
||||||
request: MetadataRequest,
|
request: MetadataRequest,
|
||||||
callback: OutcomeReceiver<MetadataResult, Exception>
|
callback: OutcomeReceiver<MetadataResult, Exception>,
|
||||||
) {
|
) {
|
||||||
|
// MUST get pid/uid in binder thread
|
||||||
|
val callingPid = Binder.getCallingPid()
|
||||||
|
val callingUid = Binder.getCallingUid()
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val graphProto = graphApi.invoke(application, Process.myUid(), Binder.getCallingUid(),
|
val graphProto =
|
||||||
|
graphApi.invoke(
|
||||||
|
application,
|
||||||
|
callingPid,
|
||||||
|
callingUid,
|
||||||
GetPreferenceGraphRequest(
|
GetPreferenceGraphRequest(
|
||||||
includeValue = false,
|
includeValue = false,
|
||||||
flags = PreferenceGetterFlags.METADATA
|
flags = PreferenceGetterFlags.METADATA,
|
||||||
))
|
),
|
||||||
|
)
|
||||||
val result = transformCatalystGetMetadataResponse(this@PreferenceService, graphProto)
|
val result = transformCatalystGetMetadataResponse(this@PreferenceService, graphProto)
|
||||||
callback.onResult(result)
|
callback.onResult(result)
|
||||||
}
|
}
|
||||||
@@ -64,17 +70,16 @@ class PreferenceService : SettingsPreferenceService() {
|
|||||||
|
|
||||||
override fun onGetPreferenceValue(
|
override fun onGetPreferenceValue(
|
||||||
request: GetValueRequest,
|
request: GetValueRequest,
|
||||||
callback: OutcomeReceiver<GetValueResult, Exception>
|
callback: OutcomeReceiver<GetValueResult, Exception>,
|
||||||
) {
|
) {
|
||||||
|
// MUST get pid/uid in binder thread
|
||||||
|
val callingPid = Binder.getCallingPid()
|
||||||
|
val callingUid = Binder.getCallingUid()
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val apiRequest = transformFrameworkGetValueRequest(request)
|
val apiRequest = transformFrameworkGetValueRequest(request)
|
||||||
val response = getApiHandler.invoke(application, Process.myUid(),
|
val response = getApiHandler.invoke(application, callingPid, callingUid, apiRequest)
|
||||||
Binder.getCallingUid(), apiRequest)
|
val result =
|
||||||
val result = transformCatalystGetValueResponse(
|
transformCatalystGetValueResponse(this@PreferenceService, request, response)
|
||||||
this@PreferenceService,
|
|
||||||
request,
|
|
||||||
response
|
|
||||||
)
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
callback.onError(IllegalStateException("No response"))
|
callback.onError(IllegalStateException("No response"))
|
||||||
} else {
|
} else {
|
||||||
@@ -85,8 +90,11 @@ class PreferenceService : SettingsPreferenceService() {
|
|||||||
|
|
||||||
override fun onSetPreferenceValue(
|
override fun onSetPreferenceValue(
|
||||||
request: SetValueRequest,
|
request: SetValueRequest,
|
||||||
callback: OutcomeReceiver<SetValueResult, Exception>
|
callback: OutcomeReceiver<SetValueResult, Exception>,
|
||||||
) {
|
) {
|
||||||
|
// MUST get pid/uid in binder thread
|
||||||
|
val callingPid = Binder.getCallingPid()
|
||||||
|
val callingUid = Binder.getCallingUid()
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val apiRequest = transformFrameworkSetValueRequest(request)
|
val apiRequest = transformFrameworkSetValueRequest(request)
|
||||||
if (apiRequest == null) {
|
if (apiRequest == null) {
|
||||||
@@ -94,8 +102,7 @@ class PreferenceService : SettingsPreferenceService() {
|
|||||||
SetValueResult.Builder(SetValueResult.RESULT_INVALID_REQUEST).build()
|
SetValueResult.Builder(SetValueResult.RESULT_INVALID_REQUEST).build()
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
val response = setApiHandler.invoke(application, Process.myUid(),
|
val response = setApiHandler.invoke(application, callingPid, callingUid, apiRequest)
|
||||||
Binder.getCallingUid(), apiRequest)
|
|
||||||
|
|
||||||
callback.onResult(transformCatalystSetValueResponse(response))
|
callback.onResult(transformCatalystSetValueResponse(response))
|
||||||
}
|
}
|
||||||
@@ -106,9 +113,9 @@ class PreferenceService : SettingsPreferenceService() {
|
|||||||
private class GraphProvider(override val id: Int) : GetPreferenceGraphApiHandler(emptySet()) {
|
private class GraphProvider(override val id: Int) : GetPreferenceGraphApiHandler(emptySet()) {
|
||||||
override fun hasPermission(
|
override fun hasPermission(
|
||||||
application: Application,
|
application: Application,
|
||||||
myUid: Int,
|
callingPid: Int,
|
||||||
callingUid: Int,
|
callingUid: Int,
|
||||||
request: GetPreferenceGraphRequest
|
request: GetPreferenceGraphRequest,
|
||||||
) = true
|
) = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,432 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2020 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.sim.receivers;
|
|
||||||
|
|
||||||
import static android.content.Context.MODE_PRIVATE;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.provider.Settings;
|
|
||||||
import android.telephony.SubscriptionInfo;
|
|
||||||
import android.telephony.SubscriptionManager;
|
|
||||||
import android.telephony.TelephonyManager;
|
|
||||||
import android.telephony.UiccCardInfo;
|
|
||||||
import android.telephony.UiccPortInfo;
|
|
||||||
import android.telephony.UiccSlotInfo;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.android.settings.flags.Flags;
|
|
||||||
import com.android.settings.network.SubscriptionUtil;
|
|
||||||
import com.android.settings.network.UiccSlotUtil;
|
|
||||||
import com.android.settings.network.UiccSlotsException;
|
|
||||||
import com.android.settings.sim.ChooseSimActivity;
|
|
||||||
import com.android.settings.sim.DsdsDialogActivity;
|
|
||||||
import com.android.settings.sim.SimActivationNotifier;
|
|
||||||
import com.android.settings.sim.SimNotificationService;
|
|
||||||
import com.android.settings.sim.SwitchToEsimConfirmDialogActivity;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/** Perform actions after a slot change event is triggered. */
|
|
||||||
public class SimSlotChangeHandler {
|
|
||||||
private static final String TAG = "SimSlotChangeHandler";
|
|
||||||
|
|
||||||
private static final String EUICC_PREFS = "euicc_prefs";
|
|
||||||
// Shared preference keys
|
|
||||||
private static final String KEY_REMOVABLE_SLOT_STATE = "removable_slot_state";
|
|
||||||
private static final String KEY_SUW_PSIM_ACTION = "suw_psim_action";
|
|
||||||
// User's last removable SIM insertion / removal action during SUW.
|
|
||||||
private static final int LAST_USER_ACTION_IN_SUW_NONE = 0;
|
|
||||||
private static final int LAST_USER_ACTION_IN_SUW_INSERT = 1;
|
|
||||||
private static final int LAST_USER_ACTION_IN_SUW_REMOVE = 2;
|
|
||||||
|
|
||||||
private static volatile SimSlotChangeHandler sSlotChangeHandler;
|
|
||||||
|
|
||||||
/** Returns a SIM slot change handler singleton. */
|
|
||||||
public static SimSlotChangeHandler get() {
|
|
||||||
if (sSlotChangeHandler == null) {
|
|
||||||
synchronized (SimSlotChangeHandler.class) {
|
|
||||||
if (sSlotChangeHandler == null) {
|
|
||||||
sSlotChangeHandler = new SimSlotChangeHandler();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sSlotChangeHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SubscriptionManager mSubMgr;
|
|
||||||
private TelephonyManager mTelMgr;
|
|
||||||
private Context mContext;
|
|
||||||
|
|
||||||
void onSlotsStatusChange(Context context) {
|
|
||||||
init(context);
|
|
||||||
|
|
||||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
|
||||||
throw new IllegalStateException("Cannot be called from main thread.");
|
|
||||||
}
|
|
||||||
|
|
||||||
UiccSlotInfo removableSlotInfo = getRemovableUiccSlotInfo();
|
|
||||||
if (removableSlotInfo == null) {
|
|
||||||
Log.e(TAG, "Unable to find the removable slot. Do nothing.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Log.i(TAG, "The removableSlotInfo: " + removableSlotInfo);
|
|
||||||
int lastRemovableSlotState = getLastRemovableSimSlotState(mContext);
|
|
||||||
int currentRemovableSlotState = removableSlotInfo.getCardStateInfo();
|
|
||||||
Log.d(TAG,
|
|
||||||
"lastRemovableSlotState: " + lastRemovableSlotState + ",currentRemovableSlotState: "
|
|
||||||
+ currentRemovableSlotState);
|
|
||||||
boolean isRemovableSimInserted =
|
|
||||||
lastRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_ABSENT
|
|
||||||
&& currentRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_PRESENT;
|
|
||||||
boolean isRemovableSimRemoved =
|
|
||||||
lastRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_PRESENT
|
|
||||||
&& currentRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_ABSENT;
|
|
||||||
|
|
||||||
// Sets the current removable slot state.
|
|
||||||
setRemovableSimSlotState(mContext, currentRemovableSlotState);
|
|
||||||
|
|
||||||
if (mTelMgr.getActiveModemCount() > 1) {
|
|
||||||
if (!isRemovableSimInserted) {
|
|
||||||
Log.d(TAG, "Removable Sim is not inserted in DSDS mode. Do nothing.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Flags.isDualSimOnboardingEnabled()) {
|
|
||||||
// ForNewUi, when the user inserts the psim, showing the sim onboarding for the user
|
|
||||||
// to setup the sim switching or the default data subscription in DSDS.
|
|
||||||
// Will show dialog for below case.
|
|
||||||
// 1. the psim slot is not active.
|
|
||||||
// 2. there are one or more active sim.
|
|
||||||
handleRemovableSimInsertWhenDsds(removableSlotInfo);
|
|
||||||
return;
|
|
||||||
} else if (!isMultipleEnabledProfilesSupported()) {
|
|
||||||
Log.d(TAG, "The device is already in DSDS mode and no MEP. Do nothing.");
|
|
||||||
return;
|
|
||||||
} else if (isMultipleEnabledProfilesSupported()) {
|
|
||||||
handleRemovableSimInsertUnderDsdsMep(removableSlotInfo);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRemovableSimInserted) {
|
|
||||||
handleSimInsert(removableSlotInfo);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isRemovableSimRemoved) {
|
|
||||||
handleSimRemove(removableSlotInfo);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Log.i(TAG, "Do nothing on slot status changes.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void onSuwFinish(Context context) {
|
|
||||||
init(context);
|
|
||||||
|
|
||||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
|
||||||
throw new IllegalStateException("Cannot be called from main thread.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mTelMgr.getActiveModemCount() > 1) {
|
|
||||||
Log.i(TAG, "The device is already in DSDS mode. Do nothing.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UiccSlotInfo removableSlotInfo = getRemovableUiccSlotInfo();
|
|
||||||
if (removableSlotInfo == null) {
|
|
||||||
Log.e(TAG, "Unable to find the removable slot. Do nothing.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean embeddedSimExist = getGroupedEmbeddedSubscriptions().size() != 0;
|
|
||||||
int removableSlotAction = getSuwRemovableSlotAction(mContext);
|
|
||||||
setSuwRemovableSlotAction(mContext, LAST_USER_ACTION_IN_SUW_NONE);
|
|
||||||
|
|
||||||
if (embeddedSimExist
|
|
||||||
&& removableSlotInfo.getCardStateInfo() == UiccSlotInfo.CARD_STATE_INFO_PRESENT) {
|
|
||||||
if (mTelMgr.isMultiSimSupported() == TelephonyManager.MULTISIM_ALLOWED) {
|
|
||||||
Log.i(TAG, "DSDS condition satisfied. Show notification.");
|
|
||||||
SimNotificationService.scheduleSimNotification(
|
|
||||||
mContext, SimActivationNotifier.NotificationType.ENABLE_DSDS);
|
|
||||||
} else if (removableSlotAction == LAST_USER_ACTION_IN_SUW_INSERT) {
|
|
||||||
Log.i(
|
|
||||||
TAG,
|
|
||||||
"Both removable SIM and eSIM are present. DSDS condition doesn't"
|
|
||||||
+ " satisfied. User inserted pSIM during SUW. Show choose SIM"
|
|
||||||
+ " screen.");
|
|
||||||
startChooseSimActivity(true);
|
|
||||||
}
|
|
||||||
} else if (removableSlotAction == LAST_USER_ACTION_IN_SUW_REMOVE) {
|
|
||||||
handleSimRemove(removableSlotInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init(Context context) {
|
|
||||||
mSubMgr =
|
|
||||||
(SubscriptionManager)
|
|
||||||
context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
|
|
||||||
mTelMgr = context.getSystemService(TelephonyManager.class);
|
|
||||||
mContext = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleSimInsert(UiccSlotInfo removableSlotInfo) {
|
|
||||||
Log.i(TAG, "Handle SIM inserted.");
|
|
||||||
if (!isSuwFinished(mContext)) {
|
|
||||||
Log.i(TAG, "Still in SUW. Handle SIM insertion after SUW is finished");
|
|
||||||
setSuwRemovableSlotAction(mContext, LAST_USER_ACTION_IN_SUW_INSERT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (removableSlotInfo.getPorts().stream().findFirst().get().isActive()) {
|
|
||||||
Log.i(TAG, "The removable slot is already active. Do nothing.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasActiveEsimSubscription()) {
|
|
||||||
if (mTelMgr.isMultiSimSupported() == TelephonyManager.MULTISIM_ALLOWED) {
|
|
||||||
Log.i(TAG, "Enabled profile exists. DSDS condition satisfied.");
|
|
||||||
if (Flags.isDualSimOnboardingEnabled()) {
|
|
||||||
// enable dsds by sim onboarding flow
|
|
||||||
handleRemovableSimInsertWhenDsds(removableSlotInfo);
|
|
||||||
} else {
|
|
||||||
startDsdsDialogActivity();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.i(TAG, "Enabled profile exists. DSDS condition not satisfied.");
|
|
||||||
startChooseSimActivity(true);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(
|
|
||||||
TAG,
|
|
||||||
"No enabled eSIM profile. Ready to switch to removable slot and show"
|
|
||||||
+ " notification.");
|
|
||||||
try {
|
|
||||||
UiccSlotUtil.switchToRemovableSlot(
|
|
||||||
UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID, mContext.getApplicationContext());
|
|
||||||
} catch (UiccSlotsException e) {
|
|
||||||
Log.e(TAG, "Failed to switch to removable slot.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SimNotificationService.scheduleSimNotification(
|
|
||||||
mContext, SimActivationNotifier.NotificationType.SWITCH_TO_REMOVABLE_SLOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleSimRemove(UiccSlotInfo removableSlotInfo) {
|
|
||||||
Log.i(TAG, "Handle SIM removed.");
|
|
||||||
|
|
||||||
if (!isSuwFinished(mContext)) {
|
|
||||||
Log.i(TAG, "Still in SUW. Handle SIM removal after SUW is finished");
|
|
||||||
setSuwRemovableSlotAction(mContext, LAST_USER_ACTION_IN_SUW_REMOVE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<SubscriptionInfo> groupedEmbeddedSubscriptions = getGroupedEmbeddedSubscriptions();
|
|
||||||
if (groupedEmbeddedSubscriptions.size() == 0 || !removableSlotInfo.getPorts().stream()
|
|
||||||
.findFirst().get().isActive()) {
|
|
||||||
Log.i(TAG, "eSIM slot is active or no subscriptions exist. Do nothing."
|
|
||||||
+ " The removableSlotInfo: " + removableSlotInfo
|
|
||||||
+ ", groupedEmbeddedSubscriptions: " + groupedEmbeddedSubscriptions);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is only 1 eSIM profile exists, we ask the user if they want to switch to that
|
|
||||||
// profile.
|
|
||||||
if (groupedEmbeddedSubscriptions.size() == 1) {
|
|
||||||
Log.i(TAG, "Only 1 eSIM profile found. Ask user's consent to switch.");
|
|
||||||
startSwitchSlotConfirmDialogActivity(groupedEmbeddedSubscriptions.get(0));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are more than 1 eSIM profiles installed, we show a screen to let users to choose
|
|
||||||
// the number they want to use.
|
|
||||||
Log.i(TAG, "Multiple eSIM profiles found. Ask user which subscription to use.");
|
|
||||||
startChooseSimActivity(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasOtherActiveSubInfo(int psimSubId) {
|
|
||||||
List<SubscriptionInfo> activeSubs = SubscriptionUtil.getActiveSubscriptions(mSubMgr);
|
|
||||||
return activeSubs.stream()
|
|
||||||
.anyMatch(subscriptionInfo -> subscriptionInfo.getSubscriptionId() != psimSubId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasAnyPortActiveInSlot(UiccSlotInfo removableSlotInfo) {
|
|
||||||
return removableSlotInfo.getPorts().stream().anyMatch(UiccPortInfo::isActive);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleRemovableSimInsertWhenDsds(UiccSlotInfo removableSlotInfo) {
|
|
||||||
Log.i(TAG, "ForNewUi: Handle Removable SIM inserted");
|
|
||||||
List<SubscriptionInfo> subscriptionInfos = getAvailableRemovableSubscription();
|
|
||||||
if (subscriptionInfos.isEmpty()) {
|
|
||||||
Log.e(TAG, "Unable to find the removable subscriptionInfo. Do nothing.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Log.d(TAG, "getAvailableRemovableSubscription:" + subscriptionInfos);
|
|
||||||
int psimSubId = subscriptionInfos.get(0).getSubscriptionId();
|
|
||||||
if (!hasAnyPortActiveInSlot(removableSlotInfo) || hasOtherActiveSubInfo(psimSubId)) {
|
|
||||||
Log.d(TAG, "ForNewUi Start Setup flow");
|
|
||||||
startSimConfirmDialogActivity(psimSubId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleRemovableSimInsertUnderDsdsMep(UiccSlotInfo removableSlotInfo) {
|
|
||||||
Log.i(TAG, "Handle Removable SIM inserted under DSDS+Mep.");
|
|
||||||
|
|
||||||
if (removableSlotInfo.getPorts().stream().findFirst().get().isActive()) {
|
|
||||||
Log.i(TAG, "The removable slot is already active. Do nothing. removableSlotInfo: "
|
|
||||||
+ removableSlotInfo);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<SubscriptionInfo> subscriptionInfos = getAvailableRemovableSubscription();
|
|
||||||
if (subscriptionInfos.isEmpty()) {
|
|
||||||
Log.e(TAG, "Unable to find the removable subscriptionInfo. Do nothing.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Log.d(TAG, "getAvailableRemovableSubscription:" + subscriptionInfos);
|
|
||||||
startSimConfirmDialogActivity(subscriptionInfos.get(0).getSubscriptionId());
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getLastRemovableSimSlotState(Context context) {
|
|
||||||
final SharedPreferences prefs = context.getSharedPreferences(EUICC_PREFS, MODE_PRIVATE);
|
|
||||||
return prefs.getInt(KEY_REMOVABLE_SLOT_STATE, UiccSlotInfo.CARD_STATE_INFO_ABSENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setRemovableSimSlotState(Context context, int state) {
|
|
||||||
final SharedPreferences prefs = context.getSharedPreferences(EUICC_PREFS, MODE_PRIVATE);
|
|
||||||
prefs.edit().putInt(KEY_REMOVABLE_SLOT_STATE, state).apply();
|
|
||||||
Log.d(TAG, "setRemovableSimSlotState: " + state);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getSuwRemovableSlotAction(Context context) {
|
|
||||||
final SharedPreferences prefs = context.getSharedPreferences(EUICC_PREFS, MODE_PRIVATE);
|
|
||||||
return prefs.getInt(KEY_SUW_PSIM_ACTION, LAST_USER_ACTION_IN_SUW_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSuwRemovableSlotAction(Context context, int action) {
|
|
||||||
final SharedPreferences prefs = context.getSharedPreferences(EUICC_PREFS, MODE_PRIVATE);
|
|
||||||
prefs.edit().putInt(KEY_SUW_PSIM_ACTION, action).apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private UiccSlotInfo getRemovableUiccSlotInfo() {
|
|
||||||
UiccSlotInfo[] slotInfos = mTelMgr.getUiccSlotsInfo();
|
|
||||||
if (slotInfos == null) {
|
|
||||||
Log.e(TAG, "slotInfos is null. Unable to get slot infos.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
for (UiccSlotInfo slotInfo : slotInfos) {
|
|
||||||
if (slotInfo != null && slotInfo.isRemovable()) {
|
|
||||||
return slotInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isSuwFinished(Context context) {
|
|
||||||
try {
|
|
||||||
// DEVICE_PROVISIONED is 0 if still in setup wizard. 1 if setup completed.
|
|
||||||
return Settings.Global.getInt(
|
|
||||||
context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED)
|
|
||||||
== 1;
|
|
||||||
} catch (Settings.SettingNotFoundException e) {
|
|
||||||
Log.e(TAG, "Cannot get DEVICE_PROVISIONED from the device.", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasActiveEsimSubscription() {
|
|
||||||
List<SubscriptionInfo> activeSubs = SubscriptionUtil.getActiveSubscriptions(mSubMgr);
|
|
||||||
return activeSubs.stream().anyMatch(SubscriptionInfo::isEmbedded);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<SubscriptionInfo> getGroupedEmbeddedSubscriptions() {
|
|
||||||
List<SubscriptionInfo> groupedSubscriptions =
|
|
||||||
SubscriptionUtil.getSelectableSubscriptionInfoList(mContext);
|
|
||||||
if (groupedSubscriptions == null) {
|
|
||||||
return ImmutableList.of();
|
|
||||||
}
|
|
||||||
return ImmutableList.copyOf(
|
|
||||||
groupedSubscriptions.stream()
|
|
||||||
.filter(sub -> sub.isEmbedded())
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected List<SubscriptionInfo> getAvailableRemovableSubscription() {
|
|
||||||
List<SubscriptionInfo> removableSubscriptions =
|
|
||||||
SubscriptionUtil.getAvailableSubscriptions(mContext);
|
|
||||||
return ImmutableList.copyOf(
|
|
||||||
removableSubscriptions.stream()
|
|
||||||
// ToDo: This condition is for psim only. If device supports removable
|
|
||||||
// esim, it needs an new condition.
|
|
||||||
.filter(sub -> !sub.isEmbedded())
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startChooseSimActivity(boolean psimInserted) {
|
|
||||||
Intent intent = ChooseSimActivity.getIntent(mContext);
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
intent.putExtra(ChooseSimActivity.KEY_HAS_PSIM, psimInserted);
|
|
||||||
mContext.startActivityAsUser(intent, UserHandle.SYSTEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startSwitchSlotConfirmDialogActivity(SubscriptionInfo subscriptionInfo) {
|
|
||||||
Intent intent = new Intent(mContext, SwitchToEsimConfirmDialogActivity.class);
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
intent.putExtra(SwitchToEsimConfirmDialogActivity.KEY_SUB_TO_ENABLE, subscriptionInfo);
|
|
||||||
mContext.startActivityAsUser(intent, UserHandle.SYSTEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startDsdsDialogActivity() {
|
|
||||||
Intent intent = new Intent(mContext, DsdsDialogActivity.class);
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
mContext.startActivityAsUser(intent, UserHandle.SYSTEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startSimConfirmDialogActivity(int subId) {
|
|
||||||
if (!isSuwFinished(mContext)) {
|
|
||||||
Log.d(TAG, "Still in SUW. Do nothing");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!SubscriptionManager.isUsableSubscriptionId(subId)) {
|
|
||||||
Log.i(TAG, "Unable to enable subscription due to invalid subscription ID.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Log.d(TAG, "Start ToggleSubscriptionDialogActivity with " + subId + " under DSDS+Mep.");
|
|
||||||
SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, subId, true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isMultipleEnabledProfilesSupported() {
|
|
||||||
List<UiccCardInfo> cardInfos = mTelMgr.getUiccCardsInfo();
|
|
||||||
if (cardInfos == null) {
|
|
||||||
Log.d(TAG, "UICC cards info list is empty.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return cardInfos.stream().anyMatch(
|
|
||||||
cardInfo -> cardInfo.isMultipleEnabledProfilesSupported());
|
|
||||||
}
|
|
||||||
|
|
||||||
private SimSlotChangeHandler() {}
|
|
||||||
}
|
|
||||||
480
src/com/android/settings/sim/receivers/SimSlotChangeHandler.kt
Normal file
480
src/com/android/settings/sim/receivers/SimSlotChangeHandler.kt
Normal file
@@ -0,0 +1,480 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.sim.receivers
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Looper
|
||||||
|
import android.os.UserHandle
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.provider.Settings.SettingNotFoundException
|
||||||
|
import android.telephony.SubscriptionInfo
|
||||||
|
import android.telephony.SubscriptionManager
|
||||||
|
import android.telephony.TelephonyManager
|
||||||
|
import android.telephony.UiccCardInfo
|
||||||
|
import android.telephony.UiccSlotInfo
|
||||||
|
import android.util.Log
|
||||||
|
import com.android.settings.flags.Flags
|
||||||
|
import com.android.settings.network.SubscriptionUtil
|
||||||
|
import com.android.settings.network.UiccSlotUtil
|
||||||
|
import com.android.settings.network.UiccSlotsException
|
||||||
|
import com.android.settings.network.telephony.SubscriptionRepository
|
||||||
|
import com.android.settings.sim.ChooseSimActivity
|
||||||
|
import com.android.settings.sim.DsdsDialogActivity
|
||||||
|
import com.android.settings.sim.SimActivationNotifier
|
||||||
|
import com.android.settings.sim.SimNotificationService
|
||||||
|
import com.android.settings.sim.SwitchToEsimConfirmDialogActivity
|
||||||
|
import com.google.common.collect.ImmutableList
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.flow.firstOrNull
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
|
import java.util.stream.Collectors
|
||||||
|
import kotlin.concurrent.Volatile
|
||||||
|
|
||||||
|
/** Perform actions after a slot change event is triggered. */
|
||||||
|
class SimSlotChangeHandler private constructor() {
|
||||||
|
private var mSubMgr: SubscriptionManager? = null
|
||||||
|
private var mTelMgr: TelephonyManager? = null
|
||||||
|
private var mContext: Context? = null
|
||||||
|
|
||||||
|
fun onSlotsStatusChange(context: Context) {
|
||||||
|
init(context)
|
||||||
|
|
||||||
|
check(Looper.myLooper() != Looper.getMainLooper()) { "Cannot be called from main thread." }
|
||||||
|
|
||||||
|
val removableSlotInfo = removableUiccSlotInfo
|
||||||
|
if (removableSlotInfo == null) {
|
||||||
|
Log.e(TAG, "Unable to find the removable slot. Do nothing.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
"The removableSlotInfo: $removableSlotInfo"
|
||||||
|
)
|
||||||
|
val lastRemovableSlotState = getLastRemovableSimSlotState(mContext!!)
|
||||||
|
val currentRemovableSlotState = removableSlotInfo.cardStateInfo
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
("lastRemovableSlotState: " + lastRemovableSlotState + ",currentRemovableSlotState: "
|
||||||
|
+ currentRemovableSlotState)
|
||||||
|
)
|
||||||
|
val isRemovableSimInserted =
|
||||||
|
lastRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_ABSENT
|
||||||
|
&& currentRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_PRESENT
|
||||||
|
val isRemovableSimRemoved =
|
||||||
|
lastRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_PRESENT
|
||||||
|
&& currentRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_ABSENT
|
||||||
|
|
||||||
|
// Sets the current removable slot state.
|
||||||
|
setRemovableSimSlotState(mContext!!, currentRemovableSlotState)
|
||||||
|
|
||||||
|
if (mTelMgr!!.activeModemCount > 1) {
|
||||||
|
if (!isRemovableSimInserted) {
|
||||||
|
Log.d(TAG, "Removable Sim is not inserted in DSDS mode. Do nothing.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Flags.isDualSimOnboardingEnabled()) {
|
||||||
|
// ForNewUi, when the user inserts the psim, showing the sim onboarding for the user
|
||||||
|
// to setup the sim switching or the default data subscription in DSDS.
|
||||||
|
// Will show dialog for below case.
|
||||||
|
// 1. the psim slot is not active.
|
||||||
|
// 2. there are one or more active sim.
|
||||||
|
handleRemovableSimInsertWhenDsds(removableSlotInfo)
|
||||||
|
return
|
||||||
|
} else if (!isMultipleEnabledProfilesSupported) {
|
||||||
|
Log.d(TAG, "The device is already in DSDS mode and no MEP. Do nothing.")
|
||||||
|
return
|
||||||
|
} else if (isMultipleEnabledProfilesSupported) {
|
||||||
|
handleRemovableSimInsertUnderDsdsMep(removableSlotInfo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRemovableSimInserted) {
|
||||||
|
handleSimInsert(removableSlotInfo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (isRemovableSimRemoved) {
|
||||||
|
handleSimRemove(removableSlotInfo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Log.i(TAG, "Do nothing on slot status changes.")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSuwFinish(context: Context) {
|
||||||
|
init(context)
|
||||||
|
|
||||||
|
check(Looper.myLooper() != Looper.getMainLooper()) { "Cannot be called from main thread." }
|
||||||
|
|
||||||
|
if (mTelMgr!!.activeModemCount > 1) {
|
||||||
|
Log.i(TAG, "The device is already in DSDS mode. Do nothing.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val removableSlotInfo = removableUiccSlotInfo
|
||||||
|
if (removableSlotInfo == null) {
|
||||||
|
Log.e(TAG, "Unable to find the removable slot. Do nothing.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val embeddedSimExist = groupedEmbeddedSubscriptions.size != 0
|
||||||
|
val removableSlotAction = getSuwRemovableSlotAction(mContext!!)
|
||||||
|
setSuwRemovableSlotAction(mContext!!, LAST_USER_ACTION_IN_SUW_NONE)
|
||||||
|
|
||||||
|
if (embeddedSimExist
|
||||||
|
&& removableSlotInfo.cardStateInfo == UiccSlotInfo.CARD_STATE_INFO_PRESENT
|
||||||
|
) {
|
||||||
|
if (mTelMgr!!.isMultiSimSupported() == TelephonyManager.MULTISIM_ALLOWED) {
|
||||||
|
Log.i(TAG, "DSDS condition satisfied. Show notification.")
|
||||||
|
SimNotificationService.scheduleSimNotification(
|
||||||
|
mContext, SimActivationNotifier.NotificationType.ENABLE_DSDS
|
||||||
|
)
|
||||||
|
} else if (removableSlotAction == LAST_USER_ACTION_IN_SUW_INSERT) {
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
("Both removable SIM and eSIM are present. DSDS condition doesn't"
|
||||||
|
+ " satisfied. User inserted pSIM during SUW. Show choose SIM"
|
||||||
|
+ " screen.")
|
||||||
|
)
|
||||||
|
startChooseSimActivity(true)
|
||||||
|
}
|
||||||
|
} else if (removableSlotAction == LAST_USER_ACTION_IN_SUW_REMOVE) {
|
||||||
|
handleSimRemove(removableSlotInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun init(context: Context) {
|
||||||
|
mSubMgr =
|
||||||
|
context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
|
||||||
|
mTelMgr = context.getSystemService(TelephonyManager::class.java)
|
||||||
|
mContext = context
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSimInsert(removableSlotInfo: UiccSlotInfo) {
|
||||||
|
Log.i(TAG, "Handle SIM inserted.")
|
||||||
|
if (!isSuwFinished(mContext!!)) {
|
||||||
|
Log.i(TAG, "Still in SUW. Handle SIM insertion after SUW is finished")
|
||||||
|
setSuwRemovableSlotAction(mContext!!, LAST_USER_ACTION_IN_SUW_INSERT)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (removableSlotInfo.ports.stream().findFirst().get().isActive) {
|
||||||
|
Log.i(TAG, "The removable slot is already active. Do nothing.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasActiveEsimSubscription()) {
|
||||||
|
if (mTelMgr!!.isMultiSimSupported() == TelephonyManager.MULTISIM_ALLOWED) {
|
||||||
|
Log.i(TAG, "Enabled profile exists. DSDS condition satisfied.")
|
||||||
|
if (Flags.isDualSimOnboardingEnabled()) {
|
||||||
|
// enable dsds by sim onboarding flow
|
||||||
|
handleRemovableSimInsertWhenDsds(removableSlotInfo)
|
||||||
|
} else {
|
||||||
|
startDsdsDialogActivity()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, "Enabled profile exists. DSDS condition not satisfied.")
|
||||||
|
startChooseSimActivity(true)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
"No enabled eSIM profile. Ready to switch to removable slot and show"
|
||||||
|
+ " notification."
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
UiccSlotUtil.switchToRemovableSlot(
|
||||||
|
UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID, mContext!!.applicationContext
|
||||||
|
)
|
||||||
|
} catch (e: UiccSlotsException) {
|
||||||
|
Log.e(TAG, "Failed to switch to removable slot.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
SimNotificationService.scheduleSimNotification(
|
||||||
|
mContext, SimActivationNotifier.NotificationType.SWITCH_TO_REMOVABLE_SLOT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSimRemove(removableSlotInfo: UiccSlotInfo) {
|
||||||
|
Log.i(TAG, "Handle SIM removed.")
|
||||||
|
|
||||||
|
if (!isSuwFinished(mContext!!)) {
|
||||||
|
Log.i(TAG, "Still in SUW. Handle SIM removal after SUW is finished")
|
||||||
|
setSuwRemovableSlotAction(mContext!!, LAST_USER_ACTION_IN_SUW_REMOVE)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val groupedEmbeddedSubscriptions =
|
||||||
|
groupedEmbeddedSubscriptions
|
||||||
|
if (groupedEmbeddedSubscriptions.isEmpty() || !removableSlotInfo.ports.stream()
|
||||||
|
.findFirst().get().isActive
|
||||||
|
) {
|
||||||
|
Log.i(
|
||||||
|
TAG, ("eSIM slot is active or no subscriptions exist. Do nothing."
|
||||||
|
+ " The removableSlotInfo: " + removableSlotInfo
|
||||||
|
+ ", groupedEmbeddedSubscriptions: " + groupedEmbeddedSubscriptions)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is only 1 eSIM profile exists, we ask the user if they want to switch to that
|
||||||
|
// profile.
|
||||||
|
if (groupedEmbeddedSubscriptions.size == 1) {
|
||||||
|
Log.i(TAG, "Only 1 eSIM profile found. Ask user's consent to switch.")
|
||||||
|
startSwitchSlotConfirmDialogActivity(groupedEmbeddedSubscriptions[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are more than 1 eSIM profiles installed, we show a screen to let users to choose
|
||||||
|
// the number they want to use.
|
||||||
|
Log.i(TAG, "Multiple eSIM profiles found. Ask user which subscription to use.")
|
||||||
|
startChooseSimActivity(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasOtherActiveSubInfo(psimSubId: Int): Boolean {
|
||||||
|
val activeSubs = SubscriptionUtil.getActiveSubscriptions(mSubMgr)
|
||||||
|
return activeSubs.stream()
|
||||||
|
.anyMatch { subscriptionInfo -> subscriptionInfo.subscriptionId != psimSubId }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasAnyPortActiveInSlot(removableSlotInfo: UiccSlotInfo): Boolean {
|
||||||
|
return removableSlotInfo.ports.stream().anyMatch { slot -> slot.isActive }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleRemovableSimInsertWhenDsds(removableSlotInfo: UiccSlotInfo) {
|
||||||
|
Log.i(TAG, "ForNewUi: Handle Removable SIM inserted")
|
||||||
|
CoroutineScope(Dispatchers.Default + SupervisorJob()).launch {
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
val subscriptionInfos =
|
||||||
|
withTimeoutOrNull(DEFAULT_WAIT_AFTER_SIM_INSERTED_TIMEOUT_MILLIS) {
|
||||||
|
SubscriptionRepository(mContext!!)
|
||||||
|
.removableSubscriptionInfoListFlow()
|
||||||
|
.firstOrNull { it.isNotEmpty() }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subscriptionInfos.isNullOrEmpty()) {
|
||||||
|
Log.e(TAG, "Unable to find the removable subscriptionInfo. Do nothing.")
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"getAvailableRemovableSubscription:$subscriptionInfos"
|
||||||
|
)
|
||||||
|
val psimSubId = subscriptionInfos[0].subscriptionId
|
||||||
|
if (!hasAnyPortActiveInSlot(removableSlotInfo)
|
||||||
|
|| hasOtherActiveSubInfo(psimSubId)) {
|
||||||
|
Log.d(TAG, "ForNewUi Start Setup flow")
|
||||||
|
startSimConfirmDialogActivity(psimSubId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleRemovableSimInsertUnderDsdsMep(removableSlotInfo: UiccSlotInfo) {
|
||||||
|
Log.i(TAG, "Handle Removable SIM inserted under DSDS+Mep.")
|
||||||
|
|
||||||
|
if (removableSlotInfo.ports.stream().findFirst().get().isActive) {
|
||||||
|
Log.i(
|
||||||
|
TAG, "The removable slot is already active. Do nothing. removableSlotInfo: "
|
||||||
|
+ removableSlotInfo
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val subscriptionInfos =
|
||||||
|
availableRemovableSubscription
|
||||||
|
if (subscriptionInfos.isEmpty()) {
|
||||||
|
Log.e(TAG, "Unable to find the removable subscriptionInfo. Do nothing.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"getAvailableRemovableSubscription:$subscriptionInfos"
|
||||||
|
)
|
||||||
|
startSimConfirmDialogActivity(subscriptionInfos[0].subscriptionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLastRemovableSimSlotState(context: Context): Int {
|
||||||
|
val prefs = context.getSharedPreferences(EUICC_PREFS, Context.MODE_PRIVATE)
|
||||||
|
return prefs.getInt(KEY_REMOVABLE_SLOT_STATE, UiccSlotInfo.CARD_STATE_INFO_ABSENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setRemovableSimSlotState(context: Context, state: Int) {
|
||||||
|
val prefs = context.getSharedPreferences(EUICC_PREFS, Context.MODE_PRIVATE)
|
||||||
|
prefs.edit().putInt(KEY_REMOVABLE_SLOT_STATE, state).apply()
|
||||||
|
Log.d(TAG, "setRemovableSimSlotState: $state")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSuwRemovableSlotAction(context: Context): Int {
|
||||||
|
val prefs = context.getSharedPreferences(EUICC_PREFS, Context.MODE_PRIVATE)
|
||||||
|
return prefs.getInt(KEY_SUW_PSIM_ACTION, LAST_USER_ACTION_IN_SUW_NONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setSuwRemovableSlotAction(context: Context, action: Int) {
|
||||||
|
val prefs = context.getSharedPreferences(EUICC_PREFS, Context.MODE_PRIVATE)
|
||||||
|
prefs.edit().putInt(KEY_SUW_PSIM_ACTION, action).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val removableUiccSlotInfo: UiccSlotInfo?
|
||||||
|
get() {
|
||||||
|
val slotInfos = mTelMgr!!.uiccSlotsInfo
|
||||||
|
if (slotInfos == null) {
|
||||||
|
Log.e(
|
||||||
|
TAG,
|
||||||
|
"slotInfos is null. Unable to get slot infos."
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
for (slotInfo in slotInfos) {
|
||||||
|
if (slotInfo != null && slotInfo.isRemovable) {
|
||||||
|
return slotInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasActiveEsimSubscription(): Boolean {
|
||||||
|
val activeSubs = SubscriptionUtil.getActiveSubscriptions(mSubMgr)
|
||||||
|
return activeSubs.stream().anyMatch { subscriptionInfo -> subscriptionInfo.isEmbedded }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val groupedEmbeddedSubscriptions: List<SubscriptionInfo>
|
||||||
|
get() {
|
||||||
|
val groupedSubscriptions =
|
||||||
|
SubscriptionUtil.getSelectableSubscriptionInfoList(mContext)
|
||||||
|
?: return ImmutableList.of()
|
||||||
|
return ImmutableList.copyOf(
|
||||||
|
groupedSubscriptions.stream()
|
||||||
|
.filter { sub: SubscriptionInfo -> sub.isEmbedded }
|
||||||
|
.collect(Collectors.toList()))
|
||||||
|
}
|
||||||
|
|
||||||
|
protected val availableRemovableSubscription: List<SubscriptionInfo>
|
||||||
|
get() {
|
||||||
|
val removableSubscriptions =
|
||||||
|
SubscriptionUtil.getAvailableSubscriptions(mContext)
|
||||||
|
return ImmutableList.copyOf(
|
||||||
|
removableSubscriptions.stream()
|
||||||
|
// ToDo: This condition is for psim only. If device supports removable
|
||||||
|
// esim, it needs an new condition.
|
||||||
|
.filter { sub: SubscriptionInfo -> !sub.isEmbedded }
|
||||||
|
.collect(Collectors.toList()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startChooseSimActivity(psimInserted: Boolean) {
|
||||||
|
val intent = ChooseSimActivity.getIntent(mContext)
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
intent.putExtra(ChooseSimActivity.KEY_HAS_PSIM, psimInserted)
|
||||||
|
mContext!!.startActivityAsUser(intent, UserHandle.SYSTEM)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startSwitchSlotConfirmDialogActivity(subscriptionInfo: SubscriptionInfo) {
|
||||||
|
val intent = Intent(
|
||||||
|
mContext,
|
||||||
|
SwitchToEsimConfirmDialogActivity::class.java
|
||||||
|
)
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
intent.putExtra(SwitchToEsimConfirmDialogActivity.KEY_SUB_TO_ENABLE, subscriptionInfo)
|
||||||
|
mContext!!.startActivityAsUser(intent, UserHandle.SYSTEM)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startDsdsDialogActivity() {
|
||||||
|
val intent = Intent(mContext, DsdsDialogActivity::class.java)
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
mContext!!.startActivityAsUser(intent, UserHandle.SYSTEM)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startSimConfirmDialogActivity(subId: Int) {
|
||||||
|
if (!isSuwFinished(mContext!!)) {
|
||||||
|
Log.d(TAG, "Still in SUW. Do nothing")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!SubscriptionManager.isUsableSubscriptionId(subId)) {
|
||||||
|
Log.i(TAG, "Unable to enable subscription due to invalid subscription ID.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"Start ToggleSubscriptionDialogActivity with $subId under DSDS+Mep."
|
||||||
|
)
|
||||||
|
SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, subId, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val isMultipleEnabledProfilesSupported: Boolean
|
||||||
|
get() {
|
||||||
|
val cardInfos = mTelMgr!!.uiccCardsInfo
|
||||||
|
if (cardInfos == null) {
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"UICC cards info list is empty."
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return cardInfos.stream()
|
||||||
|
.anyMatch { cardInfo: UiccCardInfo -> cardInfo.isMultipleEnabledProfilesSupported }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSuwFinished(context: Context): Boolean {
|
||||||
|
try {
|
||||||
|
// DEVICE_PROVISIONED is 0 if still in setup wizard. 1 if setup completed.
|
||||||
|
return (Settings.Global.getInt(
|
||||||
|
context.contentResolver, Settings.Global.DEVICE_PROVISIONED)
|
||||||
|
== 1)
|
||||||
|
} catch (e: SettingNotFoundException) {
|
||||||
|
Log.e(TAG, "Cannot get DEVICE_PROVISIONED from the device.", e)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "SimSlotChangeHandler"
|
||||||
|
|
||||||
|
private const val EUICC_PREFS = "euicc_prefs"
|
||||||
|
|
||||||
|
// Shared preference keys
|
||||||
|
private const val KEY_REMOVABLE_SLOT_STATE = "removable_slot_state"
|
||||||
|
private const val KEY_SUW_PSIM_ACTION = "suw_psim_action"
|
||||||
|
|
||||||
|
// User's last removable SIM insertion / removal action during SUW.
|
||||||
|
private const val LAST_USER_ACTION_IN_SUW_NONE = 0
|
||||||
|
private const val LAST_USER_ACTION_IN_SUW_INSERT = 1
|
||||||
|
private const val LAST_USER_ACTION_IN_SUW_REMOVE = 2
|
||||||
|
|
||||||
|
private const val DEFAULT_WAIT_AFTER_SIM_INSERTED_TIMEOUT_MILLIS: Long = 25 * 1000L
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
private var slotChangeHandler: SimSlotChangeHandler? = null
|
||||||
|
|
||||||
|
/** Returns a SIM slot change handler singleton. */
|
||||||
|
@JvmStatic
|
||||||
|
fun get(): SimSlotChangeHandler? {
|
||||||
|
if (slotChangeHandler == null) {
|
||||||
|
synchronized(SimSlotChangeHandler::class.java) {
|
||||||
|
if (slotChangeHandler == null) {
|
||||||
|
slotChangeHandler = SimSlotChangeHandler()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return slotChangeHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,6 @@ package com.android.settings.accessibility;
|
|||||||
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
|
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
|
||||||
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
|
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
|
||||||
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
|
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
|
||||||
import static com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
@@ -37,7 +36,6 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.icu.text.CaseMap;
|
import android.icu.text.CaseMap;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.platform.test.flag.junit.SetFlagsRule;
|
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -57,7 +55,6 @@ import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragm
|
|||||||
import com.android.settings.testutils.shadow.ShadowFragment;
|
import com.android.settings.testutils.shadow.ShadowFragment;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Answers;
|
import org.mockito.Answers;
|
||||||
@@ -83,18 +80,11 @@ public class AccessibilityShortcutPreferenceFragmentTest {
|
|||||||
PLACEHOLDER_PACKAGE_NAME + "tile.placeholder";
|
PLACEHOLDER_PACKAGE_NAME + "tile.placeholder";
|
||||||
private static final ComponentName PLACEHOLDER_COMPONENT_NAME = new ComponentName(
|
private static final ComponentName PLACEHOLDER_COMPONENT_NAME = new ComponentName(
|
||||||
PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CLASS_NAME);
|
PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CLASS_NAME);
|
||||||
private static final ComponentName PLACEHOLDER_TILE_COMPONENT_NAME = new ComponentName(
|
|
||||||
PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_TILE_CLASS_NAME);
|
|
||||||
private static final String PLACEHOLDER_TILE_TOOLTIP_CONTENT =
|
|
||||||
PLACEHOLDER_PACKAGE_NAME + "tooltip_content";
|
|
||||||
private static final String PLACEHOLDER_DIALOG_TITLE = "title";
|
|
||||||
|
|
||||||
private static final String SOFTWARE_SHORTCUT_KEY =
|
private static final String SOFTWARE_SHORTCUT_KEY =
|
||||||
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
|
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
|
||||||
private static final String HARDWARE_SHORTCUT_KEY =
|
private static final String HARDWARE_SHORTCUT_KEY =
|
||||||
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
|
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
|
||||||
@Rule
|
|
||||||
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
|
||||||
private TestAccessibilityShortcutPreferenceFragment mFragment;
|
private TestAccessibilityShortcutPreferenceFragment mFragment;
|
||||||
private PreferenceScreen mScreen;
|
private PreferenceScreen mScreen;
|
||||||
private Context mContext = ApplicationProvider.getApplicationContext();
|
private Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
@@ -151,14 +141,6 @@ public class AccessibilityShortcutPreferenceFragmentTest {
|
|||||||
assertThat(expectedType).isEqualTo(HARDWARE);
|
assertThat(expectedType).isEqualTo(HARDWARE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@Config(shadows = ShadowFragment.class)
|
|
||||||
public void showQuickSettingsTooltipIfNeeded_dontShowTooltipView() {
|
|
||||||
mFragment.showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_EDIT);
|
|
||||||
|
|
||||||
assertThat(getLatestPopupWindow()).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Config(shadows = ShadowFragment.class)
|
@Config(shadows = ShadowFragment.class)
|
||||||
public void showGeneralCategory_shouldInitCategory() {
|
public void showGeneralCategory_shouldInitCategory() {
|
||||||
@@ -260,16 +242,6 @@ public class AccessibilityShortcutPreferenceFragmentTest {
|
|||||||
return PLACEHOLDER_PACKAGE_NAME;
|
return PLACEHOLDER_PACKAGE_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ComponentName getTileComponentName() {
|
|
||||||
return PLACEHOLDER_TILE_COMPONENT_NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type) {
|
|
||||||
return PLACEHOLDER_TILE_TOOLTIP_CONTENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getUserShortcutTypes() {
|
public int getUserShortcutTypes() {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -315,14 +315,6 @@ public class ToggleFeaturePreferenceFragmentTest {
|
|||||||
.isEqualTo(PLACEHOLDER_COMPONENT_NAME.flattenToString());
|
.isEqualTo(PLACEHOLDER_COMPONENT_NAME.flattenToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@Config(shadows = ShadowFragment.class)
|
|
||||||
public void showQuickSettingsTooltipIfNeeded_dontShowTooltipView() {
|
|
||||||
mFragment.showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_EDIT);
|
|
||||||
|
|
||||||
assertThat(getLatestPopupWindow()).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getShortcutTypeSummary_shortcutSummaryIsCorrectlySet() {
|
public void getShortcutTypeSummary_shortcutSummaryIsCorrectlySet() {
|
||||||
final PreferredShortcut userPreferredShortcut = new PreferredShortcut(
|
final PreferredShortcut userPreferredShortcut = new PreferredShortcut(
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 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.connecteddevice.display
|
||||||
|
|
||||||
|
import android.hardware.display.DisplayTopology.TreeNode.POSITION_LEFT
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.hardware.display.DisplayTopology
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import androidx.preference.PreferenceViewHolder
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
class DisplayTopologyPreferenceTest {
|
||||||
|
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||||
|
val preference = DisplayTopologyPreference(context)
|
||||||
|
val injector = TestInjector()
|
||||||
|
val rootView = View.inflate(context, preference.layoutResource, /*parent=*/ null)
|
||||||
|
val holder = PreferenceViewHolder.createInstanceForTests(rootView)
|
||||||
|
val wallpaper = ColorDrawable(Color.MAGENTA)
|
||||||
|
|
||||||
|
init {
|
||||||
|
preference.injector = injector
|
||||||
|
injector.systemWallpaper = wallpaper
|
||||||
|
preference.onBindViewHolder(holder)
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestInjector : DisplayTopologyPreference.Injector() {
|
||||||
|
var topology : DisplayTopology? = null
|
||||||
|
var systemWallpaper : Drawable? = null
|
||||||
|
|
||||||
|
override fun displayTopology(context : Context) : DisplayTopology? { return topology }
|
||||||
|
|
||||||
|
override fun wallpaper(context : Context) : Drawable { return systemWallpaper!! }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun disabledTopology() {
|
||||||
|
preference.onAttached()
|
||||||
|
preference.onGlobalLayout()
|
||||||
|
|
||||||
|
assertThat(preference.mPaneContent.childCount).isEqualTo(0)
|
||||||
|
// TODO(b/352648432): update test when we show the main display even when
|
||||||
|
// a topology is not active.
|
||||||
|
assertThat(preference.mTopologyHint.text).isEqualTo("")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun twoDisplaysGenerateBlocks() {
|
||||||
|
val child = DisplayTopology.TreeNode(
|
||||||
|
/* displayId= */ 42, /* width= */ 100f, /* height= */ 80f,
|
||||||
|
POSITION_LEFT, /* offset= */ 42f)
|
||||||
|
val root = DisplayTopology.TreeNode(
|
||||||
|
/* displayId= */ 0, /* width= */ 200f, /* height= */ 160f,
|
||||||
|
POSITION_LEFT, /* offset= */ 0f)
|
||||||
|
root.addChild(child)
|
||||||
|
injector.topology = DisplayTopology(root, /*primaryDisplayId=*/ 0)
|
||||||
|
|
||||||
|
// This layoutParams needs to be non-null for the global layout handler.
|
||||||
|
preference.mPaneHolder.layoutParams = FrameLayout.LayoutParams(
|
||||||
|
/* width= */ 640, /* height= */ 480)
|
||||||
|
|
||||||
|
// Force pane width to have a reasonable value (hundreds of dp) so the TopologyScale is
|
||||||
|
// calculated reasonably.
|
||||||
|
preference.mPaneContent.left = 0
|
||||||
|
preference.mPaneContent.right = 640
|
||||||
|
|
||||||
|
preference.onAttached()
|
||||||
|
preference.onGlobalLayout()
|
||||||
|
|
||||||
|
assertThat(preference.mPaneContent.childCount).isEqualTo(2)
|
||||||
|
val block0 = preference.mPaneContent.getChildAt(0)
|
||||||
|
val block1 = preference.mPaneContent.getChildAt(1)
|
||||||
|
|
||||||
|
// Block of child display is on the left.
|
||||||
|
val (childBlock, rootBlock) = if (block0.x < block1.x)
|
||||||
|
listOf(block0, block1)
|
||||||
|
else
|
||||||
|
listOf(block1, block0)
|
||||||
|
|
||||||
|
// After accounting for padding, child should be half the length of root in each dimension.
|
||||||
|
assertThat(childBlock.layoutParams.width + BLOCK_PADDING)
|
||||||
|
.isEqualTo(rootBlock.layoutParams.width / 2)
|
||||||
|
assertThat(childBlock.layoutParams.height + BLOCK_PADDING)
|
||||||
|
.isEqualTo(rootBlock.layoutParams.height / 2)
|
||||||
|
assertThat(childBlock.y).isGreaterThan(rootBlock.y)
|
||||||
|
assertThat(block0.background).isEqualTo(wallpaper)
|
||||||
|
assertThat(block1.background).isEqualTo(wallpaper)
|
||||||
|
assertThat(rootBlock.x - BLOCK_PADDING * 2)
|
||||||
|
.isEqualTo(childBlock.x + childBlock.layoutParams.width)
|
||||||
|
|
||||||
|
assertThat(preference.mTopologyHint.text)
|
||||||
|
.isEqualTo(context.getString(R.string.external_display_topology_hint))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,7 +21,6 @@ import static com.android.settings.gestures.OneHandedSettings.ONE_HANDED_SHORTCU
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
@@ -32,8 +31,6 @@ import android.provider.SearchIndexableResource;
|
|||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
|
|
||||||
import com.android.settingslib.search.SearchIndexableRaw;
|
import com.android.settingslib.search.SearchIndexableRaw;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -62,21 +59,6 @@ public class OneHandedSettingsTest {
|
|||||||
SystemProperties.set(OneHandedSettingsUtils.SUPPORT_ONE_HANDED_MODE, "true");
|
SystemProperties.set(OneHandedSettingsUtils.SUPPORT_ONE_HANDED_MODE, "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getTileTooltipContent_returnsExpectedValues() {
|
|
||||||
// Simulate to call getTileTooltipContent after onDetach
|
|
||||||
assertThat(mSettings.getTileTooltipContent(QuickSettingsTooltipType.GUIDE_TO_EDIT))
|
|
||||||
.isNull();
|
|
||||||
// Simulate to call getTileTooltipContent after onAttach
|
|
||||||
when(mSettings.getContext()).thenReturn(mContext);
|
|
||||||
assertThat(mSettings.getTileTooltipContent(QuickSettingsTooltipType.GUIDE_TO_EDIT))
|
|
||||||
.isEqualTo(mContext.getText(
|
|
||||||
R.string.accessibility_one_handed_mode_qs_tooltip_content));
|
|
||||||
assertThat(mSettings.getTileTooltipContent(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE))
|
|
||||||
.isEqualTo(mContext.getText(
|
|
||||||
R.string.accessibility_one_handed_mode_auto_added_qs_tooltip_content));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getLogTag_returnsCorrectTag() {
|
public void getLogTag_returnsCorrectTag() {
|
||||||
assertThat(mSettings.getLogTag()).isEqualTo("OneHandedSettings");
|
assertThat(mSettings.getLogTag()).isEqualTo("OneHandedSettings");
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
|||||||
import com.android.settings.network.telephony.CellInfoUtil.getNetworkTitle
|
import com.android.settings.network.telephony.CellInfoUtil.getNetworkTitle
|
||||||
import com.android.settings.network.telephony.CellInfoUtil.getOperatorNumeric
|
import com.android.settings.network.telephony.CellInfoUtil.getOperatorNumeric
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
@@ -95,6 +96,7 @@ class CellInfoUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Ignore("b/383858953")
|
||||||
fun cellInfoListToString() {
|
fun cellInfoListToString() {
|
||||||
val cellInfoList =
|
val cellInfoList =
|
||||||
listOf(
|
listOf(
|
||||||
|
|||||||
Reference in New Issue
Block a user