Add support for BooleanState in CustomTile
Added API for a TileService to declare that they are a boolean tile. In that case, the tiles behave like frameworks tiles when using TB. Test: atest CustomTileTest Test: manual, added to DevelopmentTiles and observe behavior Test: manual, Grayscale behavior doesn't change Bug: 138579147 Change-Id: I2f2dae90a49897015138ab9615a4a556da623358
This commit is contained in:
@@ -41849,6 +41849,7 @@ package android.service.quicksettings {
|
||||
field public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
|
||||
field public static final String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
|
||||
field public static final String META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE";
|
||||
field public static final String META_DATA_BOOLEAN_TILE = "android.service.quicksettings.BOOLEAN_TILE";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -125,12 +125,30 @@ public class TileService extends Service {
|
||||
public static final String META_DATA_ACTIVE_TILE
|
||||
= "android.service.quicksettings.ACTIVE_TILE";
|
||||
|
||||
/**
|
||||
* Meta-data for a tile to support {@code BooleanState}.
|
||||
* <p>
|
||||
* BooleanState is for tiles that should support switch tile behavior in accessibility. This is
|
||||
* the behavior of most of the framework tiles.
|
||||
*
|
||||
* To make a TileService support BooleanState, set this meta-data to true on the TileService's
|
||||
* manifest declaration.
|
||||
* <pre class="prettyprint">
|
||||
* {@literal
|
||||
* <meta-data android:name="android.service.quicksettings.BOOLEAN_TILE"
|
||||
* android:value="true" />
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public static final String META_DATA_BOOLEAN_TILE =
|
||||
"android.service.quicksettings.BOOLEAN_TILE";
|
||||
|
||||
/**
|
||||
* Used to notify SysUI that Listening has be requested.
|
||||
* @hide
|
||||
*/
|
||||
public static final String ACTION_REQUEST_LISTENING
|
||||
= "android.service.quicksettings.action.REQUEST_LISTENING";
|
||||
public static final String ACTION_REQUEST_LISTENING =
|
||||
"android.service.quicksettings.action.REQUEST_LISTENING";
|
||||
|
||||
/**
|
||||
* @hide
|
||||
|
||||
@@ -186,6 +186,8 @@ public interface QSTile {
|
||||
return toStringBuilder().toString();
|
||||
}
|
||||
|
||||
// Used in dumps to determine current state of a tile.
|
||||
// This string may be used for CTS testing of tiles, so removing elements is discouraged.
|
||||
protected StringBuilder toStringBuilder() {
|
||||
final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
|
||||
sb.append(",icon=").append(icon);
|
||||
|
||||
@@ -39,6 +39,7 @@ import android.text.format.DateUtils;
|
||||
import android.util.Log;
|
||||
import android.view.IWindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.widget.Switch;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.systemui.Dependency;
|
||||
@@ -82,6 +83,11 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
|
||||
mTile = new Tile();
|
||||
updateDefaultTileAndIcon();
|
||||
mServiceManager = host.getTileServices().getTileWrapper(this);
|
||||
if (mServiceManager.isBooleanTile()) {
|
||||
// Replace states with BooleanState
|
||||
resetStates();
|
||||
}
|
||||
|
||||
mService = mServiceManager.getTileService();
|
||||
mServiceManager.setTileChangeListener(this);
|
||||
mUser = ActivityManager.getCurrentUser();
|
||||
@@ -246,8 +252,10 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
|
||||
|
||||
@Override
|
||||
public State newTileState() {
|
||||
State state = new State();
|
||||
return state;
|
||||
if (mServiceManager != null && mServiceManager.isBooleanTile()) {
|
||||
return new BooleanState();
|
||||
}
|
||||
return new State();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -336,6 +344,12 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
|
||||
} else {
|
||||
state.contentDescription = state.label;
|
||||
}
|
||||
|
||||
if (state instanceof BooleanState) {
|
||||
state.expandedAccessibilityClassName = Switch.class.getName();
|
||||
((BooleanState) state).value = (state.state == Tile.STATE_ACTIVE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -130,6 +130,24 @@ public class TileLifecycleManager extends BroadcastReceiver implements
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the associated TileService is a Boolean Tile.
|
||||
*
|
||||
* @return true if {@link TileService#META_DATA_BOOLEAN_TILE} is set to {@code true} for this
|
||||
* tile
|
||||
* @see TileService#META_DATA_BOOLEAN_TILE
|
||||
*/
|
||||
public boolean isBooleanTile() {
|
||||
try {
|
||||
ServiceInfo info = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(),
|
||||
PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA);
|
||||
return info.metaData != null
|
||||
&& info.metaData.getBoolean(TileService.META_DATA_BOOLEAN_TILE, false);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds just long enough to send any queued messages, then unbinds.
|
||||
*/
|
||||
|
||||
@@ -123,6 +123,10 @@ public class TileServiceManager {
|
||||
return mStateManager.isActiveTile();
|
||||
}
|
||||
|
||||
public boolean isBooleanTile() {
|
||||
return mStateManager.isBooleanTile();
|
||||
}
|
||||
|
||||
public void setShowingDialog(boolean dialog) {
|
||||
mShowingDialog = dialog;
|
||||
}
|
||||
|
||||
@@ -139,6 +139,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
|
||||
mQSSettingsPanelOption = QSSettingsControllerKt.getQSSettingsPanelOption();
|
||||
}
|
||||
|
||||
protected final void resetStates() {
|
||||
mState = newTileState();
|
||||
mTmpState = newTileState();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Lifecycle getLifecycle() {
|
||||
@@ -629,6 +634,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps the state of this tile along with its name.
|
||||
*
|
||||
* This may be used for CTS testing of tiles.
|
||||
*/
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
pw.println(this.getClass().getSimpleName() + ":");
|
||||
|
||||
128
packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
vendored
Normal file
128
packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.qs.external
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.Icon
|
||||
import android.service.quicksettings.IQSTileService
|
||||
import android.service.quicksettings.Tile
|
||||
import android.test.suitebuilder.annotation.SmallTest
|
||||
import android.view.IWindowManager
|
||||
import androidx.test.runner.AndroidJUnit4
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.plugins.qs.QSTile
|
||||
import com.android.systemui.qs.QSTileHost
|
||||
import junit.framework.Assert.assertFalse
|
||||
import junit.framework.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.ArgumentMatchers.anyString
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.any
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class CustomTileTest : SysuiTestCase() {
|
||||
|
||||
companion object {
|
||||
const val packageName = "test_package"
|
||||
const val className = "test_class"
|
||||
val componentName = ComponentName(packageName, className)
|
||||
val TILE_SPEC = CustomTile.toSpec(componentName)
|
||||
}
|
||||
|
||||
@Mock private lateinit var mTileHost: QSTileHost
|
||||
@Mock private lateinit var mTileService: IQSTileService
|
||||
@Mock private lateinit var mTileServices: TileServices
|
||||
@Mock private lateinit var mTileServiceManager: TileServiceManager
|
||||
@Mock private lateinit var mWindowService: IWindowManager
|
||||
@Mock private lateinit var mPackageManager: PackageManager
|
||||
@Mock private lateinit var mApplicationInfo: ApplicationInfo
|
||||
@Mock private lateinit var mServiceInfo: ServiceInfo
|
||||
|
||||
private lateinit var customTile: CustomTile
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
mContext.addMockSystemService("window", mWindowService)
|
||||
mContext.setMockPackageManager(mPackageManager)
|
||||
`when`(mTileHost.tileServices).thenReturn(mTileServices)
|
||||
`when`(mTileHost.context).thenReturn(mContext)
|
||||
`when`(mTileServices.getTileWrapper(any(CustomTile::class.java)))
|
||||
.thenReturn(mTileServiceManager)
|
||||
`when`(mTileServiceManager.tileService).thenReturn(mTileService)
|
||||
`when`(mPackageManager.getApplicationInfo(anyString(), anyInt()))
|
||||
.thenReturn(mApplicationInfo)
|
||||
|
||||
`when`(mPackageManager.getServiceInfo(any(ComponentName::class.java), anyInt()))
|
||||
.thenReturn(mServiceInfo)
|
||||
mServiceInfo.applicationInfo = mApplicationInfo
|
||||
|
||||
customTile = CustomTile.create(mTileHost, TILE_SPEC)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBooleanTileHasBooleanState() {
|
||||
`when`(mTileServiceManager.isBooleanTile).thenReturn(true)
|
||||
customTile = CustomTile.create(mTileHost, TILE_SPEC)
|
||||
|
||||
assertTrue(customTile.state is QSTile.BooleanState)
|
||||
assertTrue(customTile.newTileState() is QSTile.BooleanState)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRegularTileHasNotBooleanState() {
|
||||
assertFalse(customTile.state is QSTile.BooleanState)
|
||||
assertFalse(customTile.newTileState() is QSTile.BooleanState)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testValueUpdatedInBooleanTile() {
|
||||
`when`(mTileServiceManager.isBooleanTile).thenReturn(true)
|
||||
customTile = CustomTile.create(mTileHost, TILE_SPEC)
|
||||
customTile.qsTile.icon = mock(Icon::class.java)
|
||||
`when`(customTile.qsTile.icon.loadDrawable(any(Context::class.java)))
|
||||
.thenReturn(mock(Drawable::class.java))
|
||||
|
||||
val state = customTile.newTileState()
|
||||
assertTrue(state is QSTile.BooleanState)
|
||||
|
||||
customTile.qsTile.state = Tile.STATE_INACTIVE
|
||||
customTile.handleUpdateState(state, null)
|
||||
assertFalse((state as QSTile.BooleanState).value)
|
||||
|
||||
customTile.qsTile.state = Tile.STATE_ACTIVE
|
||||
customTile.handleUpdateState(state, null)
|
||||
assertTrue(state.value)
|
||||
|
||||
customTile.qsTile.state = Tile.STATE_UNAVAILABLE
|
||||
customTile.handleUpdateState(state, null)
|
||||
assertFalse(state.value)
|
||||
}
|
||||
}
|
||||
@@ -101,6 +101,7 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
|
||||
defaultServiceInfo = new ServiceInfo();
|
||||
defaultServiceInfo.metaData = new Bundle();
|
||||
defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_ACTIVE_TILE, true);
|
||||
defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_BOOLEAN_TILE, true);
|
||||
}
|
||||
when(mMockPackageManagerAdapter.getServiceInfo(any(), anyInt(), anyInt()))
|
||||
.thenReturn(defaultServiceInfo);
|
||||
@@ -237,4 +238,9 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
|
||||
verifyBind(2);
|
||||
verify(mMockTileService, times(2)).onStartListening();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBooleanTile() throws Exception {
|
||||
assertTrue(mStateManager.isBooleanTile());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user