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

Commit 0ff1bd36 authored by Tom Hsu's avatar Tom Hsu Committed by Android (Google) Code Review
Browse files

Merge "Shows a warning dialog avoid using BT, Wifi and APM." into main

parents 474a52ef 9d118960
Loading
Loading
Loading
Loading
+145 −0
Original line number Original line Diff line number Diff line
/*
 * 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.settingslib.satellite

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.OutcomeReceiver
import android.telephony.satellite.SatelliteManager
import android.util.Log
import android.view.WindowManager
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.android.settingslib.wifi.WifiUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.Default
import kotlinx.coroutines.Job
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeoutException
import kotlin.coroutines.resume

/** A util for Satellite dialog */
object SatelliteDialogUtils {

    /**
     * Uses to start Satellite dialog to prevent users from using the BT, Airplane Mode, and
     * Wifi during the satellite mode is on.
     */
    @JvmStatic
    fun mayStartSatelliteWarningDialog(
            context: Context,
            lifecycleOwner: LifecycleOwner,
            type: Int,
            allowClick: (isAllowed: Boolean) -> Unit
    ): Job {
        return mayStartSatelliteWarningDialog(
                context, lifecycleOwner.lifecycleScope, type, allowClick)
    }

    /**
     * Uses to start Satellite dialog to prevent users from using the BT, Airplane Mode, and
     * Wifi during the satellite mode is on.
     */
    @JvmStatic
    fun mayStartSatelliteWarningDialog(
            context: Context,
            coroutineScope: CoroutineScope,
            type: Int,
            allowClick: (isAllowed: Boolean) -> Unit
    ): Job =
            coroutineScope.launch {
                var isSatelliteModeOn = false
                try {
                    isSatelliteModeOn = requestIsEnabled(context)
                } catch (e: InterruptedException) {
                    Log.w(TAG, "Error to get satellite status : $e")
                } catch (e: ExecutionException) {
                    Log.w(TAG, "Error to get satellite status : $e")
                } catch (e: TimeoutException) {
                    Log.w(TAG, "Error to get satellite status : $e")
                }

                if (isSatelliteModeOn) {
                    startSatelliteWarningDialog(context, type)
                }
                withContext(Dispatchers.Main) {
                    allowClick(!isSatelliteModeOn)
                }
            }

