Merge "Add dialog and controller for TileServiceRequest"
This commit is contained in:
committed by
Android (Google) Code Review
commit
b0ede0654f
@@ -60,6 +60,10 @@ public abstract class QSTileView extends LinearLayout {
|
||||
|
||||
public abstract int getDetailY();
|
||||
|
||||
public View getLabel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public View getLabelContainer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
36
packages/SystemUI/res/layout/tile_service_request_dialog.xml
Normal file
36
packages/SystemUI/res/layout/tile_service_request_dialog.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2021 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.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal"
|
||||
>
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:textDirection="locale"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/TextAppearance.PrivacyDialog"
|
||||
android:lineHeight="20sp"
|
||||
/>
|
||||
</LinearLayout>
|
||||
@@ -1634,4 +1634,8 @@
|
||||
<item name="communal_source_height_percentage" format="float" type="dimen">0.80</item>
|
||||
|
||||
<dimen name="drag_and_drop_icon_size">70dp</dimen>
|
||||
|
||||
<dimen name="qs_tile_service_request_dialog_width">304dp</dimen>
|
||||
<dimen name="qs_tile_service_request_tile_width">192dp</dimen>
|
||||
<dimen name="qs_tile_service_request_content_space">24dp</dimen>
|
||||
</resources>
|
||||
|
||||
@@ -3024,4 +3024,12 @@
|
||||
<string name="wifi_failed_connect_message">Failed to connect to network</string>
|
||||
<!-- Provider Model: Title to see all the networks [CHAR LIMIT=50] -->
|
||||
<string name="see_all_networks">See all</string>
|
||||
|
||||
<!-- Text for TileService request dialog. This is shown to the user that an app is requesting
|
||||
user approval to add the shown tile to Quick Settings [CHAR LIMIT=NONE] -->
|
||||
<string name="qs_tile_request_dialog_text"><xliff:g id="appName" example="Fake App">%1$s</xliff:g> wants to add the following tile to Quick Settings</string>
|
||||
<!-- Text for TileService request dialog. Text for button for user to approve adding the tile [CHAR LIMIT=20] -->
|
||||
<string name="qs_tile_request_dialog_add">Add tile</string>
|
||||
<!-- Text for TileService request dialog. Text for button for user to reject adding the tile [CHAR LIMIT=20] -->
|
||||
<string name="qs_tile_request_dialog_not_add">Do not add tile</string>
|
||||
</resources>
|
||||
|
||||
@@ -727,6 +727,16 @@
|
||||
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
|
||||
</style>
|
||||
|
||||
<!-- TileService request dialog -->
|
||||
<style name="TileRequestDialog" parent="Theme.SystemUI.QuickSettings.Dialog">
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowBackground">@drawable/qs_dialog_bg</item>
|
||||
<item name="android:windowIsFloating">true</item>
|
||||
<item name="android:backgroundDimEnabled">true</item>
|
||||
<item name="android:windowCloseOnTouchOutside">true</item>
|
||||
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
|
||||
</style>
|
||||
|
||||
<!-- USB Contaminant dialog -->
|
||||
<style name ="USBContaminant" />
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import com.android.systemui.qs.external.CustomTile;
|
||||
import com.android.systemui.qs.external.CustomTileStatePersister;
|
||||
import com.android.systemui.qs.external.TileLifecycleManager;
|
||||
import com.android.systemui.qs.external.TileServiceKey;
|
||||
import com.android.systemui.qs.external.TileServiceRequestController;
|
||||
import com.android.systemui.qs.external.TileServices;
|
||||
import com.android.systemui.qs.logging.QSLogger;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
@@ -106,6 +107,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
||||
private UserTracker mUserTracker;
|
||||
private SecureSettings mSecureSettings;
|
||||
|
||||
private final TileServiceRequestController mTileServiceRequestController;
|
||||
|
||||
@Inject
|
||||
public QSTileHost(Context context,
|
||||
StatusBarIconController iconController,
|
||||
@@ -122,7 +125,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
||||
UiEventLogger uiEventLogger,
|
||||
UserTracker userTracker,
|
||||
SecureSettings secureSettings,
|
||||
CustomTileStatePersister customTileStatePersister
|
||||
CustomTileStatePersister customTileStatePersister,
|
||||
TileServiceRequestController.Builder tileServiceRequestControllerBuilder
|
||||
) {
|
||||
mIconController = iconController;
|
||||
mContext = context;
|
||||
@@ -133,6 +137,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
||||
mQSLogger = qsLogger;
|
||||
mUiEventLogger = uiEventLogger;
|
||||
mBroadcastDispatcher = broadcastDispatcher;
|
||||
mTileServiceRequestController = tileServiceRequestControllerBuilder.create(this);
|
||||
|
||||
mInstanceIdSequence = new InstanceIdSequence(MAX_QS_INSTANCE_ID);
|
||||
mServices = new TileServices(this, bgLooper, mBroadcastDispatcher, userTracker);
|
||||
@@ -152,6 +157,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
||||
tunerService.addTunable(this, TILES_SETTING);
|
||||
// AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
|
||||
mAutoTiles = autoTiles.get();
|
||||
mTileServiceRequestController.init();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -171,6 +177,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
||||
mServices.destroy();
|
||||
mPluginManager.removePluginListener(this);
|
||||
mDumpManager.unregisterDumpable(TAG);
|
||||
mTileServiceRequestController.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
110
packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
vendored
Normal file
110
packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.Context
|
||||
import android.graphics.drawable.Icon
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import android.view.WindowInsets
|
||||
import android.widget.TextView
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.plugins.qs.QSTile
|
||||
import com.android.systemui.plugins.qs.QSTileView
|
||||
import com.android.systemui.qs.tileimpl.QSIconViewImpl
|
||||
import com.android.systemui.qs.tileimpl.QSTileImpl
|
||||
import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
|
||||
import com.android.systemui.qs.tileimpl.QSTileViewImpl
|
||||
import com.android.systemui.statusbar.phone.SystemUIDialog
|
||||
|
||||
/**
|
||||
* Dialog to present to the user to ask for authorization to add a [TileService].
|
||||
*/
|
||||
class TileRequestDialog(
|
||||
context: Context
|
||||
) : SystemUIDialog(context, R.style.TileRequestDialog) {
|
||||
|
||||
companion object {
|
||||
internal val CONTENT_ID = R.id.content
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
window?.apply {
|
||||
attributes.fitInsetsTypes = attributes.fitInsetsTypes or WindowInsets.Type.statusBars()
|
||||
attributes.receiveInsetsIgnoringZOrder = true
|
||||
setLayout(
|
||||
context.resources
|
||||
.getDimensionPixelSize(R.dimen.qs_tile_service_request_dialog_width),
|
||||
WRAP_CONTENT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the data of the tile to add, to show the user.
|
||||
*/
|
||||
fun setTileData(tileData: TileData) {
|
||||
val ll = (LayoutInflater
|
||||
.from(context)
|
||||
.inflate(R.layout.tile_service_request_dialog, null)
|
||||
as ViewGroup).apply {
|
||||
requireViewById<TextView>(R.id.text).apply {
|
||||
text = context
|
||||
.getString(R.string.qs_tile_request_dialog_text, tileData.appName)
|
||||
}
|
||||
addView(
|
||||
createTileView(tileData),
|
||||
context.resources.getDimensionPixelSize(
|
||||
R.dimen.qs_tile_service_request_tile_width),
|
||||
context.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size)
|
||||
)
|
||||
}
|
||||
val spacing = context.resources.getDimensionPixelSize(
|
||||
R.dimen.qs_tile_service_request_content_space
|
||||
)
|
||||
setView(ll, spacing, spacing, spacing, spacing / 2)
|
||||
}
|
||||
|
||||
private fun createTileView(tileData: TileData): QSTileView {
|
||||
val tile = QSTileViewImpl(context, QSIconViewImpl(context), true)
|
||||
val state = QSTile.BooleanState().apply {
|
||||
label = tileData.label
|
||||
icon = tileData.icon?.loadDrawable(context)?.let {
|
||||
QSTileImpl.DrawableIcon(it)
|
||||
} ?: ResourceIcon.get(R.drawable.android)
|
||||
}
|
||||
tile.onStateChanged(state)
|
||||
tile.isSelected = true
|
||||
return tile
|
||||
}
|
||||
|
||||
/**
|
||||
* Data bundle of information to show the user.
|
||||
*
|
||||
* @property appName Name of the app requesting their [TileService] to be added.
|
||||
* @property label Label of the tile.
|
||||
* @property icon Icon for the tile.
|
||||
*/
|
||||
data class TileData(
|
||||
val appName: CharSequence,
|
||||
val label: CharSequence,
|
||||
val icon: Icon?
|
||||
)
|
||||
}
|
||||
142
packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
vendored
Normal file
142
packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.app.Dialog
|
||||
import android.content.ComponentName
|
||||
import android.content.DialogInterface
|
||||
import android.graphics.drawable.Icon
|
||||
import android.util.Log
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.qs.QSTileHost
|
||||
import com.android.systemui.statusbar.commandline.Command
|
||||
import com.android.systemui.statusbar.commandline.CommandRegistry
|
||||
import com.android.systemui.statusbar.phone.SystemUIDialog
|
||||
import com.android.systemui.R
|
||||
import java.io.PrintWriter
|
||||
import java.util.function.Consumer
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val TAG = "TileServiceRequestController"
|
||||
|
||||
/**
|
||||
* Controller to interface between [TileRequestDialog] and [QSTileHost].
|
||||
*/
|
||||
class TileServiceRequestController constructor(
|
||||
private val qsTileHost: QSTileHost,
|
||||
private val commandRegistry: CommandRegistry,
|
||||
private val dialogCreator: () -> TileRequestDialog = { TileRequestDialog(qsTileHost.context) }
|
||||
) {
|
||||
|
||||
companion object {
|
||||
// Temporary return values while there's no API
|
||||
internal const val ADD_TILE = 0
|
||||
internal const val DONT_ADD_TILE = 1
|
||||
internal const val TILE_ALREADY_ADDED = 2
|
||||
internal const val DISMISSED = 3
|
||||
}
|
||||
|
||||
fun init() {
|
||||
commandRegistry.registerCommand("tile-service-add") { TileServiceRequestCommand() }
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
commandRegistry.unregisterCommand("tile-service-add")
|
||||
}
|
||||
|
||||
private fun addTile(componentName: ComponentName) {
|
||||
qsTileHost.addTile(componentName, true)
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun requestTileAdd(
|
||||
componentName: ComponentName,
|
||||
appName: CharSequence,
|
||||
label: CharSequence,
|
||||
icon: Icon?,
|
||||
callback: Consumer<Int>
|
||||
) {
|
||||
if (isTileAlreadyAdded(componentName)) {
|
||||
callback.accept(TILE_ALREADY_ADDED)
|
||||
return
|
||||
}
|
||||
val dialogResponse = object : Consumer<Int> {
|
||||
override fun accept(response: Int) {
|
||||
if (response == ADD_TILE) {
|
||||
addTile(componentName)
|
||||
}
|
||||
callback.accept(response)
|
||||
}
|
||||
}
|
||||
val tileData = TileRequestDialog.TileData(appName, label, icon)
|
||||
createDialog(tileData, dialogResponse).show()
|
||||
}
|
||||
|
||||
private fun createDialog(
|
||||
tileData: TileRequestDialog.TileData,
|
||||
responseHandler: Consumer<Int>
|
||||
): SystemUIDialog {
|
||||
val dialogClickListener = DialogInterface.OnClickListener { _, which ->
|
||||
if (which == Dialog.BUTTON_POSITIVE) {
|
||||
responseHandler.accept(ADD_TILE)
|
||||
} else {
|
||||
responseHandler.accept(DONT_ADD_TILE)
|
||||
}
|
||||
}
|
||||
return dialogCreator().apply {
|
||||
setTileData(tileData)
|
||||
setShowForAllUsers(true)
|
||||
setCanceledOnTouchOutside(true)
|
||||
setOnCancelListener { responseHandler.accept(DISMISSED) }
|
||||
setPositiveButton(R.string.qs_tile_request_dialog_add, dialogClickListener)
|
||||
setNegativeButton(R.string.qs_tile_request_dialog_not_add, dialogClickListener)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isTileAlreadyAdded(componentName: ComponentName): Boolean {
|
||||
val spec = CustomTile.toSpec(componentName)
|
||||
return qsTileHost.indexOf(spec) != -1
|
||||
}
|
||||
|
||||
inner class TileServiceRequestCommand : Command {
|
||||
override fun execute(pw: PrintWriter, args: List<String>) {
|
||||
val componentName: ComponentName = ComponentName.unflattenFromString(args[0])
|
||||
?: run {
|
||||
Log.w(TAG, "Malformed componentName ${args[0]}")
|
||||
return
|
||||
}
|
||||
requestTileAdd(componentName, args[1], args[2], null) {
|
||||
Log.d(TAG, "Response: $it")
|
||||
}
|
||||
}
|
||||
|
||||
override fun help(pw: PrintWriter) {
|
||||
pw.println("Usage: adb shell cmd statusbar tile-service-add " +
|
||||
"<componentName> <appName> <label>")
|
||||
}
|
||||
}
|
||||
|
||||
@SysUISingleton
|
||||
class Builder @Inject constructor(
|
||||
private val commandRegistry: CommandRegistry
|
||||
) {
|
||||
fun create(qsTileHost: QSTileHost): TileServiceRequestController {
|
||||
return TileServiceRequestController(qsTileHost, commandRegistry)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -286,6 +286,10 @@ open class QSTileViewImpl @JvmOverloads constructor(
|
||||
return labelContainer
|
||||
}
|
||||
|
||||
override fun getLabel(): View {
|
||||
return label
|
||||
}
|
||||
|
||||
override fun getSecondaryLabel(): View {
|
||||
return secondaryLabel
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import com.android.systemui.plugins.FalsingManager;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.qs.dagger.QSFragmentComponent;
|
||||
import com.android.systemui.qs.external.CustomTileStatePersister;
|
||||
import com.android.systemui.qs.external.TileServiceRequestController;
|
||||
import com.android.systemui.qs.logging.QSLogger;
|
||||
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
@@ -95,6 +96,10 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
||||
private KeyguardBypassController mBypassController;
|
||||
@Mock
|
||||
private FalsingManager mFalsingManager;
|
||||
@Mock
|
||||
private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder;
|
||||
@Mock
|
||||
private TileServiceRequestController mTileServiceRequestController;
|
||||
|
||||
public QSFragmentTest() {
|
||||
super(QSFragment.class);
|
||||
@@ -108,6 +113,9 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
||||
when(mQsComponentFactory.create(any(QSFragment.class))).thenReturn(mQsFragmentComponent);
|
||||
when(mQsFragmentComponent.getQSPanelController()).thenReturn(mQSPanelController);
|
||||
|
||||
when(mTileServiceRequestControllerBuilder.create(any()))
|
||||
.thenReturn(mTileServiceRequestController);
|
||||
|
||||
mMockMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
|
||||
mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
|
||||
new LayoutInflaterBuilder(mContext)
|
||||
@@ -136,7 +144,8 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
||||
() -> mock(AutoTileManager.class), mock(DumpManager.class),
|
||||
mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
|
||||
mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class),
|
||||
mock(SecureSettings.class), mock(CustomTileStatePersister.class));
|
||||
mock(SecureSettings.class), mock(CustomTileStatePersister.class),
|
||||
mTileServiceRequestControllerBuilder);
|
||||
qs.setHost(host);
|
||||
|
||||
qs.setListening(true);
|
||||
|
||||
@@ -21,6 +21,7 @@ import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
@@ -59,6 +60,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.qs.external.CustomTile;
|
||||
import com.android.systemui.qs.external.CustomTileStatePersister;
|
||||
import com.android.systemui.qs.external.TileServiceKey;
|
||||
import com.android.systemui.qs.external.TileServiceRequestController;
|
||||
import com.android.systemui.qs.logging.QSLogger;
|
||||
import com.android.systemui.qs.tileimpl.QSTileImpl;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
@@ -124,6 +126,10 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
private SecureSettings mSecureSettings;
|
||||
@Mock
|
||||
private CustomTileStatePersister mCustomTileStatePersister;
|
||||
@Mock
|
||||
private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder;
|
||||
@Mock
|
||||
private TileServiceRequestController mTileServiceRequestController;
|
||||
|
||||
private Handler mHandler;
|
||||
private TestableLooper mLooper;
|
||||
@@ -134,10 +140,14 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mLooper = TestableLooper.get(this);
|
||||
mHandler = new Handler(mLooper.getLooper());
|
||||
|
||||
when(mTileServiceRequestControllerBuilder.create(any()))
|
||||
.thenReturn(mTileServiceRequestController);
|
||||
|
||||
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
|
||||
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
|
||||
mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
|
||||
mSecureSettings, mCustomTileStatePersister);
|
||||
mSecureSettings, mCustomTileStatePersister, mTileServiceRequestControllerBuilder);
|
||||
setUpTileFactory();
|
||||
|
||||
when(mSecureSettings.getStringForUser(eq(QSTileHost.TILES_SETTING), anyInt()))
|
||||
@@ -371,11 +381,12 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
|
||||
BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger,
|
||||
UiEventLogger uiEventLogger, UserTracker userTracker,
|
||||
SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister) {
|
||||
SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister,
|
||||
TileServiceRequestController.Builder tileServiceRequestControllerBuilder) {
|
||||
super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
|
||||
tunerService, autoTiles, dumpManager, broadcastDispatcher,
|
||||
Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings,
|
||||
customTileStatePersister);
|
||||
customTileStatePersister, tileServiceRequestControllerBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
138
packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
vendored
Normal file
138
packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.graphics.drawable.Icon
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.plugins.qs.QSTileView
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
class TileRequestDialogTest : SysuiTestCase() {
|
||||
|
||||
companion object {
|
||||
private const val APP_NAME = "App name"
|
||||
private const val LABEL = "Label"
|
||||
}
|
||||
|
||||
private lateinit var dialog: TileRequestDialog
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
// Create in looper so we can make sure that the tile is fully updated
|
||||
TestableLooper.get(this).runWithLooper {
|
||||
dialog = TileRequestDialog(mContext)
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun teardown() {
|
||||
if (this::dialog.isInitialized) {
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun useCorrectTheme() {
|
||||
assertThat(dialog.context.themeResId).isEqualTo(R.style.TileRequestDialog)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setTileData_hasCorrectViews() {
|
||||
val icon = Icon.createWithResource(mContext, R.drawable.cloud)
|
||||
val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
|
||||
|
||||
dialog.setTileData(tileData)
|
||||
dialog.show()
|
||||
|
||||
val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
|
||||
|
||||
assertThat(content.childCount).isEqualTo(2)
|
||||
assertThat(content.getChildAt(0)).isInstanceOf(TextView::class.java)
|
||||
assertThat(content.getChildAt(1)).isInstanceOf(QSTileView::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setTileData_hasCorrectAppName() {
|
||||
val icon = Icon.createWithResource(mContext, R.drawable.cloud)
|
||||
val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
|
||||
|
||||
dialog.setTileData(tileData)
|
||||
dialog.show()
|
||||
|
||||
val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
|
||||
val text = content.getChildAt(0) as TextView
|
||||
assertThat(text.text.toString()).contains(APP_NAME)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setTileData_hasCorrectLabel() {
|
||||
val icon = Icon.createWithResource(mContext, R.drawable.cloud)
|
||||
val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
|
||||
|
||||
dialog.setTileData(tileData)
|
||||
dialog.show()
|
||||
|
||||
TestableLooper.get(this).processAllMessages()
|
||||
|
||||
val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
|
||||
val tile = content.getChildAt(1) as QSTileView
|
||||
assertThat((tile.label as TextView).text.toString()).isEqualTo(LABEL)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setTileData_hasIcon() {
|
||||
val icon = Icon.createWithResource(mContext, R.drawable.cloud)
|
||||
val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
|
||||
|
||||
dialog.setTileData(tileData)
|
||||
dialog.show()
|
||||
|
||||
TestableLooper.get(this).processAllMessages()
|
||||
|
||||
val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
|
||||
val tile = content.getChildAt(1) as QSTileView
|
||||
assertThat((tile.icon.iconView as ImageView).drawable).isNotNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setTileData_nullIcon_hasIcon() {
|
||||
val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, null)
|
||||
|
||||
dialog.setTileData(tileData)
|
||||
dialog.show()
|
||||
|
||||
TestableLooper.get(this).processAllMessages()
|
||||
|
||||
val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
|
||||
val tile = content.getChildAt(1) as QSTileView
|
||||
assertThat((tile.icon.iconView as ImageView).drawable).isNotNull()
|
||||
}
|
||||
}
|
||||
162
packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
vendored
Normal file
162
packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.DialogInterface
|
||||
import android.graphics.drawable.Icon
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.qs.QSTileHost
|
||||
import com.android.systemui.statusbar.commandline.CommandRegistry
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.capture
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.anyBoolean
|
||||
import org.mockito.Mockito.anyInt
|
||||
import org.mockito.Mockito.anyString
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
import java.util.function.Consumer
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
class TileServiceRequestControllerTest : SysuiTestCase() {
|
||||
|
||||
companion object {
|
||||
private val TEST_COMPONENT = ComponentName("test_pkg", "test_cls")
|
||||
private const val TEST_APP_NAME = "App"
|
||||
private const val TEST_LABEL = "Label"
|
||||
}
|
||||
|
||||
@Mock
|
||||
private lateinit var tileRequestDialog: TileRequestDialog
|
||||
@Mock
|
||||
private lateinit var qsTileHost: QSTileHost
|
||||
@Mock
|
||||
private lateinit var commandRegistry: CommandRegistry
|
||||
@Mock
|
||||
private lateinit var icon: Icon
|
||||
|
||||
private lateinit var controller: TileServiceRequestController
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
// Tile not present by default
|
||||
`when`(qsTileHost.indexOf(anyString())).thenReturn(-1)
|
||||
|
||||
controller = TileServiceRequestController(qsTileHost, commandRegistry) {
|
||||
tileRequestDialog
|
||||
}
|
||||
|
||||
controller.init()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestTileAdd_dataIsPassedToDialog() {
|
||||
controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, Callback())
|
||||
|
||||
verify(tileRequestDialog).setTileData(
|
||||
TileRequestDialog.TileData(TEST_APP_NAME, TEST_LABEL, icon)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun tileAlreadyAdded_correctResult() {
|
||||
`when`(qsTileHost.indexOf(CustomTile.toSpec(TEST_COMPONENT))).thenReturn(2)
|
||||
|
||||
val callback = Callback()
|
||||
controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
|
||||
|
||||
assertThat(callback.lastAccepted).isEqualTo(TileServiceRequestController.TILE_ALREADY_ADDED)
|
||||
verify(qsTileHost, never()).addTile(any(ComponentName::class.java), anyBoolean())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showAllUsers_set() {
|
||||
controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, Callback())
|
||||
verify(tileRequestDialog).setShowForAllUsers(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cancelOnTouchOutside_set() {
|
||||
controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, Callback())
|
||||
verify(tileRequestDialog).setCanceledOnTouchOutside(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cancelListener_dismissResult() {
|
||||
val cancelListenerCaptor =
|
||||
ArgumentCaptor.forClass(DialogInterface.OnCancelListener::class.java)
|
||||
|
||||
val callback = Callback()
|
||||
controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
|
||||
verify(tileRequestDialog).setOnCancelListener(capture(cancelListenerCaptor))
|
||||
|
||||
cancelListenerCaptor.value.onCancel(tileRequestDialog)
|
||||
assertThat(callback.lastAccepted).isEqualTo(TileServiceRequestController.DISMISSED)
|
||||
verify(qsTileHost, never()).addTile(any(ComponentName::class.java), anyBoolean())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun positiveActionListener_tileAddedResult() {
|
||||
val clickListenerCaptor =
|
||||
ArgumentCaptor.forClass(DialogInterface.OnClickListener::class.java)
|
||||
|
||||
val callback = Callback()
|
||||
controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
|
||||
verify(tileRequestDialog).setPositiveButton(anyInt(), capture(clickListenerCaptor))
|
||||
|
||||
clickListenerCaptor.value.onClick(tileRequestDialog, DialogInterface.BUTTON_POSITIVE)
|
||||
|
||||
assertThat(callback.lastAccepted).isEqualTo(TileServiceRequestController.ADD_TILE)
|
||||
verify(qsTileHost).addTile(TEST_COMPONENT, /* end */ true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun negativeActionListener_tileNotAddedResult() {
|
||||
val clickListenerCaptor =
|
||||
ArgumentCaptor.forClass(DialogInterface.OnClickListener::class.java)
|
||||
|
||||
val callback = Callback()
|
||||
controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
|
||||
verify(tileRequestDialog).setNegativeButton(anyInt(), capture(clickListenerCaptor))
|
||||
|
||||
clickListenerCaptor.value.onClick(tileRequestDialog, DialogInterface.BUTTON_NEGATIVE)
|
||||
|
||||
assertThat(callback.lastAccepted).isEqualTo(TileServiceRequestController.DONT_ADD_TILE)
|
||||
verify(qsTileHost, never()).addTile(any(ComponentName::class.java), anyBoolean())
|
||||
}
|
||||
|
||||
private class Callback : Consumer<Int> {
|
||||
var lastAccepted: Int? = null
|
||||
private set
|
||||
override fun accept(t: Int) {
|
||||
lastAccepted = t
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,12 +98,20 @@ public class TileServicesTest extends SysuiTestCase {
|
||||
private UserTracker mUserTracker;
|
||||
@Mock
|
||||
private SecureSettings mSecureSettings;
|
||||
@Mock
|
||||
private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder;
|
||||
@Mock
|
||||
private TileServiceRequestController mTileServiceRequestController;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mDependency.injectMockDependency(BluetoothController.class);
|
||||
mManagers = new ArrayList<>();
|
||||
|
||||
when(mTileServiceRequestControllerBuilder.create(any()))
|
||||
.thenReturn(mTileServiceRequestController);
|
||||
|
||||
QSTileHost host = new QSTileHost(mContext,
|
||||
mStatusBarIconController,
|
||||
mQSFactory,
|
||||
@@ -119,7 +127,8 @@ public class TileServicesTest extends SysuiTestCase {
|
||||
mUiEventLogger,
|
||||
mUserTracker,
|
||||
mSecureSettings,
|
||||
mock(CustomTileStatePersister.class));
|
||||
mock(CustomTileStatePersister.class),
|
||||
mTileServiceRequestControllerBuilder);
|
||||
mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher,
|
||||
mUserTracker);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user