Merge "[Settings] Avoid from crash - SystemProp default null" into sc-qpr1-dev
diff --git a/src/com/android/settings/display/SmartAutoRotatePreferenceController.java b/src/com/android/settings/display/SmartAutoRotatePreferenceController.java
index f8d5f96..849fbe7 100644
--- a/src/com/android/settings/display/SmartAutoRotatePreferenceController.java
+++ b/src/com/android/settings/display/SmartAutoRotatePreferenceController.java
@@ -22,6 +22,8 @@
import static com.android.settings.display.SmartAutoRotateController.hasSufficientPermission;
import static com.android.settings.display.SmartAutoRotateController.isRotationResolverServiceAvailable;
+import android.text.TextUtils;
+import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -37,7 +39,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.view.RotationPolicy;
import com.android.settings.R;
-import com.android.settings.core.BasePreferenceController;
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -45,12 +49,10 @@
/**
* SmartAutoRotatePreferenceController provides auto rotate summary in display settings
*/
-public class SmartAutoRotatePreferenceController extends BasePreferenceController
+public class SmartAutoRotatePreferenceController extends TogglePreferenceController
implements LifecycleObserver, OnStart, OnStop {
- private RotationPolicy.RotationPolicyListener mRotationPolicyListener;
- private Preference mPreference;
-
+ private final MetricsFeatureProvider mMetricsFeatureProvider;
private final SensorPrivacyManager mPrivacyManager;
private final PowerManager mPowerManager;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -60,12 +62,16 @@
}
};
+ private RotationPolicy.RotationPolicyListener mRotationPolicyListener;
+ private Preference mPreference;
+
public SmartAutoRotatePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mPrivacyManager = SensorPrivacyManager.getInstance(context);
mPrivacyManager
.addSensorPrivacyListener(CAMERA, (sensor, enabled) -> refreshSummary(mPreference));
mPowerManager = context.getSystemService(PowerManager.class);
+ mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
}
@Override
@@ -75,12 +81,28 @@
}
@Override
+ public boolean isSliceable() {
+ return TextUtils.equals(getPreferenceKey(), "auto_rotate");
+ }
+
+ @Override
+ public boolean isPublicSlice() {
+ return true;
+ }
+
+ @Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
}
@Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ refreshSummary(mPreference);
+ }
+
+ @Override
public void onStart() {
mContext.registerReceiver(mReceiver,
new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
@@ -89,7 +111,7 @@
@Override
public void onChange() {
if (mPreference != null) {
- refreshSummary(mPreference);
+ updateState(mPreference);
}
}
};
@@ -122,6 +144,20 @@
}
@Override
+ public boolean isChecked() {
+ return !RotationPolicy.isRotationLocked(mContext);
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ final boolean isLocked = !isChecked;
+ mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_ROTATION_LOCK,
+ isLocked);
+ RotationPolicy.setRotationLock(mContext, isLocked);
+ return true;
+ }
+
+ @Override
public CharSequence getSummary() {
int activeStringId = R.string.auto_rotate_option_off;
if (!RotationPolicy.isRotationLocked(mContext)) {
diff --git a/src/com/android/settings/gestures/OneHandedActionPullDownPrefController.java b/src/com/android/settings/gestures/OneHandedActionPullDownPrefController.java
index e17165b..26b3763 100644
--- a/src/com/android/settings/gestures/OneHandedActionPullDownPrefController.java
+++ b/src/com/android/settings/gestures/OneHandedActionPullDownPrefController.java
@@ -93,7 +93,8 @@
return;
}
if (uri.equals(OneHandedSettingsUtils.ONE_HANDED_MODE_ENABLED_URI)
- || uri.equals(OneHandedSettingsUtils.SHORTCUT_ENABLED_URI)) {
+ || uri.equals(OneHandedSettingsUtils.SOFTWARE_SHORTCUT_ENABLED_URI)
+ || uri.equals(OneHandedSettingsUtils.HARDWARE_SHORTCUT_ENABLED_URI)) {
mPreference.setEnabled(OneHandedSettingsUtils.canEnableController(mContext));
} else if (uri.equals(OneHandedSettingsUtils.SHOW_NOTIFICATION_ENABLED_URI)) {
updateState(mPreference);
diff --git a/src/com/android/settings/gestures/OneHandedActionShowNotificationPrefController.java b/src/com/android/settings/gestures/OneHandedActionShowNotificationPrefController.java
index 9f56a14..524c135 100644
--- a/src/com/android/settings/gestures/OneHandedActionShowNotificationPrefController.java
+++ b/src/com/android/settings/gestures/OneHandedActionShowNotificationPrefController.java
@@ -93,7 +93,8 @@
return;
}
if (uri.equals(OneHandedSettingsUtils.ONE_HANDED_MODE_ENABLED_URI)
- || uri.equals(OneHandedSettingsUtils.SHORTCUT_ENABLED_URI)) {
+ || uri.equals(OneHandedSettingsUtils.SOFTWARE_SHORTCUT_ENABLED_URI)
+ || uri.equals(OneHandedSettingsUtils.HARDWARE_SHORTCUT_ENABLED_URI)) {
mPreference.setEnabled(OneHandedSettingsUtils.canEnableController(mContext));
} else if (uri.equals(OneHandedSettingsUtils.SHOW_NOTIFICATION_ENABLED_URI)) {
updateState(mPreference);
diff --git a/src/com/android/settings/gestures/OneHandedSettingsUtils.java b/src/com/android/settings/gestures/OneHandedSettingsUtils.java
index f058689..04898dc 100644
--- a/src/com/android/settings/gestures/OneHandedSettingsUtils.java
+++ b/src/com/android/settings/gestures/OneHandedSettingsUtils.java
@@ -27,6 +27,7 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
+import android.text.TextUtils;
import androidx.annotation.VisibleForTesting;
@@ -45,8 +46,10 @@
Settings.Secure.getUriFor(Settings.Secure.ONE_HANDED_MODE_ENABLED);
static final Uri SHOW_NOTIFICATION_ENABLED_URI =
Settings.Secure.getUriFor(Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
- static final Uri SHORTCUT_ENABLED_URI =
+ static final Uri SOFTWARE_SHORTCUT_ENABLED_URI =
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
+ static final Uri HARDWARE_SHORTCUT_ENABLED_URI =
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
public enum OneHandedTimeout {
NEVER(0), SHORT(4), MEDIUM(8), LONG(12);
@@ -238,9 +241,20 @@
* @return true if user enabled one-handed shortcut in settings, false otherwise.
*/
public static boolean getShortcutEnabled(Context context) {
- final String targets = Settings.Secure.getStringForUser(context.getContentResolver(),
+ // Checks SOFTWARE_SHORTCUT_KEY
+ final String targetsSW = Settings.Secure.getStringForUser(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, sCurrentUserId);
- return targets != null ? targets.contains(ONE_HANDED_MODE_TARGET_NAME) : false;
+ if (!TextUtils.isEmpty(targetsSW) && targetsSW.contains(ONE_HANDED_MODE_TARGET_NAME)) {
+ return true;
+ }
+
+ // Checks HARDWARE_SHORTCUT_KEY
+ final String targetsHW = Settings.Secure.getStringForUser(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, sCurrentUserId);
+ if (!TextUtils.isEmpty(targetsHW) && targetsHW.contains(ONE_HANDED_MODE_TARGET_NAME)) {
+ return true;
+ }
+ return false;
}
/**
@@ -285,7 +299,8 @@
final ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(ONE_HANDED_MODE_ENABLED_URI, true, this);
resolver.registerContentObserver(SHOW_NOTIFICATION_ENABLED_URI, true, this);
- resolver.registerContentObserver(SHORTCUT_ENABLED_URI, true, this);
+ resolver.registerContentObserver(SOFTWARE_SHORTCUT_ENABLED_URI, true, this);
+ resolver.registerContentObserver(HARDWARE_SHORTCUT_ENABLED_URI, true, this);
}
@Override
diff --git a/src/com/android/settings/network/telephony/MobileNetworkActivity.java b/src/com/android/settings/network/telephony/MobileNetworkActivity.java
index c6fe39c..efb5f8c 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkActivity.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkActivity.java
@@ -39,7 +39,6 @@
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Lifecycle;
-import com.android.internal.util.CollectionUtils;
import com.android.settings.R;
import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.network.ProxySubscriptionManager;
@@ -48,6 +47,7 @@
import com.android.settings.network.helper.SubscriptionAnnotation;
import java.util.List;
+import java.util.function.Function;
/**
* Activity for displaying MobileNetworkSettings
@@ -64,15 +64,14 @@
@VisibleForTesting
ProxySubscriptionManager mProxySubscriptionMgr;
- private int mCurSubscriptionId;
+ private int mCurSubscriptionId = SUB_ID_NULL;
// This flag forces subscription information fragment to be re-created.
// Otherwise, fragment will be kept when subscription id has not been changed.
//
// Set initial value to true allows subscription information fragment to be re-created when
// Activity re-create occur.
- private boolean mFragmentForceReload = true;
- private boolean mPendingSubscriptionChange = false;
+ private boolean mPendingSubscriptionChange = true;
@Override
protected void onNewIntent(Intent intent) {
@@ -80,21 +79,25 @@
validate(intent);
setIntent(intent);
- int updateSubscriptionIndex = SUB_ID_NULL;
+ int updateSubscriptionIndex = mCurSubscriptionId;
if (intent != null) {
updateSubscriptionIndex = intent.getIntExtra(Settings.EXTRA_SUB_ID, SUB_ID_NULL);
}
+ SubscriptionInfo info = getSubscriptionOrDefault(updateSubscriptionIndex);
+ if (info == null) {
+ Log.d(TAG, "Invalid subId request " + mCurSubscriptionId
+ + " -> " + updateSubscriptionIndex);
+ return;
+ }
+
int oldSubId = mCurSubscriptionId;
- mCurSubscriptionId = updateSubscriptionIndex;
- mFragmentForceReload = (mCurSubscriptionId == oldSubId);
- final SubscriptionInfo info = getSubscription();
updateSubscriptions(info, null);
// If the subscription has changed or the new intent doesnt contain the opt in action,
// remove the old discovery dialog. If the activity is being recreated, we will see
// onCreate -> onNewIntent, so the dialog will first be recreated for the old subscription
// and then removed.
- if (updateSubscriptionIndex != oldSubId || !doesIntentContainOptInAction(intent)) {
+ if (mCurSubscriptionId != oldSubId || !doesIntentContainOptInAction(intent)) {
removeContactDiscoveryDialog(oldSubId);
}
// evaluate showing the new discovery dialog if this intent contains an action to show the
@@ -135,7 +138,13 @@
// perform registration after mCurSubscriptionId been configured.
registerActiveSubscriptionsListener();
- final SubscriptionInfo subscription = getSubscription();
+ SubscriptionInfo subscription = getSubscriptionOrDefault(mCurSubscriptionId);
+ if (subscription == null) {
+ Log.d(TAG, "Invalid subId request " + mCurSubscriptionId);
+ tryToFinishActivity();
+ return;
+ }
+
maybeShowContactDiscoveryDialog(subscription);
updateSubscriptions(subscription, null);
@@ -158,39 +167,81 @@
* Implementation of ProxySubscriptionManager.OnActiveSubscriptionChangedListener
*/
public void onChanged() {
+ mPendingSubscriptionChange = false;
+
+ if (mCurSubscriptionId == SUB_ID_NULL) {
+ return;
+ }
+
if (!getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
mPendingSubscriptionChange = true;
return;
}
- SubscriptionInfo info = getSubscription();
- int oldSubIndex = mCurSubscriptionId;
- updateSubscriptions(info, null);
- // Remove the dialog if the subscription associated with this activity changes.
- if (info == null) {
- // Close the activity when subscription removed
- if ((oldSubIndex != SUB_ID_NULL)
- && (!isFinishing()) && (!isDestroyed())) {
- finish();
+ SubscriptionInfo subInfo = getSubscription(mCurSubscriptionId, null);
+ if (subInfo != null) {
+ if (mCurSubscriptionId != subInfo.getSubscriptionId()) {
+ // update based on subscription status change
+ removeContactDiscoveryDialog(mCurSubscriptionId);
+ updateSubscriptions(subInfo, null);
}
return;
}
- int subIndex = info.getSubscriptionId();
- if (subIndex != oldSubIndex) {
- removeContactDiscoveryDialog(oldSubIndex);
+
+ Log.w(TAG, "subId missing: " + mCurSubscriptionId);
+
+ // When UI is not the active one, avoid from destroy it immediately
+ // but wait until onResume() to see if subscription back online again.
+ // This is to avoid from glitch behavior of subscription which changes
+ // the UI when UI is considered as in the background or only partly
+ // visible.
+ if (!getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
+ mPendingSubscriptionChange = true;
+ return;
+ }
+
+ // Subscription could be missing
+ tryToFinishActivity();
+ }
+
+ protected void runSubscriptionUpdate(Runnable onUpdateRemaining) {
+ SubscriptionInfo subInfo = getSubscription(mCurSubscriptionId, null);
+ if (subInfo == null) {
+ tryToFinishActivity();
+ return;
+ }
+ if (mCurSubscriptionId != subInfo.getSubscriptionId()) {
+ removeContactDiscoveryDialog(mCurSubscriptionId);
+ updateSubscriptions(subInfo, null);
+ }
+ onUpdateRemaining.run();
+ }
+
+ protected void tryToFinishActivity() {
+ if ((!isFinishing()) && (!isDestroyed())) {
+ finish();
}
}
@Override
protected void onStart() {
getProxySubscriptionManager().setLifecycle(getLifecycle());
- super.onStart();
- // updateSubscriptions doesn't need to be called, onChanged will always be called after we
- // register a listener.
if (mPendingSubscriptionChange) {
mPendingSubscriptionChange = false;
- onChanged();
+ runSubscriptionUpdate(() -> super.onStart());
+ return;
}
+ super.onStart();
+ }
+
+ @Override
+ protected void onResume() {
+ if (mPendingSubscriptionChange) {
+ mPendingSubscriptionChange = false;
+ runSubscriptionUpdate(() -> super.onResume());
+ return;
+ }
+ super.onResume();
}
@Override
@@ -235,30 +286,49 @@
}
mCurSubscriptionId = subscriptionIndex;
- mFragmentForceReload = false;
+ }
+
+ /**
+ * Select one of the subscription as the default subscription.
+ * @param subAnnoList a list of {@link SubscriptionAnnotation}
+ * @return ideally the {@link SubscriptionAnnotation} as expected
+ */
+ protected SubscriptionAnnotation defaultSubscriptionSelection(
+ List<SubscriptionAnnotation> subAnnoList) {
+ return (subAnnoList == null) ? null :
+ subAnnoList.stream()
+ .filter(SubscriptionAnnotation::isDisplayAllowed)
+ .filter(SubscriptionAnnotation::isActive)
+ .findFirst().orElse(null);
+ }
+
+ protected SubscriptionInfo getSubscriptionOrDefault(int subscriptionId) {
+ return getSubscription(subscriptionId,
+ (subscriptionId != SUB_ID_NULL) ? null : (
+ subAnnoList -> defaultSubscriptionSelection(subAnnoList)
+ ));
}
/**
* Get the current subscription to display. First check whether intent has {@link
- * Settings#EXTRA_SUB_ID} and if so find the subscription with that id. If not, just return the
- * first one in the mSubscriptionInfos list since it is already sorted by sim slot.
+ * Settings#EXTRA_SUB_ID} and if so find the subscription with that id.
+ * If not, select default one based on {@link Function} provided.
+ *
+ * @param preferredSubscriptionId preferred subscription id
+ * @param selectionOfDefault when true current subscription is absent
*/
@VisibleForTesting
- SubscriptionInfo getSubscription() {
+ protected SubscriptionInfo getSubscription(int preferredSubscriptionId,
+ Function<List<SubscriptionAnnotation>, SubscriptionAnnotation> selectionOfDefault) {
List<SubscriptionAnnotation> subList =
(new SelectableSubscriptions(this, true)).call();
- SubscriptionAnnotation currentSubInfo = null;
- if (mCurSubscriptionId != SUB_ID_NULL) {
- currentSubInfo = subList.stream()
- .filter(SubscriptionAnnotation::isDisplayAllowed)
- .filter(subAnno -> (subAnno.getSubscriptionId() == mCurSubscriptionId))
- .findFirst().orElse(null);
- }
- if (currentSubInfo == null) {
- currentSubInfo = subList.stream()
- .filter(SubscriptionAnnotation::isDisplayAllowed)
- .filter(SubscriptionAnnotation::isActive)
- .findFirst().orElse(null);
+ Log.d(TAG, "get subId=" + preferredSubscriptionId + " from " + subList);
+ SubscriptionAnnotation currentSubInfo = subList.stream()
+ .filter(SubscriptionAnnotation::isDisplayAllowed)
+ .filter(subAnno -> (subAnno.getSubscriptionId() == preferredSubscriptionId))
+ .findFirst().orElse(null);
+ if ((currentSubInfo == null) && (selectionOfDefault != null)) {
+ currentSubInfo = selectionOfDefault.apply(subList);
}
return (currentSubInfo == null) ? null : currentSubInfo.getSubInfo();
}
@@ -285,10 +355,6 @@
final String fragmentTag = buildFragmentTag(subId);
if (fragmentManager.findFragmentByTag(fragmentTag) != null) {
- if (!mFragmentForceReload) {
- Log.d(TAG, "Keep current fragment: " + fragmentTag);
- return;
- }
Log.d(TAG, "Construct fragment: " + fragmentTag);
}
diff --git a/src/com/android/settings/network/telephony/MobileNetworkUtils.java b/src/com/android/settings/network/telephony/MobileNetworkUtils.java
index bad650d..ebf77a0 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkUtils.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkUtils.java
@@ -84,6 +84,8 @@
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
public class MobileNetworkUtils {
@@ -258,9 +260,16 @@
public static boolean showEuiccSettings(Context context) {
long timeForAccess = SystemClock.elapsedRealtime();
try {
- return ((Future<Boolean>) ThreadUtils.postOnBackgroundThread(()
- -> showEuiccSettingsDetecting(context))).get();
- } catch (ExecutionException | InterruptedException exception) {
+ Boolean isShow = ((Future<Boolean>) ThreadUtils.postOnBackgroundThread(() -> {
+ try {
+ return showEuiccSettingsDetecting(context);
+ } catch (Exception threadException) {
+ Log.w(TAG, "Accessing Euicc failure", threadException);
+ }
+ return Boolean.FALSE;
+ })).get(3, TimeUnit.SECONDS);
+ return ((isShow != null) && isShow.booleanValue());
+ } catch (ExecutionException | InterruptedException | TimeoutException exception) {
timeForAccess = SystemClock.elapsedRealtime() - timeForAccess;
Log.w(TAG, "Accessing Euicc takes too long: +" + timeForAccess + "ms");
}
diff --git a/src/com/android/settings/sim/SimDialogActivity.java b/src/com/android/settings/sim/SimDialogActivity.java
index 252b6c0..e5457ae 100644
--- a/src/com/android/settings/sim/SimDialogActivity.java
+++ b/src/com/android/settings/sim/SimDialogActivity.java
@@ -24,6 +24,7 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
+import android.view.WindowManager;
import android.widget.Toast;
import androidx.fragment.app.Fragment;
@@ -59,6 +60,8 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ getWindow().addSystemFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
showOrUpdateDialog();
}