Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cfd023dc authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[Catalyst] Migrate Airplane Mode preference" into main

parents e0c04764 efbe144a
Loading
Loading
Loading
Loading
+23 −8
Original line number Diff line number Diff line
@@ -147,9 +147,24 @@ public class AirplaneModeEnabler extends GlobalSettingsChangeListener {
     * @return any subscription within device is under ECM mode
     */
    public boolean isInEcmMode() {
        return isInEcmMode(mContext, mTelephonyManager);
    }

    /**
     * Check the status of ECM mode
     *
     * @param context Caller's {@link Context}
     * @param telephonyManager The default {@link TelephonyManager}
     *
     * @return any subscription within device is under ECM mode
     */
    public static boolean isInEcmMode(Context context, TelephonyManager telephonyManager) {
        if (context == null || telephonyManager == null) {
            return false;
        }
        if (Flags.enforceTelephonyFeatureMappingForPublicApis()) {
            try {
                if (mTelephonyManager.getEmergencyCallbackMode()) {
                if (telephonyManager.getEmergencyCallbackMode()) {
                    return true;
                }
            } catch (UnsupportedOperationException e) {
@@ -157,26 +172,26 @@ public class AirplaneModeEnabler extends GlobalSettingsChangeListener {
                // Ignore exception, device is not in ECM mode.
            }
        } else {
            if (mTelephonyManager.getEmergencyCallbackMode()) {
            if (telephonyManager.getEmergencyCallbackMode()) {
                return true;
            }
        }
        final List<SubscriptionInfo> subInfoList =
                ProxySubscriptionManager.getInstance(mContext).getActiveSubscriptionsInfo();
                ProxySubscriptionManager.getInstance(context).getActiveSubscriptionsInfo();
        if (subInfoList == null) {
            return false;
        }
        for (SubscriptionInfo subInfo : subInfoList) {
            final TelephonyManager telephonyManager =
                    mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId());
            if (telephonyManager != null) {
            final TelephonyManager telephonyManagerForSubId =
                    telephonyManager.createForSubscriptionId(subInfo.getSubscriptionId());
            if (telephonyManagerForSubId != null) {
                if (!Flags.enforceTelephonyFeatureMappingForPublicApis()) {
                    if (telephonyManager.getEmergencyCallbackMode()) {
                    if (telephonyManagerForSubId.getEmergencyCallbackMode()) {
                        return true;
                    }
                } else {
                    try {
                        if (telephonyManager.getEmergencyCallbackMode()) {
                        if (telephonyManagerForSubId.getEmergencyCallbackMode()) {
                            return true;
                        }
                    } catch (UnsupportedOperationException e) {
+160 −6
Original line number Diff line number Diff line
@@ -16,34 +16,188 @@

package com.android.settings.network

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Looper
import android.os.UserHandle
import android.os.UserManager
import android.provider.Settings
import android.telephony.PhoneStateListener
import android.telephony.TelephonyManager
import android.util.Log
import androidx.annotation.DrawableRes
import androidx.preference.Preference
import com.android.settings.AirplaneModeEnabler
import com.android.settings.PreferenceRestrictionMixin
import com.android.settings.R
import com.android.settings.Utils
import com.android.settingslib.RestrictedSwitchPreference
import com.android.settingslib.datastore.AbstractKeyedDataObservable
import com.android.settingslib.datastore.DataChangeReason
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.SettingsGlobalStore
import com.android.settingslib.datastore.SettingsStore
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.ReadWritePermit
import com.android.settingslib.metadata.SensitivityLevel
import com.android.settingslib.metadata.SwitchPreference
import com.android.settingslib.preference.SwitchPreferenceBinding
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

// LINT.IfChange
class AirplaneModePreference :
    SwitchPreference(KEY, R.string.airplane_mode), PreferenceAvailabilityProvider {
open class AirplaneModePreference :
    SwitchPreference(KEY, R.string.airplane_mode),
    SwitchPreferenceBinding,
    PreferenceAvailabilityProvider,
    PreferenceLifecycleProvider,
    PreferenceRestrictionMixin {

    override val icon: Int
        @DrawableRes get() = R.drawable.ic_airplanemode_active

    override fun storage(context: Context) = SettingsGlobalStore.get(context)
    override fun isAvailable(context: Context) =
        (context.resources.getBoolean(R.bool.config_show_toggle_airplane) &&
            !context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))

    override fun isEnabled(context: Context) = super<PreferenceRestrictionMixin>.isEnabled(context)

    override val restrictionKeys
        get() = arrayOf(UserManager.DISALLOW_AIRPLANE_MODE)

    override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
        ReadWritePermit.ALLOW

    override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
        when {
            isSatelliteOn(context) || isInEcmMode(context) -> ReadWritePermit.DISALLOW
            else -> ReadWritePermit.ALLOW
        }

    override val sensitivityLevel
        get() = SensitivityLevel.HIGH_SENSITIVITY

    override fun isAvailable(context: Context) =
        (context.resources.getBoolean(R.bool.config_show_toggle_airplane) &&
            !context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
    override fun storage(context: Context): KeyValueStore =
        AirplaneModeStorage(context, SettingsGlobalStore.get(context))

    @Suppress("DEPRECATION", "MissingPermission", "UNCHECKED_CAST")
    private class AirplaneModeStorage(
        private val context: Context,
        private val settingsStore: SettingsStore,
    ) : AbstractKeyedDataObservable<String>(), KeyValueStore {
        private var phoneStateListener: PhoneStateListener? = null

        override fun contains(key: String) =
            settingsStore.contains(KEY) &&
                context.getSystemService(TelephonyManager::class.java) != null

        override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) =
            DEFAULT_VALUE as T

        override fun <T : Any> getValue(key: String, valueType: Class<T>): T =
            (settingsStore.getBoolean(key) ?: DEFAULT_VALUE) as T

        override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
            if (value is Boolean) {
                settingsStore.setBoolean(key, value)

                val intent = Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED)
                intent.putExtra("state", value)
                context.sendBroadcastAsUser(intent, UserHandle.ALL)
            }
        }

        override fun onFirstObserverAdded() {
            context.getSystemService(TelephonyManager::class.java)?.let {
                phoneStateListener =
                    object : PhoneStateListener(Looper.getMainLooper()) {
                        @Deprecated("Deprecated in Java")
                        override fun onRadioPowerStateChanged(state: Int) {
                            Log.d(TAG, "onRadioPowerStateChanged(), state=$state")
                            notifyChange(KEY, DataChangeReason.UPDATE)
                        }
                    }
                it.listen(phoneStateListener, PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED)
            }
        }

        override fun onLastObserverRemoved() {
            context
                .getSystemService(TelephonyManager::class.java)
                ?.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
        }
    }

    override fun onCreate(context: PreferenceLifecycleContext) {
        context.requirePreference<RestrictedSwitchPreference>(KEY).onPreferenceChangeListener =
            Preference.OnPreferenceChangeListener { _: Preference, _: Any ->
                if (isInEcmMode(context)) {
                    showEcmDialog(context)
                    return@OnPreferenceChangeListener false
                }
                if (isSatelliteOn(context)) {
                    showSatelliteDialog(context)
                    return@OnPreferenceChangeListener false
                }
                return@OnPreferenceChangeListener true
            }
    }

    override fun onActivityResult(
        context: PreferenceLifecycleContext,
        requestCode: Int,
        resultCode: Int,
        data: Intent?,
    ): Boolean {
        if (requestCode == REQUEST_CODE_EXIT_ECM && resultCode == Activity.RESULT_OK) {
            storage(context).setValue(KEY, Boolean::class.javaObjectType, true)
        }
        return true
    }

    private fun isInEcmMode(context: Context) =
        AirplaneModeEnabler.isInEcmMode(
            context,
            context.getSystemService(TelephonyManager::class.java),
        )

    private fun isSatelliteOn(context: Context): Boolean {
        try {
            return SatelliteRepository(context)
                .requestIsSessionStarted(Executors.newSingleThreadExecutor())
                .get(2000, TimeUnit.MILLISECONDS)
        } catch (e: Exception) {
            Log.e(TAG, "Error to get satellite status : $e")
        }
        return false
    }

    private fun showEcmDialog(context: PreferenceLifecycleContext) {
        val intent =
            Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null)
                .setPackage(Utils.PHONE_PACKAGE_NAME)
        context.startActivityForResult(intent, REQUEST_CODE_EXIT_ECM, null)
    }

    private fun showSatelliteDialog(context: PreferenceLifecycleContext) {
        val intent =
            Intent(context, SatelliteWarningDialogActivity::class.java)
                .putExtra(
                    SatelliteWarningDialogActivity.EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG,
                    SatelliteWarningDialogActivity.TYPE_IS_AIRPLANE_MODE,
                )
        context.startActivity(intent)
    }

    companion object {
        const val TAG = "AirplaneModePreference"
        const val KEY = Settings.Global.AIRPLANE_MODE_ON
        const val DEFAULT_VALUE = false
        const val REQUEST_CODE_EXIT_ECM = 1

        fun Context.isAirplaneModeOn() = SettingsGlobalStore.get(this).getBoolean(KEY) == true
    }
+7 −3
Original line number Diff line number Diff line
@@ -59,7 +59,9 @@ public class NetworkDashboardFragment extends DashboardFragment implements
    public void onAttach(Context context) {
        super.onAttach(context);

        if (isCatalystEnabled()) {
            use(AirplaneModePreferenceController.class).setFragment(this);
        }
        use(NetworkProviderCallsSmsController.class).init(this);
    }

@@ -102,8 +104,10 @@ public class NetworkDashboardFragment extends DashboardFragment implements

        switch (requestCode) {
            case AirplaneModePreferenceController.REQUEST_CODE_EXIT_ECM:
                if (isCatalystEnabled()) {
                    use(AirplaneModePreferenceController.class)
                            .onActivityResult(requestCode, resultCode, data);
                }
                break;
        }
    }
+1 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ class NetworkDashboardScreen : PreferenceScreenCreator, PreferenceIconProvider {
    override fun getPreferenceHierarchy(context: Context) =
        preferenceHierarchy(this) {
            +MobileNetworkListScreen.KEY order -15
            +AirplaneModePreference() order -5
            +DataSaverScreen.KEY order 10
        }

+9 −0
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package com.android.settings.network;

import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;

import static com.android.settings.flags.Flags.FLAG_CATALYST_NETWORK_PROVIDER_AND_INTERNET_SCREEN;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
@@ -28,6 +32,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Looper;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.provider.SettingsSlicesContract;
import android.util.AndroidRuntimeException;
@@ -42,6 +47,7 @@ import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.RestrictedSwitchPreference;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -49,6 +55,8 @@ import org.mockito.MockitoAnnotations;

@RunWith(AndroidJUnit4.class)
public class AirplaneModePreferenceControllerTest {
    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);

    private static final int ON = 1;
    private static final int OFF = 0;
@@ -66,6 +74,7 @@ public class AirplaneModePreferenceControllerTest {

    @Before
    public void setUp() {
        mSetFlagsRule.disableFlags(FLAG_CATALYST_NETWORK_PROVIDER_AND_INTERNET_SCREEN);
        if (Looper.myLooper() == null) {
            Looper.prepare();
        }
Loading