    private fun startSatelliteWarningDialog(context: Context, type: Int) {
        context.startActivity(Intent(Intent.ACTION_MAIN).apply {
            component = ComponentName(
                    "com.android.settings",
                    "com.android.settings.network.SatelliteWarningDialogActivity"
            )
            putExtra(WifiUtils.DIALOG_WINDOW_TYPE,
                    WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
            putExtra(EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG, type)
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
        })
    }

    /**
     * Checks if the satellite modem is enabled.
     *
     * @param executor The executor to run the asynchronous operation on
     * @return A ListenableFuture that will resolve to `true` if the satellite modem enabled,
     *         `false` otherwise.
     */
    private suspend fun requestIsEnabled(
            context: Context,
    ): Boolean = withContext(Default) {
        val satelliteManager: SatelliteManager? =
                context.getSystemService(SatelliteManager::class.java)
        if (satelliteManager == null) {
            Log.w(TAG, "SatelliteManager is null")
            return@withContext false
        }

        suspendCancellableCoroutine {continuation ->
            satelliteManager?.requestIsEnabled(Default.asExecutor(),
                    object : OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> {
                        override fun onResult(result: Boolean) {
                            Log.i(TAG, "Satellite modem enabled status: $result")
                            continuation.resume(result)
                        }

                        override fun onError(error: SatelliteManager.SatelliteException) {
                            super.onError(error)
                            Log.w(TAG, "Can't get satellite modem enabled status", error)
                            continuation.resume(false)
                        }
                    })
        }
    }

    const val TAG = "SatelliteDialogUtils"

    const val EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG: String =
            "extra_type_of_satellite_warning_dialog"
    const val TYPE_IS_UNKNOWN = -1
    const val TYPE_IS_WIFI = 0
    const val TYPE_IS_BLUETOOTH = 1
    const val TYPE_IS_AIRPLANE_MODE = 2
}
 No newline at end of file
+4 −1
Original line number Original line Diff line number Diff line
@@ -41,7 +41,10 @@ android_app {
//###########################################################
//###########################################################
android_robolectric_test {
android_robolectric_test {
    name: "SettingsLibRoboTests",
    name: "SettingsLibRoboTests",
    srcs: ["src/**/*.java"],
    srcs: [
        "src/**/*.java",
        "src/**/*.kt",
    ],
    static_libs: [
    static_libs: [
        "Settings_robolectric_meta_service_file",
        "Settings_robolectric_meta_service_file",
        "Robolectric_shadows_androidx_fragment_upstream",
        "Robolectric_shadows_androidx_fragment_upstream",
+162 −0
Original line number Original line Diff line number Diff line
/*
 * 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.settingslib.satellite

import android.content.Context
import android.content.Intent
import android.os.OutcomeReceiver
import android.platform.test.annotations.RequiresFlagsEnabled
import android.telephony.satellite.SatelliteManager
import android.telephony.satellite.SatelliteManager.SatelliteException
import android.util.AndroidRuntimeException
import androidx.test.core.app.ApplicationProvider
import com.android.internal.telephony.flags.Flags
import com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_WIFI
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.`when`
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.Mockito.verify
import org.mockito.internal.verification.Times
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class SatelliteDialogUtilsTest {
    @JvmField
    @Rule
    val mockitoRule: MockitoRule = MockitoJUnit.rule()

    @Spy
    var context: Context = ApplicationProvider.getApplicationContext()
    @Mock
    private lateinit var satelliteManager: SatelliteManager

    private val coroutineScope = CoroutineScope(Dispatchers.Main)

    @Before
    fun setUp() {
        `when`(context.getSystemService(SatelliteManager::class.java))
                .thenReturn(satelliteManager)
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
    fun mayStartSatelliteWarningDialog_satelliteIsOn_showWarningDialog() = runBlocking {
        `when`(
                satelliteManager.requestIsEnabled(
                        any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>()
                )
        )
                .thenAnswer { invocation ->
                    val receiver = invocation
                            .getArgument<
                                    OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>(
                                    1
                            )
                    receiver.onResult(true)
                    null
                }

        try {
            SatelliteDialogUtils.mayStartSatelliteWarningDialog(
                    context, coroutineScope, TYPE_IS_WIFI, allowClick = {
                        assertTrue(it)
                })
        } catch (e: AndroidRuntimeException) {
            // Catch exception of starting activity .
        }
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
    fun mayStartSatelliteWarningDialog_satelliteIsOff_notShowWarningDialog() = runBlocking {
        `when`(
                satelliteManager.requestIsEnabled(
                        any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>()
                )
        )
                .thenAnswer { invocation ->
                    val receiver = invocation
                            .getArgument<
                                    OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>(
                                    1
                            )
                    receiver.onResult(false)
                    null
                }


        SatelliteDialogUtils.mayStartSatelliteWarningDialog(
            context, coroutineScope, TYPE_IS_WIFI, allowClick = {
                assertFalse(it)
            })

        verify(context, Times(0)).startActivity(any<Intent>())
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
    fun mayStartSatelliteWarningDialog_noSatelliteManager_notShowWarningDialog() = runBlocking {
        `when`(context.getSystemService(SatelliteManager::class.java))
                .thenReturn(null)

        SatelliteDialogUtils.mayStartSatelliteWarningDialog(
            context, coroutineScope, TYPE_IS_WIFI, allowClick = {
                assertFalse(it)
            })

        verify(context, Times(0)).startActivity(any<Intent>())
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
    fun mayStartSatelliteWarningDialog_satelliteErrorResult_notShowWarningDialog() = runBlocking {
        `when`(
                satelliteManager.requestIsEnabled(
                        any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>()
                )
        )
                .thenAnswer { invocation ->
                    val receiver = invocation
                            .getArgument<
                                    OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>(
                                    1
                            )
                    receiver.onError(SatelliteException(SatelliteManager.SATELLITE_RESULT_ERROR))
                    null
                }


        SatelliteDialogUtils.mayStartSatelliteWarningDialog(
            context, coroutineScope, TYPE_IS_WIFI, allowClick = {
                assertFalse(it)
            })

        verify(context, Times(0)).startActivity(any<Intent>())
    }
}
+25 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


package com.android.systemui.qs.tiles;
package com.android.systemui.qs.tiles;


import static com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_AIRPLANE_MODE;

import android.content.BroadcastReceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
@@ -32,9 +34,12 @@ import android.telephony.TelephonyManager;
import android.widget.Switch;
import android.widget.Switch;


import androidx.annotation.Nullable;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;


import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.telephony.flags.Flags;
import com.android.settingslib.satellite.SatelliteDialogUtils;
import com.android.systemui.animation.Expandable;
import com.android.systemui.animation.Expandable;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Background;
@@ -54,6 +59,8 @@ import com.android.systemui.util.settings.GlobalSettings;


import dagger.Lazy;
import dagger.Lazy;


import kotlinx.coroutines.Job;

import javax.inject.Inject;
import javax.inject.Inject;


/** Quick settings tile: Airplane mode **/
/** Quick settings tile: Airplane mode **/
@@ -66,6 +73,9 @@ public class AirplaneModeTile extends QSTileImpl<BooleanState> {
    private final Lazy<ConnectivityManager> mLazyConnectivityManager;
    private final Lazy<ConnectivityManager> mLazyConnectivityManager;


    private boolean mListening;
    private boolean mListening;
    @Nullable
    @VisibleForTesting
    Job mClickJob;


    @Inject
    @Inject
    public AirplaneModeTile(
    public AirplaneModeTile(
@@ -111,6 +121,21 @@ public class AirplaneModeTile extends QSTileImpl<BooleanState> {
                    new Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS), 0);
                    new Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS), 0);
            return;
            return;
        }
        }

        if (Flags.oemEnabledSatelliteFlag()) {
            if (mClickJob != null && !mClickJob.isCompleted()) {
                return;
            }
            mClickJob = SatelliteDialogUtils.mayStartSatelliteWarningDialog(
                    mContext, this, TYPE_IS_AIRPLANE_MODE, isAllowClick -> {
                        if (isAllowClick) {
                            setEnabled(!airplaneModeEnabled);
                        }
                        return null;
                    });
            return;
        }

        setEnabled(!airplaneModeEnabled);
        setEnabled(!airplaneModeEnabled);
    }
    }


+27 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.systemui.qs.tiles;
package com.android.systemui.qs.tiles;


import static com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_BLUETOOTH;
import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;


import android.annotation.Nullable;
import android.annotation.Nullable;
@@ -33,11 +34,14 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.Log;
import android.widget.Switch;
import android.widget.Switch;


import androidx.annotation.VisibleForTesting;

import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.satellite.SatelliteDialogUtils;
import com.android.systemui.animation.Expandable;
import com.android.systemui.animation.Expandable;
import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogViewModel;
import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogViewModel;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Background;
@@ -55,6 +59,8 @@ import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.BluetoothController;


import kotlinx.coroutines.Job;

import java.util.List;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executor;


@@ -78,6 +84,9 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
    private final BluetoothTileDialogViewModel mDialogViewModel;
    private final BluetoothTileDialogViewModel mDialogViewModel;


    private final FeatureFlags mFeatureFlags;
    private final FeatureFlags mFeatureFlags;
    @Nullable
    @VisibleForTesting
    Job mClickJob;


    @Inject
    @Inject
    public BluetoothTile(
    public BluetoothTile(
@@ -110,6 +119,24 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {


    @Override
    @Override
    protected void handleClick(@Nullable Expandable expandable) {
    protected void handleClick(@Nullable Expandable expandable) {
        if (com.android.internal.telephony.flags.Flags.oemEnabledSatelliteFlag()) {
            if (mClickJob != null && !mClickJob.isCompleted()) {
                return;
            }
            mClickJob = SatelliteDialogUtils.mayStartSatelliteWarningDialog(
                    mContext, this, TYPE_IS_BLUETOOTH, isAllowClick -> {
                        if (!isAllowClick) {
                            return null;
                        }
                        handleClickEvent(expandable);
                        return null;
                    });
            return;
        }
        handleClickEvent(expandable);
    }

    private void handleClickEvent(@Nullable Expandable expandable) {
        if (mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG)) {
        if (mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG)) {
            mDialogViewModel.showDialog(expandable);
            mDialogViewModel.showDialog(expandable);
        } else {
        } else {
Loading