Loading packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +3 −3 Original line number Diff line number Diff line Loading @@ -23,8 +23,8 @@ import com.android.systemui.InitController; import com.android.systemui.SystemUIAppComponentFactory; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardSliceProvider; import com.android.systemui.media.taptotransfer.MediaTttChipController; import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper; import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender; import com.android.systemui.people.PeopleProvider; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.unfold.SysUIUnfoldComponent; Loading Loading @@ -134,7 +134,7 @@ public interface SysUIComponent { }); getNaturalRotationUnfoldProgressProvider().ifPresent(o -> o.init()); // No init method needed, just needs to be gotten so that it's created. getMediaTttChipController(); getMediaTttChipControllerSender(); getMediaTttCommandLineHelper(); getUnfoldLatencyTracker().init(); } Loading Loading @@ -190,7 +190,7 @@ public interface SysUIComponent { Optional<NaturalRotationUnfoldProgressProvider> getNaturalRotationUnfoldProgressProvider(); /** */ Optional<MediaTttChipController> getMediaTttChipController(); Optional<MediaTttChipControllerSender> getMediaTttChipControllerSender(); /** */ Optional<MediaTttCommandLineHelper> getMediaTttCommandLineHelper(); Loading packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java +5 −5 Original line number Diff line number Diff line Loading @@ -26,9 +26,9 @@ import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.media.MediaHost; import com.android.systemui.media.MediaHostStatesManager; import com.android.systemui.media.taptotransfer.MediaTttChipController; import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper; import com.android.systemui.media.taptotransfer.MediaTttFlags; import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender; import com.android.systemui.statusbar.commandline.CommandRegistry; import com.android.systemui.util.concurrency.DelayableExecutor; Loading Loading @@ -80,7 +80,7 @@ public interface MediaModule { /** */ @Provides @SysUISingleton static Optional<MediaTttChipController> providesMediaTttChipController( static Optional<MediaTttChipControllerSender> providesMediaTttChipControllerSender( MediaTttFlags mediaTttFlags, Context context, WindowManager windowManager, Loading @@ -89,7 +89,7 @@ public interface MediaModule { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } return Optional.of(new MediaTttChipController( return Optional.of(new MediaTttChipControllerSender( context, windowManager, mainExecutor, backgroundExecutor)); } Loading @@ -100,12 +100,12 @@ public interface MediaModule { MediaTttFlags mediaTttFlags, CommandRegistry commandRegistry, Context context, MediaTttChipController mediaTttChipController, MediaTttChipControllerSender mediaTttChipControllerSender, @Main DelayableExecutor mainExecutor) { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } return Optional.of(new MediaTttCommandLineHelper( commandRegistry, context, mediaTttChipController, mainExecutor)); commandRegistry, context, mediaTttChipControllerSender, mainExecutor)); } } packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt +13 −8 Original line number Diff line number Diff line Loading @@ -23,6 +23,10 @@ import androidx.annotation.VisibleForTesting import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender import com.android.systemui.media.taptotransfer.sender.MoveCloserToTransfer import com.android.systemui.media.taptotransfer.sender.TransferInitiated import com.android.systemui.media.taptotransfer.sender.TransferSucceeded import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.util.concurrency.DelayableExecutor Loading @@ -38,7 +42,7 @@ import javax.inject.Inject class MediaTttCommandLineHelper @Inject constructor( commandRegistry: CommandRegistry, context: Context, private val mediaTttChipController: MediaTttChipController, private val mediaTttChipControllerSender: MediaTttChipControllerSender, @Main private val mainExecutor: DelayableExecutor, ) { private val appIconDrawable = Loading @@ -54,21 +58,21 @@ class MediaTttCommandLineHelper @Inject constructor( val otherDeviceName = args[0] when (args[1]) { MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME -> { mediaTttChipController.displayChip( MoveCloserToTransfer(otherDeviceName, appIconDrawable) mediaTttChipControllerSender.displayChip( MoveCloserToTransfer(appIconDrawable, otherDeviceName) ) } TRANSFER_INITIATED_COMMAND_NAME -> { val futureTask = FutureTask { fakeUndoRunnable } mediaTttChipController.displayChip( TransferInitiated(otherDeviceName, appIconDrawable, futureTask) mediaTttChipControllerSender.displayChip( TransferInitiated(appIconDrawable, otherDeviceName, futureTask) ) mainExecutor.executeDelayed({ futureTask.run() }, FUTURE_WAIT_TIME) } TRANSFER_SUCCEEDED_COMMAND_NAME -> { mediaTttChipController.displayChip( TransferSucceeded(otherDeviceName, appIconDrawable, fakeUndoRunnable) mediaTttChipControllerSender.displayChip( TransferSucceeded(appIconDrawable, otherDeviceName, fakeUndoRunnable) ) } else -> { Loading @@ -90,7 +94,7 @@ class MediaTttCommandLineHelper @Inject constructor( inner class RemoveChipCommand : Command { override fun execute(pw: PrintWriter, args: List<String>) { mediaTttChipController.removeChip() mediaTttChipControllerSender.removeChip() } override fun help(pw: PrintWriter) { pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_TAG") Loading @@ -114,3 +118,4 @@ val TRANSFER_INITIATED_COMMAND_NAME = TransferInitiated::class.simpleName!! val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!! private const val FUTURE_WAIT_TIME = 2000L private const val TAG = "MediaTapToTransferCli" packages/SystemUI/src/com/android/systemui/media/taptotransfer/README.md 0 → 100644 +11 −0 Original line number Diff line number Diff line # Media Tap-To-Transfer This package (and child packages) include code for the media tap-to-transfer feature, which allows users to easily transfer playing media between devices. In media transfer, there are two devices: the *sender* and the *receiver*. The sender device will start and stop media casts to the receiver device. On both devices, System UI will display a chip informing the user about the media cast occurring. This package is structured so that the sender code is in the sender package, the receiver code is in the receiver package, and code that's shared between them is in the common package. packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt 0 → 100644 +104 −0 Original line number Diff line number Diff line /* * 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.media.taptotransfer.common import android.annotation.LayoutRes import android.annotation.SuppressLint import android.content.Context import android.graphics.PixelFormat import android.view.Gravity import android.view.LayoutInflater import android.view.ViewGroup import android.view.WindowManager import com.android.internal.widget.CachingIconView import com.android.systemui.R /** * A superclass controller that provides common functionality for showing chips on the sender device * and the receiver device. * * Subclasses need to override and implement [updateChipView], which is where they can control what * gets displayed to the user. */ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>( private val context: Context, private val windowManager: WindowManager, @LayoutRes private val chipLayoutRes: Int ) { /** The window layout parameters we'll use when attaching the view to a window. */ @SuppressLint("WrongConstant") // We're allowed to use TYPE_VOLUME_OVERLAY private val windowLayoutParams = WindowManager.LayoutParams().apply { width = WindowManager.LayoutParams.WRAP_CONTENT height = WindowManager.LayoutParams.WRAP_CONTENT gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL) type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL title = "Media Tap-To-Transfer Chip View" format = PixelFormat.TRANSLUCENT setTrustedOverlay() } /** The chip view currently being displayed. Null if the chip is not being displayed. */ var chipView: ViewGroup? = null /** * Displays the chip with the current state. * * This method handles inflating and attaching the view, then delegates to [updateChipView] to * display the correct information in the chip. */ fun displayChip(chipState: T) { val oldChipView = chipView if (chipView == null) { chipView = LayoutInflater .from(context) .inflate(chipLayoutRes, null) as ViewGroup } val currentChipView = chipView!! updateChipView(chipState, currentChipView) // Add view if necessary if (oldChipView == null) { windowManager.addView(chipView, windowLayoutParams) } } /** Hides the chip. */ fun removeChip() { if (chipView == null) { return } windowManager.removeView(chipView) chipView = null } /** * A method implemented by subclasses to update [currentChipView] based on [chipState]. */ abstract fun updateChipView(chipState: T, currentChipView: ViewGroup) /** * An internal method to set the icon on the view. * * This is in the common superclass since both the sender and the receiver show an icon. */ internal fun setIcon(chipState: T, currentChipView: ViewGroup) { currentChipView.findViewById<CachingIconView>(R.id.app_icon).apply { this.setImageDrawable(chipState.appIconDrawable) } } } Loading
packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +3 −3 Original line number Diff line number Diff line Loading @@ -23,8 +23,8 @@ import com.android.systemui.InitController; import com.android.systemui.SystemUIAppComponentFactory; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardSliceProvider; import com.android.systemui.media.taptotransfer.MediaTttChipController; import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper; import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender; import com.android.systemui.people.PeopleProvider; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.unfold.SysUIUnfoldComponent; Loading Loading @@ -134,7 +134,7 @@ public interface SysUIComponent { }); getNaturalRotationUnfoldProgressProvider().ifPresent(o -> o.init()); // No init method needed, just needs to be gotten so that it's created. getMediaTttChipController(); getMediaTttChipControllerSender(); getMediaTttCommandLineHelper(); getUnfoldLatencyTracker().init(); } Loading Loading @@ -190,7 +190,7 @@ public interface SysUIComponent { Optional<NaturalRotationUnfoldProgressProvider> getNaturalRotationUnfoldProgressProvider(); /** */ Optional<MediaTttChipController> getMediaTttChipController(); Optional<MediaTttChipControllerSender> getMediaTttChipControllerSender(); /** */ Optional<MediaTttCommandLineHelper> getMediaTttCommandLineHelper(); Loading
packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java +5 −5 Original line number Diff line number Diff line Loading @@ -26,9 +26,9 @@ import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.media.MediaHost; import com.android.systemui.media.MediaHostStatesManager; import com.android.systemui.media.taptotransfer.MediaTttChipController; import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper; import com.android.systemui.media.taptotransfer.MediaTttFlags; import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender; import com.android.systemui.statusbar.commandline.CommandRegistry; import com.android.systemui.util.concurrency.DelayableExecutor; Loading Loading @@ -80,7 +80,7 @@ public interface MediaModule { /** */ @Provides @SysUISingleton static Optional<MediaTttChipController> providesMediaTttChipController( static Optional<MediaTttChipControllerSender> providesMediaTttChipControllerSender( MediaTttFlags mediaTttFlags, Context context, WindowManager windowManager, Loading @@ -89,7 +89,7 @@ public interface MediaModule { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } return Optional.of(new MediaTttChipController( return Optional.of(new MediaTttChipControllerSender( context, windowManager, mainExecutor, backgroundExecutor)); } Loading @@ -100,12 +100,12 @@ public interface MediaModule { MediaTttFlags mediaTttFlags, CommandRegistry commandRegistry, Context context, MediaTttChipController mediaTttChipController, MediaTttChipControllerSender mediaTttChipControllerSender, @Main DelayableExecutor mainExecutor) { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } return Optional.of(new MediaTttCommandLineHelper( commandRegistry, context, mediaTttChipController, mainExecutor)); commandRegistry, context, mediaTttChipControllerSender, mainExecutor)); } }
packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt +13 −8 Original line number Diff line number Diff line Loading @@ -23,6 +23,10 @@ import androidx.annotation.VisibleForTesting import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender import com.android.systemui.media.taptotransfer.sender.MoveCloserToTransfer import com.android.systemui.media.taptotransfer.sender.TransferInitiated import com.android.systemui.media.taptotransfer.sender.TransferSucceeded import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.util.concurrency.DelayableExecutor Loading @@ -38,7 +42,7 @@ import javax.inject.Inject class MediaTttCommandLineHelper @Inject constructor( commandRegistry: CommandRegistry, context: Context, private val mediaTttChipController: MediaTttChipController, private val mediaTttChipControllerSender: MediaTttChipControllerSender, @Main private val mainExecutor: DelayableExecutor, ) { private val appIconDrawable = Loading @@ -54,21 +58,21 @@ class MediaTttCommandLineHelper @Inject constructor( val otherDeviceName = args[0] when (args[1]) { MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME -> { mediaTttChipController.displayChip( MoveCloserToTransfer(otherDeviceName, appIconDrawable) mediaTttChipControllerSender.displayChip( MoveCloserToTransfer(appIconDrawable, otherDeviceName) ) } TRANSFER_INITIATED_COMMAND_NAME -> { val futureTask = FutureTask { fakeUndoRunnable } mediaTttChipController.displayChip( TransferInitiated(otherDeviceName, appIconDrawable, futureTask) mediaTttChipControllerSender.displayChip( TransferInitiated(appIconDrawable, otherDeviceName, futureTask) ) mainExecutor.executeDelayed({ futureTask.run() }, FUTURE_WAIT_TIME) } TRANSFER_SUCCEEDED_COMMAND_NAME -> { mediaTttChipController.displayChip( TransferSucceeded(otherDeviceName, appIconDrawable, fakeUndoRunnable) mediaTttChipControllerSender.displayChip( TransferSucceeded(appIconDrawable, otherDeviceName, fakeUndoRunnable) ) } else -> { Loading @@ -90,7 +94,7 @@ class MediaTttCommandLineHelper @Inject constructor( inner class RemoveChipCommand : Command { override fun execute(pw: PrintWriter, args: List<String>) { mediaTttChipController.removeChip() mediaTttChipControllerSender.removeChip() } override fun help(pw: PrintWriter) { pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_TAG") Loading @@ -114,3 +118,4 @@ val TRANSFER_INITIATED_COMMAND_NAME = TransferInitiated::class.simpleName!! val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!! private const val FUTURE_WAIT_TIME = 2000L private const val TAG = "MediaTapToTransferCli"
packages/SystemUI/src/com/android/systemui/media/taptotransfer/README.md 0 → 100644 +11 −0 Original line number Diff line number Diff line # Media Tap-To-Transfer This package (and child packages) include code for the media tap-to-transfer feature, which allows users to easily transfer playing media between devices. In media transfer, there are two devices: the *sender* and the *receiver*. The sender device will start and stop media casts to the receiver device. On both devices, System UI will display a chip informing the user about the media cast occurring. This package is structured so that the sender code is in the sender package, the receiver code is in the receiver package, and code that's shared between them is in the common package.
packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt 0 → 100644 +104 −0 Original line number Diff line number Diff line /* * 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.media.taptotransfer.common import android.annotation.LayoutRes import android.annotation.SuppressLint import android.content.Context import android.graphics.PixelFormat import android.view.Gravity import android.view.LayoutInflater import android.view.ViewGroup import android.view.WindowManager import com.android.internal.widget.CachingIconView import com.android.systemui.R /** * A superclass controller that provides common functionality for showing chips on the sender device * and the receiver device. * * Subclasses need to override and implement [updateChipView], which is where they can control what * gets displayed to the user. */ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>( private val context: Context, private val windowManager: WindowManager, @LayoutRes private val chipLayoutRes: Int ) { /** The window layout parameters we'll use when attaching the view to a window. */ @SuppressLint("WrongConstant") // We're allowed to use TYPE_VOLUME_OVERLAY private val windowLayoutParams = WindowManager.LayoutParams().apply { width = WindowManager.LayoutParams.WRAP_CONTENT height = WindowManager.LayoutParams.WRAP_CONTENT gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL) type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL title = "Media Tap-To-Transfer Chip View" format = PixelFormat.TRANSLUCENT setTrustedOverlay() } /** The chip view currently being displayed. Null if the chip is not being displayed. */ var chipView: ViewGroup? = null /** * Displays the chip with the current state. * * This method handles inflating and attaching the view, then delegates to [updateChipView] to * display the correct information in the chip. */ fun displayChip(chipState: T) { val oldChipView = chipView if (chipView == null) { chipView = LayoutInflater .from(context) .inflate(chipLayoutRes, null) as ViewGroup } val currentChipView = chipView!! updateChipView(chipState, currentChipView) // Add view if necessary if (oldChipView == null) { windowManager.addView(chipView, windowLayoutParams) } } /** Hides the chip. */ fun removeChip() { if (chipView == null) { return } windowManager.removeView(chipView) chipView = null } /** * A method implemented by subclasses to update [currentChipView] based on [chipState]. */ abstract fun updateChipView(chipState: T, currentChipView: ViewGroup) /** * An internal method to set the icon on the view. * * This is in the common superclass since both the sender and the receiver show an icon. */ internal fun setIcon(chipState: T, currentChipView: ViewGroup) { currentChipView.findViewById<CachingIconView>(R.id.app_icon).apply { this.setImageDrawable(chipState.appIconDrawable) } } }