Merge "Create Flashlight Prebuilt quick affordance" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
b96b7ad9e9
@@ -0,0 +1,25 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 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
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#1f1f1f"
|
||||
android:pathData="M8,22V11L6,8V2H18V8L16,11V22ZM12,15.5Q11.375,15.5 10.938,15.062Q10.5,14.625 10.5,14Q10.5,13.375 10.938,12.938Q11.375,12.5 12,12.5Q12.625,12.5 13.062,12.938Q13.5,13.375 13.5,14Q13.5,14.625 13.062,15.062Q12.625,15.5 12,15.5ZM8,5H16V4H8ZM16,7H8V7.4L10,10.4V20H14V10.4L16,7.4ZM12,12Z"/>
|
||||
</vector>
|
||||
25
packages/SystemUI/res-keyguard/drawable/ic_flashlight_on.xml
Normal file
25
packages/SystemUI/res-keyguard/drawable/ic_flashlight_on.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 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
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#1f1f1f"
|
||||
android:pathData="M6,5V2H18V5ZM12,15.5Q12.625,15.5 13.062,15.062Q13.5,14.625 13.5,14Q13.5,13.375 13.062,12.938Q12.625,12.5 12,12.5Q11.375,12.5 10.938,12.938Q10.5,13.375 10.5,14Q10.5,14.625 10.938,15.062Q11.375,15.5 12,15.5ZM8,22V11L6,8V7H18V8L16,11V22Z"/>
|
||||
</vector>
|
||||
@@ -25,6 +25,7 @@ package com.android.systemui.keyguard.data.quickaffordance
|
||||
object BuiltInKeyguardQuickAffordanceKeys {
|
||||
// Please keep alphabetical order of const names to simplify future maintenance.
|
||||
const val CAMERA = "camera"
|
||||
const val FLASHLIGHT = "flashlight"
|
||||
const val HOME_CONTROLS = "home"
|
||||
const val QR_CODE_SCANNER = "qr_code_scanner"
|
||||
const val QUICK_ACCESS_WALLET = "wallet"
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.systemui.keyguard.data.quickaffordance
|
||||
|
||||
import android.content.Context
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.animation.Expandable
|
||||
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||
import com.android.systemui.common.shared.model.ContentDescription
|
||||
import com.android.systemui.common.shared.model.Icon
|
||||
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
|
||||
import com.android.systemui.statusbar.policy.FlashlightController
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
|
||||
@SysUISingleton
|
||||
class FlashlightQuickAffordanceConfig @Inject constructor(
|
||||
@Application private val context: Context,
|
||||
private val flashlightController: FlashlightController,
|
||||
) : KeyguardQuickAffordanceConfig {
|
||||
|
||||
private sealed class FlashlightState {
|
||||
|
||||
abstract fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState
|
||||
|
||||
object On: FlashlightState() {
|
||||
override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
|
||||
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
|
||||
Icon.Resource(
|
||||
R.drawable.ic_flashlight_on,
|
||||
ContentDescription.Resource(R.string.quick_settings_flashlight_label)
|
||||
),
|
||||
ActivationState.Active
|
||||
)
|
||||
}
|
||||
|
||||
object OffAvailable: FlashlightState() {
|
||||
override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
|
||||
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
|
||||
Icon.Resource(
|
||||
R.drawable.ic_flashlight_off,
|
||||
ContentDescription.Resource(R.string.quick_settings_flashlight_label)
|
||||
),
|
||||
ActivationState.Inactive
|
||||
)
|
||||
}
|
||||
|
||||
object Unavailable: FlashlightState() {
|
||||
override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
|
||||
KeyguardQuickAffordanceConfig.LockScreenState.Hidden
|
||||
}
|
||||
}
|
||||
|
||||
override val key: String
|
||||
get() = BuiltInKeyguardQuickAffordanceKeys.FLASHLIGHT
|
||||
|
||||
override val pickerName: String
|
||||
get() = context.getString(R.string.quick_settings_flashlight_label)
|
||||
|
||||
override val pickerIconResourceId: Int
|
||||
get() = if (flashlightController.isEnabled) {
|
||||
R.drawable.ic_flashlight_on
|
||||
} else {
|
||||
R.drawable.ic_flashlight_off
|
||||
}
|
||||
|
||||
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
|
||||
conflatedCallbackFlow {
|
||||
val flashlightCallback = object : FlashlightController.FlashlightListener {
|
||||
override fun onFlashlightChanged(enabled: Boolean) {
|
||||
trySendWithFailureLogging(
|
||||
if (enabled) {
|
||||
FlashlightState.On.toLockScreenState()
|
||||
} else {
|
||||
FlashlightState.OffAvailable.toLockScreenState()
|
||||
},
|
||||
TAG
|
||||
)
|
||||
}
|
||||
|
||||
override fun onFlashlightError() {
|
||||
trySendWithFailureLogging(FlashlightState.OffAvailable.toLockScreenState(), TAG)
|
||||
}
|
||||
|
||||
override fun onFlashlightAvailabilityChanged(available: Boolean) {
|
||||
trySendWithFailureLogging(
|
||||
if (!available) {
|
||||
FlashlightState.Unavailable.toLockScreenState()
|
||||
} else {
|
||||
if (flashlightController.isEnabled) {
|
||||
FlashlightState.On.toLockScreenState()
|
||||
} else {
|
||||
FlashlightState.OffAvailable.toLockScreenState()
|
||||
}
|
||||
},
|
||||
TAG
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
flashlightController.addCallback(flashlightCallback)
|
||||
|
||||
awaitClose {
|
||||
flashlightController.removeCallback(flashlightCallback)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTriggered(expandable: Expandable?):
|
||||
KeyguardQuickAffordanceConfig.OnTriggeredResult {
|
||||
flashlightController
|
||||
.setFlashlight(flashlightController.isAvailable && !flashlightController.isEnabled)
|
||||
return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
|
||||
}
|
||||
|
||||
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState =
|
||||
if (flashlightController.isAvailable) {
|
||||
KeyguardQuickAffordanceConfig.PickerScreenState.Default
|
||||
} else {
|
||||
KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "FlashlightQuickAffordanceConfig"
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ object KeyguardDataQuickAffordanceModule {
|
||||
@Provides
|
||||
@ElementsIntoSet
|
||||
fun quickAffordanceConfigs(
|
||||
flashlight: FlashlightQuickAffordanceConfig,
|
||||
home: HomeControlsKeyguardQuickAffordanceConfig,
|
||||
quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
|
||||
qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig,
|
||||
@@ -33,6 +34,7 @@ object KeyguardDataQuickAffordanceModule {
|
||||
): Set<KeyguardQuickAffordanceConfig> {
|
||||
return setOf(
|
||||
camera,
|
||||
flashlight,
|
||||
home,
|
||||
quickAccessWallet,
|
||||
qrCodeScanner,
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.systemui.keyguard.data.quickaffordance
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.common.shared.model.Icon
|
||||
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
|
||||
import com.android.systemui.statusbar.policy.FlashlightController
|
||||
import com.android.systemui.utils.leaks.FakeFlashlightController
|
||||
import com.android.systemui.utils.leaks.LeakCheckedTest
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@RunWith(JUnit4::class)
|
||||
class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() {
|
||||
|
||||
@Mock private lateinit var context: Context
|
||||
private lateinit var flashlightController: FakeFlashlightController
|
||||
private lateinit var underTest : FlashlightQuickAffordanceConfig
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
injectLeakCheckedDependency(FlashlightController::class.java)
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
flashlightController = SysuiLeakCheck().getLeakChecker(FlashlightController::class.java) as FakeFlashlightController
|
||||
underTest = FlashlightQuickAffordanceConfig(context, flashlightController)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `flashlight is off -- triggered -- icon is on and active`() = runTest {
|
||||
//given
|
||||
flashlightController.isEnabled = false
|
||||
flashlightController.isAvailable = true
|
||||
val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
|
||||
val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
|
||||
|
||||
//when
|
||||
underTest.onTriggered(null)
|
||||
val lastValue = values.last()
|
||||
|
||||
//then
|
||||
assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
|
||||
assertEquals(R.drawable.ic_flashlight_on,
|
||||
((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res)
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `flashlight is on -- triggered -- icon is off and inactive`() = runTest {
|
||||
//given
|
||||
flashlightController.isEnabled = true
|
||||
flashlightController.isAvailable = true
|
||||
val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
|
||||
val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
|
||||
|
||||
//when
|
||||
underTest.onTriggered(null)
|
||||
val lastValue = values.last()
|
||||
|
||||
//then
|
||||
assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
|
||||
assertEquals(R.drawable.ic_flashlight_off,
|
||||
((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res)
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `flashlight is on -- receives error -- icon is off and inactive`() = runTest {
|
||||
//given
|
||||
flashlightController.isEnabled = true
|
||||
flashlightController.isAvailable = false
|
||||
val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
|
||||
val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
|
||||
|
||||
//when
|
||||
flashlightController.onFlashlightError()
|
||||
val lastValue = values.last()
|
||||
|
||||
//then
|
||||
assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
|
||||
assertEquals(R.drawable.ic_flashlight_off,
|
||||
((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res)
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `flashlight availability now off -- hidden`() = runTest {
|
||||
//given
|
||||
flashlightController.isEnabled = true
|
||||
flashlightController.isAvailable = false
|
||||
val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
|
||||
val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
|
||||
|
||||
//when
|
||||
flashlightController.onFlashlightAvailabilityChanged(false)
|
||||
val lastValue = values.last()
|
||||
|
||||
//then
|
||||
assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `flashlight availability now on -- flashlight on -- inactive and icon off`() = runTest {
|
||||
//given
|
||||
flashlightController.isEnabled = true
|
||||
flashlightController.isAvailable = false
|
||||
val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
|
||||
val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
|
||||
|
||||
//when
|
||||
flashlightController.onFlashlightAvailabilityChanged(true)
|
||||
val lastValue = values.last()
|
||||
|
||||
//then
|
||||
assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
|
||||
assertTrue((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState is ActivationState.Active)
|
||||
assertEquals(R.drawable.ic_flashlight_on, (lastValue.icon as? Icon.Resource)?.res)
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `flashlight availability now on -- flashlight off -- inactive and icon off`() = runTest {
|
||||
//given
|
||||
flashlightController.isEnabled = false
|
||||
flashlightController.isAvailable = false
|
||||
val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
|
||||
val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
|
||||
|
||||
//when
|
||||
flashlightController.onFlashlightAvailabilityChanged(true)
|
||||
val lastValue = values.last()
|
||||
|
||||
//then
|
||||
assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
|
||||
assertTrue((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState is ActivationState.Inactive)
|
||||
assertEquals(R.drawable.ic_flashlight_off, (lastValue.icon as? Icon.Resource)?.res)
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `flashlight available -- picker state default`() = runTest {
|
||||
//given
|
||||
flashlightController.isAvailable = true
|
||||
|
||||
//when
|
||||
val result = underTest.getPickerScreenState()
|
||||
|
||||
//then
|
||||
assertTrue(result is KeyguardQuickAffordanceConfig.PickerScreenState.Default)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `flashlight not available -- picker state unavailable`() = runTest {
|
||||
//given
|
||||
flashlightController.isAvailable = false
|
||||
|
||||
//when
|
||||
val result = underTest.getPickerScreenState()
|
||||
|
||||
//then
|
||||
assertTrue(result is KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
|
||||
}
|
||||
}
|
||||
@@ -16,32 +16,71 @@ package com.android.systemui.utils.leaks;
|
||||
|
||||
import android.testing.LeakCheck;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.systemui.statusbar.policy.FlashlightController;
|
||||
import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FakeFlashlightController extends BaseLeakChecker<FlashlightListener>
|
||||
implements FlashlightController {
|
||||
|
||||
private final List<FlashlightListener> callbacks = new ArrayList<>();
|
||||
|
||||
@VisibleForTesting
|
||||
public boolean isAvailable;
|
||||
@VisibleForTesting
|
||||
public boolean isEnabled;
|
||||
@VisibleForTesting
|
||||
public boolean hasFlashlight;
|
||||
|
||||
public FakeFlashlightController(LeakCheck test) {
|
||||
super(test, "flashlight");
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void onFlashlightAvailabilityChanged(boolean newValue) {
|
||||
callbacks.forEach(
|
||||
flashlightListener -> flashlightListener.onFlashlightAvailabilityChanged(newValue)
|
||||
);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void onFlashlightError() {
|
||||
callbacks.forEach(FlashlightListener::onFlashlightError);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasFlashlight() {
|
||||
return false;
|
||||
return hasFlashlight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlashlight(boolean newState) {
|
||||
|
||||
callbacks.forEach(flashlightListener -> flashlightListener.onFlashlightChanged(newState));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return false;
|
||||
return isAvailable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
return isEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCallback(FlashlightListener listener) {
|
||||
super.addCallback(listener);
|
||||
callbacks.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCallback(FlashlightListener listener) {
|
||||
super.removeCallback(listener);
|
||||
callbacks.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user