Provide PipManager through injection.
Adds a controller for PipControlsView so that depdencies can be provided
via injection.
Adds a TvPipComponent to allow injection of PIP related views into related
classes.
Bug: 146660939
Test: atest SystemUITests
Change-Id: Ic1a8f8f0f8e5506f354bb1f07b179db1d7e89577
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
index 3bf5ad7..12b9be1 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
@@ -27,6 +27,7 @@
import com.android.systemui.SystemUIFactory;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardSliceProvider;
+import com.android.systemui.pip.phone.dagger.PipModule;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.InjectionInflationController;
@@ -43,6 +44,7 @@
DefaultComponentBinder.class,
DependencyProvider.class,
DependencyBinder.class,
+ PipModule.class,
SystemServicesModule.class,
SystemUIFactory.ContextHolder.class,
SystemUIBinder.class,
diff --git a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
index 92aa020..383d459 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
@@ -16,17 +16,11 @@
package com.android.systemui.pip;
-import android.content.Context;
import android.content.res.Configuration;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.wm.DisplayController;
-
import java.io.PrintWriter;
public interface BasePipManager {
- void initialize(Context context, BroadcastDispatcher broadcastDispatcher,
- DisplayController displayController);
void showPictureInPictureMenu();
default void expandPip() {}
default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
index cecdc9c..599c845 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
@@ -16,7 +16,6 @@
package com.android.systemui.pip;
-import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import android.content.Context;
@@ -26,9 +25,7 @@
import android.os.UserManager;
import com.android.systemui.SystemUI;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.wm.DisplayController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -44,25 +41,20 @@
private final CommandQueue mCommandQueue;
private BasePipManager mPipManager;
- private final BroadcastDispatcher mBroadcastDispatcher;
- private final DisplayController mDisplayController;
- private boolean mSupportsPip;
@Inject
public PipUI(Context context, CommandQueue commandQueue,
- BroadcastDispatcher broadcastDispatcher,
- DisplayController displayController) {
+ BasePipManager pipManager) {
super(context);
- mBroadcastDispatcher = broadcastDispatcher;
mCommandQueue = commandQueue;
- mDisplayController = displayController;
+ mPipManager = pipManager;
}
@Override
public void start() {
PackageManager pm = mContext.getPackageManager();
- mSupportsPip = pm.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
- if (!mSupportsPip) {
+ boolean supportsPip = pm.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
+ if (!supportsPip) {
return;
}
@@ -72,11 +64,6 @@
throw new IllegalStateException("Non-primary Pip component not currently supported.");
}
- mPipManager = pm.hasSystemFeature(FEATURE_LEANBACK_ONLY)
- ? com.android.systemui.pip.tv.PipManager.getInstance()
- : com.android.systemui.pip.phone.PipManager.getInstance();
- mPipManager.initialize(mContext, mBroadcastDispatcher, mDisplayController);
-
mCommandQueue.addCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 239ef36..b5c8d66 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -51,14 +51,16 @@
import java.io.PrintWriter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Manages the picture-in-picture (PIP) UI and states for Phones.
*/
+@Singleton
public class PipManager implements BasePipManager {
private static final String TAG = "PipManager";
- private static PipManager sPipController;
-
private Context mContext;
private IActivityManager mActivityManager;
private IActivityTaskManager mActivityTaskManager;
@@ -225,12 +227,8 @@
}
}
- private PipManager() {}
-
- /**
- * Initializes {@link PipManager}.
- */
- public void initialize(Context context, BroadcastDispatcher broadcastDispatcher,
+ @Inject
+ public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
DisplayController displayController) {
mContext = context;
mActivityManager = ActivityManager.getService();
@@ -329,16 +327,6 @@
mTmpDisplayInfo.rotation);
}
- /**
- * Gets an instance of {@link PipManager}.
- */
- public static PipManager getInstance() {
- if (sPipController == null) {
- sPipController = new PipManager();
- }
- return sPipController;
- }
-
public void dump(PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipModule.java b/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipModule.java
new file mode 100644
index 0000000..c8b6982
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipModule.java
@@ -0,0 +1,34 @@
+/*
+ * 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.pip.phone.dagger;
+
+import com.android.systemui.pip.BasePipManager;
+import com.android.systemui.pip.phone.PipManager;
+
+import dagger.Binds;
+import dagger.Module;
+
+/**
+ * Dagger Module for Phone PIP.
+ */
+@Module
+public abstract class PipModule {
+
+ /** Binds PipManager as the default BasePipManager. */
+ @Binds
+ public abstract BasePipManager providePipManager(PipManager pipManager);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
index a40b72b..9c175bc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
@@ -16,286 +16,38 @@
package com.android.systemui.pip.tv;
-import android.app.PendingIntent.CanceledException;
-import android.app.RemoteAction;
import android.content.Context;
-import android.graphics.Color;
-import android.media.session.MediaController;
-import android.media.session.PlaybackState;
-import android.os.Handler;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
-import android.view.View;
import android.widget.LinearLayout;
import com.android.systemui.R;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* A view containing PIP controls including fullscreen, close, and media controls.
*/
public class PipControlsView extends LinearLayout {
- private static final String TAG = PipControlsView.class.getSimpleName();
-
- private static final float DISABLED_ACTION_ALPHA = 0.54f;
-
- /**
- * An interface to listen user action.
- */
- public abstract static interface Listener {
- /**
- * Called when an user clicks close PIP button.
- */
- public abstract void onClosed();
- };
-
- private MediaController mMediaController;
-
- private final PipManager mPipManager = PipManager.getInstance();
- private final LayoutInflater mLayoutInflater;
- private final Handler mHandler;
- private Listener mListener;
-
- private PipControlButtonView mFullButtonView;
- private PipControlButtonView mCloseButtonView;
- private PipControlButtonView mPlayPauseButtonView;
- private ArrayList<PipControlButtonView> mCustomButtonViews = new ArrayList<>();
- private List<RemoteAction> mCustomActions = new ArrayList<>();
-
- private PipControlButtonView mFocusedChild;
-
- private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
- @Override
- public void onPlaybackStateChanged(PlaybackState state) {
- updateUserActions();
- }
- };
-
- private final PipManager.MediaListener mPipMediaListener = new PipManager.MediaListener() {
- @Override
- public void onMediaControllerChanged() {
- updateMediaController();
- }
- };
-
- private final OnFocusChangeListener mFocusChangeListener = new OnFocusChangeListener() {
- @Override
- public void onFocusChange(View view, boolean hasFocus) {
- if (hasFocus) {
- mFocusedChild = (PipControlButtonView) view;
- } else if (mFocusedChild == view) {
- mFocusedChild = null;
- }
- }
- };
-
- public PipControlsView(Context context) {
- this(context, null, 0, 0);
- }
-
- public PipControlsView(Context context, AttributeSet attrs) {
- this(context, attrs, 0, 0);
- }
-
- public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mLayoutInflater = (LayoutInflater) getContext()
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mLayoutInflater.inflate(R.layout.tv_pip_controls, this);
- mHandler = new Handler();
-
+ LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ layoutInflater.inflate(R.layout.tv_pip_controls, this);
setOrientation(LinearLayout.HORIZONTAL);
setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
}
- @Override
- public void onFinishInflate() {
- super.onFinishInflate();
-
- mFullButtonView = findViewById(R.id.full_button);
- mFullButtonView.setOnFocusChangeListener(mFocusChangeListener);
- mFullButtonView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mPipManager.movePipToFullscreen();
- }
- });
-
- mCloseButtonView = findViewById(R.id.close_button);
- mCloseButtonView.setOnFocusChangeListener(mFocusChangeListener);
- mCloseButtonView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mPipManager.closePip();
- if (mListener != null) {
- mListener.onClosed();
- }
- }
- });
-
- mPlayPauseButtonView = findViewById(R.id.play_pause_button);
- mPlayPauseButtonView.setOnFocusChangeListener(mFocusChangeListener);
- mPlayPauseButtonView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mMediaController == null || mMediaController.getPlaybackState() == null) {
- return;
- }
- long actions = mMediaController.getPlaybackState().getActions();
- int state = mMediaController.getPlaybackState().getState();
- if (mPipManager.getPlaybackState() == PipManager.PLAYBACK_STATE_PAUSED) {
- mMediaController.getTransportControls().play();
- } else if (mPipManager.getPlaybackState() == PipManager.PLAYBACK_STATE_PLAYING) {
- mMediaController.getTransportControls().pause();
- }
- // View will be updated later in {@link mMediaControllerCallback}
- }
- });
+ PipControlButtonView getFullButtonView() {
+ return findViewById(R.id.full_button);
}
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- updateMediaController();
- mPipManager.addMediaListener(mPipMediaListener);
+ PipControlButtonView getCloseButtonView() {
+ return findViewById(R.id.close_button);
}
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mPipManager.removeMediaListener(mPipMediaListener);
- if (mMediaController != null) {
- mMediaController.unregisterCallback(mMediaControllerCallback);
- }
- }
-
- private void updateMediaController() {
- MediaController newController = mPipManager.getMediaController();
- if (mMediaController == newController) {
- return;
- }
- if (mMediaController != null) {
- mMediaController.unregisterCallback(mMediaControllerCallback);
- }
- mMediaController = newController;
- if (mMediaController != null) {
- mMediaController.registerCallback(mMediaControllerCallback);
- }
- updateUserActions();
- }
-
- /**
- * Updates the actions for the PIP. If there are no custom actions, then the media session
- * actions are shown.
- */
- private void updateUserActions() {
- if (!mCustomActions.isEmpty()) {
- // Ensure we have as many buttons as actions
- while (mCustomButtonViews.size() < mCustomActions.size()) {
- PipControlButtonView buttonView = (PipControlButtonView) mLayoutInflater.inflate(
- R.layout.tv_pip_custom_control, this, false);
- addView(buttonView);
- mCustomButtonViews.add(buttonView);
- }
-
- // Update the visibility of all views
- for (int i = 0; i < mCustomButtonViews.size(); i++) {
- mCustomButtonViews.get(i).setVisibility(i < mCustomActions.size()
- ? View.VISIBLE
- : View.GONE);
- }
-
- // Update the state and visibility of the action buttons, and hide the rest
- for (int i = 0; i < mCustomActions.size(); i++) {
- final RemoteAction action = mCustomActions.get(i);
- PipControlButtonView actionView = mCustomButtonViews.get(i);
-
- // TODO: Check if the action drawable has changed before we reload it
- action.getIcon().loadDrawableAsync(getContext(), d -> {
- d.setTint(Color.WHITE);
- actionView.setImageDrawable(d);
- }, mHandler);
- actionView.setText(action.getContentDescription());
- if (action.isEnabled()) {
- actionView.setOnClickListener(v -> {
- try {
- action.getActionIntent().send();
- } catch (CanceledException e) {
- Log.w(TAG, "Failed to send action", e);
- }
- });
- }
- actionView.setEnabled(action.isEnabled());
- actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
- }
-
- // Hide the media session buttons
- mPlayPauseButtonView.setVisibility(View.GONE);
- } else {
- int state = mPipManager.getPlaybackState();
- if (state == PipManager.PLAYBACK_STATE_UNAVAILABLE) {
- mPlayPauseButtonView.setVisibility(View.GONE);
- } else {
- mPlayPauseButtonView.setVisibility(View.VISIBLE);
- if (state == PipManager.PLAYBACK_STATE_PLAYING) {
- mPlayPauseButtonView.setImageResource(R.drawable.ic_pause_white);
- mPlayPauseButtonView.setText(R.string.pip_pause);
- } else {
- mPlayPauseButtonView.setImageResource(R.drawable.ic_play_arrow_white);
- mPlayPauseButtonView.setText(R.string.pip_play);
- }
- }
-
- // Hide all the custom action buttons
- for (int i = 0; i < mCustomButtonViews.size(); i++) {
- mCustomButtonViews.get(i).setVisibility(View.GONE);
- }
- }
- }
-
- /**
- * Resets to initial state.
- */
- public void reset() {
- mFullButtonView.reset();
- mCloseButtonView.reset();
- mPlayPauseButtonView.reset();
- mFullButtonView.requestFocus();
- for (int i = 0; i < mCustomButtonViews.size(); i++) {
- mCustomButtonViews.get(i).reset();
- }
- }
-
- /**
- * Sets the {@link Listener} to listen user actions.
- */
- public void setListener(Listener listener) {
- mListener = listener;
- }
-
- /**
- * Updates the set of activity-defined actions.
- */
- public void setActions(List<RemoteAction> actions) {
- mCustomActions.clear();
- mCustomActions.addAll(actions);
- updateUserActions();
- }
-
- /**
- * Returns the focused control button view to animate focused button.
- */
- PipControlButtonView getFocusedButton() {
- return mFocusedChild;
+ PipControlButtonView getPlayPauseButtonView() {
+ return findViewById(R.id.play_pause_button);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java
new file mode 100644
index 0000000..1fe531b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java
@@ -0,0 +1,251 @@
+/*
+ * 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.pip.tv;
+
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.graphics.Color;
+import android.media.session.MediaController;
+import android.media.session.PlaybackState;
+import android.os.Handler;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * Controller for {@link PipControlsView}.
+ */
+public class PipControlsViewController {
+ private static final String TAG = PipControlsViewController.class.getSimpleName();
+
+ private static final float DISABLED_ACTION_ALPHA = 0.54f;
+
+ private final PipControlsView mView;
+ private final PipManager mPipManager;
+ private final LayoutInflater mLayoutInflater;
+ private final Handler mHandler;
+ private final PipControlButtonView mPlayPauseButtonView;
+ private MediaController mMediaController;
+ private PipControlButtonView mFocusedChild;
+ private Listener mListener;
+ private ArrayList<PipControlButtonView> mCustomButtonViews = new ArrayList<>();
+ private List<RemoteAction> mCustomActions = new ArrayList<>();
+
+ public PipControlsView getView() {
+ return mView;
+ }
+
+ /**
+ * An interface to listen user action.
+ */
+ public interface Listener {
+ /**
+ * Called when a user clicks close PIP button.
+ */
+ void onClosed();
+ }
+
+ private View.OnAttachStateChangeListener
+ mOnAttachStateChangeListener =
+ new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ updateMediaController();
+ mPipManager.addMediaListener(mPipMediaListener);
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ mPipManager.removeMediaListener(mPipMediaListener);
+ }
+ };
+
+ private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ updateUserActions();
+ }
+ };
+
+ private final PipManager.MediaListener mPipMediaListener = this::updateMediaController;
+
+ private final View.OnFocusChangeListener
+ mFocusChangeListener =
+ new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View view, boolean hasFocus) {
+ if (hasFocus) {
+ mFocusedChild = (PipControlButtonView) view;
+ } else if (mFocusedChild == view) {
+ mFocusedChild = null;
+ }
+ }
+ };
+
+
+ @Inject
+ public PipControlsViewController(PipControlsView view, PipManager pipManager,
+ LayoutInflater layoutInflater, @Main Handler handler) {
+ super();
+ mView = view;
+ mPipManager = pipManager;
+ mLayoutInflater = layoutInflater;
+ mHandler = handler;
+
+ mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
+ if (mView.isAttachedToWindow()) {
+ mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
+ }
+
+ View fullButtonView = mView.getFullButtonView();
+ fullButtonView.setOnFocusChangeListener(mFocusChangeListener);
+ fullButtonView.setOnClickListener(v -> mPipManager.movePipToFullscreen());
+
+ View closeButtonView = mView.getCloseButtonView();
+ closeButtonView.setOnFocusChangeListener(mFocusChangeListener);
+ closeButtonView.setOnClickListener(v -> {
+ mPipManager.closePip();
+ if (mListener != null) {
+ mListener.onClosed();
+ }
+ });
+
+
+ mPlayPauseButtonView = mView.getPlayPauseButtonView();
+ mPlayPauseButtonView.setOnFocusChangeListener(mFocusChangeListener);
+ mPlayPauseButtonView.setOnClickListener(v -> {
+ if (mMediaController == null || mMediaController.getPlaybackState() == null) {
+ return;
+ }
+ if (mPipManager.getPlaybackState() == PipManager.PLAYBACK_STATE_PAUSED) {
+ mMediaController.getTransportControls().play();
+ } else if (mPipManager.getPlaybackState() == PipManager.PLAYBACK_STATE_PLAYING) {
+ mMediaController.getTransportControls().pause();
+ }
+ // View will be updated later in {@link mMediaControllerCallback}
+ });
+ }
+
+ private void updateMediaController() {
+ MediaController newController = mPipManager.getMediaController();
+ if (mMediaController == newController) {
+ return;
+ }
+ if (mMediaController != null) {
+ mMediaController.unregisterCallback(mMediaControllerCallback);
+ }
+ mMediaController = newController;
+ if (mMediaController != null) {
+ mMediaController.registerCallback(mMediaControllerCallback);
+ }
+ updateUserActions();
+ }
+
+ /**
+ * Updates the actions for the PIP. If there are no custom actions, then the media session
+ * actions are shown.
+ */
+ private void updateUserActions() {
+ if (!mCustomActions.isEmpty()) {
+ // Ensure we have as many buttons as actions
+ while (mCustomButtonViews.size() < mCustomActions.size()) {
+ PipControlButtonView buttonView = (PipControlButtonView) mLayoutInflater.inflate(
+ R.layout.tv_pip_custom_control, mView, false);
+ mView.addView(buttonView);
+ mCustomButtonViews.add(buttonView);
+ }
+
+ // Update the visibility of all views
+ for (int i = 0; i < mCustomButtonViews.size(); i++) {
+ mCustomButtonViews.get(i).setVisibility(
+ i < mCustomActions.size() ? View.VISIBLE : View.GONE);
+ }
+
+ // Update the state and visibility of the action buttons, and hide the rest
+ for (int i = 0; i < mCustomActions.size(); i++) {
+ final RemoteAction action = mCustomActions.get(i);
+ PipControlButtonView actionView = mCustomButtonViews.get(i);
+
+ // TODO: Check if the action drawable has changed before we reload it
+ action.getIcon().loadDrawableAsync(mView.getContext(), d -> {
+ d.setTint(Color.WHITE);
+ actionView.setImageDrawable(d);
+ }, mHandler);
+ actionView.setText(action.getContentDescription());
+ if (action.isEnabled()) {
+ actionView.setOnClickListener(v -> {
+ try {
+ action.getActionIntent().send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "Failed to send action", e);
+ }
+ });
+ }
+ actionView.setEnabled(action.isEnabled());
+ actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
+ }
+
+ // Hide the media session buttons
+ mPlayPauseButtonView.setVisibility(View.GONE);
+ } else {
+ int state = mPipManager.getPlaybackState();
+ if (state == PipManager.PLAYBACK_STATE_UNAVAILABLE) {
+ mPlayPauseButtonView.setVisibility(View.GONE);
+ } else {
+ mPlayPauseButtonView.setVisibility(View.VISIBLE);
+ if (state == PipManager.PLAYBACK_STATE_PLAYING) {
+ mPlayPauseButtonView.setImageResource(R.drawable.ic_pause_white);
+ mPlayPauseButtonView.setText(R.string.pip_pause);
+ } else {
+ mPlayPauseButtonView.setImageResource(R.drawable.ic_play_arrow_white);
+ mPlayPauseButtonView.setText(R.string.pip_play);
+ }
+ }
+
+ // Hide all the custom action buttons
+ for (int i = 0; i < mCustomButtonViews.size(); i++) {
+ mCustomButtonViews.get(i).setVisibility(View.GONE);
+ }
+ }
+ }
+
+
+ /**
+ * Sets the {@link Listener} to listen user actions.
+ */
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+
+ /**
+ * Updates the set of activity-defined actions.
+ */
+ public void setActions(List<RemoteAction> actions) {
+ mCustomActions.clear();
+ mCustomActions.addAll(actions);
+ updateUserActions();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 7532f9f..487c253 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -55,21 +55,23 @@
import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.wm.DisplayController;
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Manages the picture-in-picture (PIP) UI and states.
*/
+@Singleton
public class PipManager implements BasePipManager {
private static final String TAG = "PipManager";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String SETTINGS_PACKAGE_AND_CLASS_DELIMITER = "/";
- private static PipManager sPipManager;
private static List<Pair<String, String>> sSettingsPackageAndClassNamePairList;
/**
@@ -224,13 +226,8 @@
}
}
- private PipManager() { }
-
- /**
- * Initializes {@link PipManager}.
- */
- public void initialize(Context context, BroadcastDispatcher broadcastDispatcher,
- DisplayController displayController) {
+ @Inject
+ public PipManager(Context context, BroadcastDispatcher broadcastDispatcher) {
if (mInitialized) {
return;
}
@@ -289,7 +286,7 @@
Log.e(TAG, "Failed to register pinned stack listener", e);
}
- mPipNotification = new PipNotification(context, broadcastDispatcher);
+ mPipNotification = new PipNotification(context, broadcastDispatcher, this);
}
private void loadConfigurationsAndApply(Configuration newConfig) {
@@ -739,16 +736,6 @@
void onMediaControllerChanged();
}
- /**
- * Gets an instance of {@link PipManager}.
- */
- public static PipManager getInstance() {
- if (sPipManager == null) {
- sPipManager = new PipManager();
- }
- return sPipManager;
- }
-
private void updatePipVisibility(final boolean visible) {
Dependency.get(UiOffloadThread.class).execute(() -> {
WindowManagerWrapper.getInstance().setPipVisibility(visible);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
index 3a5fa22..f43f8e7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
@@ -24,8 +24,12 @@
import android.os.Bundle;
import com.android.systemui.R;
+import com.android.systemui.pip.tv.dagger.TvPipComponent;
import java.util.Collections;
+
+import javax.inject.Inject;
+
/**
* Activity to show the PIP menu to control PIP.
*/
@@ -34,12 +38,22 @@
static final String EXTRA_CUSTOM_ACTIONS = "custom_actions";
- private final PipManager mPipManager = PipManager.getInstance();
+ private final TvPipComponent.Builder mPipComponentBuilder;
+ private TvPipComponent mTvPipComponent;
+ private final PipManager mPipManager;
private Animator mFadeInAnimation;
private Animator mFadeOutAnimation;
- private PipControlsView mPipControlsView;
private boolean mRestorePipSizeWhenClose;
+ private PipControlsViewController mPipControlsViewController;
+
+
+ @Inject
+ public PipMenuActivity(TvPipComponent.Builder pipComponentBuilder, PipManager pipManager) {
+ super();
+ mPipComponentBuilder = pipComponentBuilder;
+ mPipManager = pipManager;
+ }
@Override
protected void onCreate(Bundle bundle) {
@@ -48,16 +62,19 @@
finish();
}
setContentView(R.layout.tv_pip_menu);
+ mTvPipComponent = mPipComponentBuilder.pipControlsView(
+ findViewById(R.id.pip_controls)).build();
+ mPipControlsViewController = mTvPipComponent.getPipControlsViewController();
+
mPipManager.addListener(this);
mRestorePipSizeWhenClose = true;
- mPipControlsView = findViewById(R.id.pip_controls);
mFadeInAnimation = AnimatorInflater.loadAnimator(
this, R.anim.tv_pip_menu_fade_in_animation);
- mFadeInAnimation.setTarget(mPipControlsView);
+ mFadeInAnimation.setTarget(mPipControlsViewController.getView());
mFadeOutAnimation = AnimatorInflater.loadAnimator(
this, R.anim.tv_pip_menu_fade_out_animation);
- mFadeOutAnimation.setTarget(mPipControlsView);
+ mFadeOutAnimation.setTarget(mPipControlsViewController.getView());
onPipMenuActionsChanged(getIntent().getParcelableExtra(EXTRA_CUSTOM_ACTIONS));
}
@@ -114,7 +131,8 @@
@Override
public void onPipMenuActionsChanged(ParceledListSlice actions) {
boolean hasCustomActions = actions != null && !actions.getList().isEmpty();
- mPipControlsView.setActions(hasCustomActions ? actions.getList() : Collections.EMPTY_LIST);
+ mPipControlsViewController.setActions(
+ hasCustomActions ? actions.getList() : Collections.EMPTY_LIST);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
index ca15131..b01c2f4 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
@@ -50,7 +50,7 @@
private static final String ACTION_MENU = "PipNotification.menu";
private static final String ACTION_CLOSE = "PipNotification.close";
- private final PipManager mPipManager = PipManager.getInstance();
+ private final PipManager mPipManager;
private final NotificationManager mNotificationManager;
private final Notification.Builder mNotificationBuilder;
@@ -144,7 +144,8 @@
}
};
- public PipNotification(Context context, BroadcastDispatcher broadcastDispatcher) {
+ public PipNotification(Context context, BroadcastDispatcher broadcastDispatcher,
+ PipManager pipManager) {
mNotificationManager = (NotificationManager) context.getSystemService(
Context.NOTIFICATION_SERVICE);
@@ -156,6 +157,7 @@
.setContentIntent(createPendingIntent(context, ACTION_MENU))
.setDeleteIntent(createPendingIntent(context, ACTION_CLOSE)));
+ mPipManager = pipManager;
mPipManager.addListener(mPipListener);
mPipManager.addMediaListener(mPipMediaListener);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/dagger/PipModule.java b/packages/SystemUI/src/com/android/systemui/pip/tv/dagger/PipModule.java
new file mode 100644
index 0000000..52b38a9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/dagger/PipModule.java
@@ -0,0 +1,46 @@
+/*
+ * 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.pip.tv.dagger;
+
+import android.app.Activity;
+
+import com.android.systemui.pip.BasePipManager;
+import com.android.systemui.pip.tv.PipManager;
+import com.android.systemui.pip.tv.PipMenuActivity;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+
+/**
+ * Dagger module for TV Pip.
+ */
+@Module(subcomponents = {TvPipComponent.class})
+public abstract class PipModule {
+
+ /** Binds PipManager as the default BasePipManager. */
+ @Binds
+ public abstract BasePipManager providePipManager(PipManager pipManager);
+
+
+ /** Inject into PipMenuActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(PipMenuActivity.class)
+ public abstract Activity providePipMenuActivity(PipMenuActivity activity);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/dagger/TvPipComponent.java b/packages/SystemUI/src/com/android/systemui/pip/tv/dagger/TvPipComponent.java
new file mode 100644
index 0000000..8e8b7f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/dagger/TvPipComponent.java
@@ -0,0 +1,61 @@
+/*
+ * 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.pip.tv.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.pip.tv.PipControlsView;
+import com.android.systemui.pip.tv.PipControlsViewController;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Scope;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Component for injecting into Pip related classes.
+ */
+@Subcomponent
+public interface TvPipComponent {
+ /**
+ * Builder for {@link StatusBarComponent}.
+ */
+ @Subcomponent.Builder
+ interface Builder {
+ @BindsInstance
+ TvPipComponent.Builder pipControlsView(PipControlsView pipControlsView);
+ TvPipComponent build();
+ }
+
+ /**
+ * Scope annotation for singleton items within the PipComponent.
+ */
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface PipScope {}
+
+ /**
+ * Creates a StatusBarWindowViewController.
+ */
+ @TvPipComponent.PipScope
+ PipControlsViewController getPipControlsViewController();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
index 264ddc0..be30a4a 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
@@ -17,11 +17,12 @@
package com.android.systemui.tv;
import com.android.systemui.dagger.SystemUIRootComponent;
+import com.android.systemui.pip.tv.dagger.PipModule;
import dagger.Binds;
import dagger.Module;
-@Module
+@Module(includes = {PipModule.class})
interface TvSystemUIBinder {
@Binds
SystemUIRootComponent bindSystemUIRootComponent(TvSystemUIRootComponent systemUIRootComponent);