diff options
8 files changed, 267 insertions, 44 deletions
diff --git a/packages/SystemUI/docs/usb_audio.md b/packages/SystemUI/docs/usb_audio.md new file mode 100644 index 000000000000..66e2df944fbc --- /dev/null +++ b/packages/SystemUI/docs/usb_audio.md @@ -0,0 +1,30 @@ +# USB audio Permission and Confirmation warning dialog resource string id matrix table +### go/support-usb-access-aoc-offload-feature + + |---|------------|----------------|------------------|-----------------|--------------------| + | # | Permission |isUsbAudioDevice| hasAudioPlayback | hasAudioCapture | string resource ID | + |---|------------|----------------|------------------|-----------------|--------------------| + | 1 | TRUE | TRUE | TRUE | FALSE | usb_audio_device_ + permission_prompt | + |---|------------|----------------|------------------|-----------------|--------------------| + | 2 | TRUE | TRUE | FALSE | TRUE | usb_audio_device_ + permission_prompt | + |---|------------|----------------|------------------|-----------------|--------------------| + | 3 | TRUE | TRUE | TRUE | TRUE | usb_audio_device_ + permission_prompt | + |---|------------|----------------|------------------|-----------------|--------------------| + | 4 | TRUE | FALSE | N/A | N/A | usb_device_ + permission_prompt | + |---|------------|----------------|------------------|-----------------|--------------------| + | 5 | FALSE | TRUE | TRUE | FALSE | usb_audio_device_ + permission_prompt | + |---|------------|----------------|------------------|-----------------|--------------------| + | 6 | FALSE | TRUE | FALSE | TRUE | usb_audio_device_ + permission_prompt_warn + |---|------------|----------------|------------------|-----------------|--------------------| + | 7 | FALSE | TRUE | TRUE | TRUE | usb_audio_device_ + permission_prompt_warn + |---|------------|----------------|------------------|-----------------|--------------------| + | 8 | FALSE | FALSE | N/A | N/A | usb_device_ + permission_prompt | + |---|------------|----------------|------------------|-----------------|--------------------| diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java index 1653e0adadb5..fb016911f913 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java @@ -29,8 +29,10 @@ import com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity; import com.android.systemui.settings.brightness.BrightnessDialog; import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity; import com.android.systemui.tuner.TunerActivity; +import com.android.systemui.usb.UsbConfirmActivity; import com.android.systemui.usb.UsbDebuggingActivity; import com.android.systemui.usb.UsbDebuggingSecondaryUserActivity; +import com.android.systemui.usb.UsbPermissionActivity; import com.android.systemui.user.CreateUserActivity; import dagger.Binds; @@ -80,6 +82,18 @@ public abstract class DefaultActivityBinder { public abstract Activity bindUsbDebuggingSecondaryUserActivity( UsbDebuggingSecondaryUserActivity activity); + /** Inject into UsbPermissionActivity. */ + @Binds + @IntoMap + @ClassKey(UsbPermissionActivity.class) + public abstract Activity bindUsbPermissionActivity(UsbPermissionActivity activity); + + /** Inject into UsbConfirmActivity. */ + @Binds + @IntoMap + @ClassKey(UsbConfirmActivity.class) + public abstract Activity bindUsbConfirmActivity(UsbConfirmActivity activity); + /** Inject into CreateUserActivity. */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbAudioWarningDialogMessage.java b/packages/SystemUI/src/com/android/systemui/usb/UsbAudioWarningDialogMessage.java new file mode 100644 index 000000000000..e06353b3aad3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbAudioWarningDialogMessage.java @@ -0,0 +1,124 @@ +/* + * 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.usb; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; +import android.content.res.Resources; +import android.util.Log; + +import com.android.systemui.R; + +import java.lang.annotation.Retention; + +import javax.inject.Inject; + +/** + * USB Audio devices warning dialog messages help class. + */ +public class UsbAudioWarningDialogMessage { + private static final String TAG = "UsbAudioWarningDialogMessage"; + + @Retention(SOURCE) + @IntDef({TYPE_PERMISSION, TYPE_CONFIRM}) + public @interface DialogType {} + public static final int TYPE_PERMISSION = 0; + public static final int TYPE_CONFIRM = 1; + + private int mDialogType; + private UsbDialogHelper mDialogHelper; + + @Inject + public UsbAudioWarningDialogMessage() { + } + + /** + * Initialize USB audio warning dialog message type and helper class. + * @param type Dialog type for Activity. + * @param usbDialogHelper Helper class for getting USB permission and confirm dialogs + */ + public void init(@DialogType int type, UsbDialogHelper usbDialogHelper) { + mDialogType = type; + mDialogHelper = usbDialogHelper; + } + + boolean hasRecordPermission() { + return mDialogHelper.packageHasAudioRecordingPermission(); + } + + boolean isUsbAudioDevice() { + return mDialogHelper.isUsbDevice() && (mDialogHelper.deviceHasAudioCapture() + || (mDialogHelper.deviceHasAudioPlayback())); + } + + boolean hasAudioPlayback() { + return mDialogHelper.deviceHasAudioPlayback(); + } + + boolean hasAudioCapture() { + return mDialogHelper.deviceHasAudioCapture(); + } + + /** + * According to USB audio warning dialog matrix table to return warning message id. + * @return string resId for USB audio warning dialog message, otherwise {ID_NULL}. + * See usb_audio.md for USB audio Permission and Confirmation warning dialog resource + * string id matrix table. + */ + public int getMessageId() { + if (!mDialogHelper.isUsbDevice()) { + return getUsbAccessoryPromptId(); + } + + if (hasRecordPermission() && isUsbAudioDevice()) { + // case# 1, 2, 3 + return R.string.usb_audio_device_prompt; + } else if (!hasRecordPermission() && isUsbAudioDevice() && hasAudioPlayback() + && !hasAudioCapture()) { + // case# 5 + return R.string.usb_audio_device_prompt; + } + + if (!hasRecordPermission() && isUsbAudioDevice() && hasAudioCapture()) { + // case# 6,7 + return R.string.usb_audio_device_prompt_warn; + } + + Log.w(TAG, "Only shows title with empty content description!"); + return Resources.ID_NULL; + } + + /** + * Gets prompt dialog title. + * @return string id for USB prompt dialog title. + */ + public int getPromptTitleId() { + return (mDialogType == TYPE_PERMISSION) + ? R.string.usb_audio_device_permission_prompt_title + : R.string.usb_audio_device_confirm_prompt_title; + } + + /** + * Gets USB Accessory prompt message id. + * @return string id for USB Accessory prompt message. + */ + public int getUsbAccessoryPromptId() { + return (mDialogType == TYPE_PERMISSION) + ? R.string.usb_accessory_permission_prompt : R.string.usb_accessory_confirm_prompt; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java index f4558feb69aa..6e523d88a326 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java @@ -16,7 +16,10 @@ package com.android.systemui.usb; -import com.android.systemui.R; +import android.content.res.Resources; +import android.os.Bundle; + +import javax.inject.Inject; /** * Dialog shown to confirm the package to start when a USB device or accessory is attached and there @@ -24,23 +27,35 @@ import com.android.systemui.R; */ public class UsbConfirmActivity extends UsbDialogActivity { + private UsbAudioWarningDialogMessage mUsbConfirmMessageHandler; + + @Inject + public UsbConfirmActivity(UsbAudioWarningDialogMessage usbAudioWarningDialogMessage) { + mUsbConfirmMessageHandler = usbAudioWarningDialogMessage; + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + mUsbConfirmMessageHandler.init(UsbAudioWarningDialogMessage.TYPE_CONFIRM, mDialogHelper); + } + @Override protected void onResume() { super.onResume(); - final int strId; - boolean useRecordWarning = false; - if (mDialogHelper.isUsbDevice()) { - useRecordWarning = mDialogHelper.deviceHasAudioCapture() - && !mDialogHelper.packageHasAudioRecordingPermission(); - strId = useRecordWarning - ? R.string.usb_device_confirm_prompt_warn - : R.string.usb_device_confirm_prompt; - } else { - // UsbAccessory case - strId = R.string.usb_accessory_confirm_prompt; - } - setAlertParams(strId); // Only show the "always use" checkbox if there is no USB/Record warning + final boolean useRecordWarning = mDialogHelper.isUsbDevice() + && (mDialogHelper.deviceHasAudioCapture() + && !mDialogHelper.packageHasAudioRecordingPermission()); + + final int titleId = mUsbConfirmMessageHandler.getPromptTitleId(); + final String title = getString(titleId, mDialogHelper.getAppName(), + mDialogHelper.getDeviceDescription()); + final int messageId = mUsbConfirmMessageHandler.getMessageId(); + String message = (messageId != Resources.ID_NULL) + ? getString(messageId, mDialogHelper.getAppName(), + mDialogHelper.getDeviceDescription()) : null; + setAlertParams(title, message); if (!useRecordWarning) { addAlwaysUseCheckbox(); } diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDialogActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDialogActivity.java index 930bc338a918..55dec5fbe344 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbDialogActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDialogActivity.java @@ -41,7 +41,7 @@ abstract class UsbDialogActivity extends AlertActivity private TextView mClearDefaultHint; @Override - protected final void onCreate(Bundle savedInstanceState) { + protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().addSystemFlags( WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); @@ -87,11 +87,10 @@ abstract class UsbDialogActivity extends AlertActivity } } - void setAlertParams(int strId) { + void setAlertParams(String title, String message) { final AlertController.AlertParams ap = mAlertParams; - ap.mTitle = mDialogHelper.getAppName(); - ap.mMessage = getString(strId, mDialogHelper.getAppName(), - mDialogHelper.getDeviceDescription()); + ap.mTitle = title; + ap.mMessage = message; ap.mPositiveButtonText = getString(android.R.string.ok); ap.mNegativeButtonText = getString(android.R.string.cancel); ap.mPositiveButtonListener = this; diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDialogHelper.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDialogHelper.java index d63fb86ea6d4..3f061d3113be 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbDialogHelper.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDialogHelper.java @@ -134,6 +134,13 @@ public class UsbDialogHelper { } /** + * @return True if the intent contains a UsbDevice which can play audio. + */ + public boolean deviceHasAudioPlayback() { + return mDevice != null && mDevice.getHasAudioPlayback(); + } + + /** * @return True if the package has RECORD_AUDIO permission specified in its manifest. */ public boolean packageHasAudioRecordingPermission() { @@ -272,15 +279,15 @@ public class UsbDialogHelper { return desc; } - /** - * Whether the calling package can set as default handler of the USB device or accessory. - * In case of a UsbAccessory this is the case if the calling package has an intent filter for - * {@link UsbManager#ACTION_USB_ACCESSORY_ATTACHED} with a usb-accessory filter matching the - * attached accessory. In case of a UsbDevice this is the case if the calling package has an - * intent filter for {@link UsbManager#ACTION_USB_DEVICE_ATTACHED} with a usb-device filter - * matching the attached device. - * - * @return True if the package can be default for the USB device. + /** + * Whether the calling package can set as default handler of the USB device or accessory. + * In case of a UsbAccessory this is the case if the calling package has an intent filter for + * {@link UsbManager#ACTION_USB_ACCESSORY_ATTACHED} with a usb-accessory filter matching the + * attached accessory. In case of a UsbDevice this is the case if the calling package has an + * intent filter for {@link UsbManager#ACTION_USB_DEVICE_ATTACHED} with a usb-device filter + * matching the attached device. + * + * @return True if the package can be default for the USB device. */ public boolean canBeDefault() { return mCanBeDefault; diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java index 38d634777177..9484d3a63801 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java @@ -16,7 +16,10 @@ package com.android.systemui.usb; -import com.android.systemui.R; +import android.content.res.Resources; +import android.os.Bundle; + +import javax.inject.Inject; /** * Dialog shown when a package requests access to a USB device or accessory. @@ -24,23 +27,36 @@ import com.android.systemui.R; public class UsbPermissionActivity extends UsbDialogActivity { private boolean mPermissionGranted = false; + private UsbAudioWarningDialogMessage mUsbPermissionMessageHandler; + + @Inject + public UsbPermissionActivity(UsbAudioWarningDialogMessage usbAudioWarningDialogMessage) { + mUsbPermissionMessageHandler = usbAudioWarningDialogMessage; + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + mUsbPermissionMessageHandler.init(UsbAudioWarningDialogMessage.TYPE_PERMISSION, + mDialogHelper); + } @Override protected void onResume() { super.onResume(); - final int strId; - boolean useRecordWarning = false; - if (mDialogHelper.isUsbDevice()) { - useRecordWarning = mDialogHelper.deviceHasAudioCapture() - && !mDialogHelper.packageHasAudioRecordingPermission(); - strId = useRecordWarning - ? R.string.usb_device_permission_prompt_warn - : R.string.usb_device_permission_prompt; - } else { - // UsbAccessory case - strId = R.string.usb_accessory_permission_prompt; - } - setAlertParams(strId); + final boolean useRecordWarning = mDialogHelper.isUsbDevice() + && (mDialogHelper.deviceHasAudioCapture() + && !mDialogHelper.packageHasAudioRecordingPermission()); + + final int titleId = mUsbPermissionMessageHandler.getPromptTitleId(); + final String title = getString(titleId, mDialogHelper.getAppName(), + mDialogHelper.getDeviceDescription()); + final int messageId = mUsbPermissionMessageHandler.getMessageId(); + String message = (messageId != Resources.ID_NULL) + ? getString(messageId, mDialogHelper.getAppName(), + mDialogHelper.getDeviceDescription()) : null; + setAlertParams(title, message); + // Only show the "always use" checkbox if there is no USB/Record warning if (!useRecordWarning && mDialogHelper.canBeDefault()) { addAlwaysUseCheckbox(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt index 3d554880ed58..b30c20db642d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt @@ -21,11 +21,16 @@ import android.hardware.usb.IUsbSerialReader import android.hardware.usb.UsbAccessory import android.hardware.usb.UsbManager import android.testing.AndroidTestingRunner +import android.testing.TestableLooper import android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS import androidx.test.filters.SmallTest import androidx.test.rule.ActivityTestRule +import androidx.test.runner.intercepting.SingleActivityFactory import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat + +import javax.inject.Inject + import org.junit.After import org.junit.Before import org.junit.Rule @@ -37,14 +42,26 @@ import org.junit.runner.RunWith */ @RunWith(AndroidTestingRunner::class) @SmallTest +@TestableLooper.RunWithLooper class UsbPermissionActivityTest : SysuiTestCase() { - class UsbPermissionActivityTestable : UsbPermissionActivity() + private var mMessage: UsbAudioWarningDialogMessage = UsbAudioWarningDialogMessage() + + open class UsbPermissionActivityTestable @Inject constructor ( + val message: UsbAudioWarningDialogMessage + ) + : UsbPermissionActivity(UsbAudioWarningDialogMessage()) @Rule @JvmField var activityRule = ActivityTestRule<UsbPermissionActivityTestable>( - UsbPermissionActivityTestable::class.java, false, false) + object : SingleActivityFactory<UsbPermissionActivityTestable>( + UsbPermissionActivityTestable::class.java + ) { + override fun create(intent: Intent?): UsbPermissionActivityTestable { + return UsbPermissionActivityTestable(mMessage) + } + }, false, false) private val activityIntent = Intent(mContext, UsbPermissionActivityTestable::class.java) .apply { @@ -72,6 +89,7 @@ class UsbPermissionActivityTest : SysuiTestCase() { @Before fun setUp() { + UsbPermissionActivityTestable(mMessage) activityRule.launchActivity(activityIntent) } |