Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 62338192 authored by Jaewan Kim's avatar Jaewan Kim Committed by Dongwon Kang
Browse files

PIP: Implement play/pause button in menu

Bug: 26685243
Change-Id: Id3138cc35dbef90d91767797e7f26f3c60191b1a
parent 342006e9
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->

<selector xmlns:android="http://schemas.android.com/apk/res/android"
    android:constantSize="true">
    <item android:state_focused="true">
        <layer-list>
            <item android:drawable="@drawable/tv_pip_button_focused" />
            <item android:drawable="@drawable/ic_play_arrow_white_24dp" />
        </layer-list>
    </item>
    <item android:drawable="@drawable/ic_play_arrow_white_24dp" />
</selector>
+4 −5
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@
        android:gravity="center"
        android:clipChildren="false">

        <ImageView android:id="@+id/full"
        <ImageView android:id="@+id/full_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:focusable="true"
@@ -53,17 +53,16 @@
            android:clipChildren="false" />
    </LinearLayout>

    <LinearLayout
    <LinearLayout android:id="@+id/play_pause"
        android:layout_width="34dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="3dp"
        android:layout_marginEnd="3dp"
        android:orientation="vertical"
        android:gravity="center"
        android:visibility="gone"
        android:clipChildren="false">

        <ImageView android:id="@+id/play_pause"
        <ImageView android:id="@+id/play_pause_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:focusable="true"
@@ -90,7 +89,7 @@
        android:gravity="center"
        android:clipChildren="false">

        <ImageView android:id="@+id/close"
        <ImageView android:id="@+id/close_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:focusable="true"
+3 −0
Original line number Diff line number Diff line
@@ -97,6 +97,9 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {

        @Override
        public void onPipResizeAboutToStart() { }

        @Override
        public void onMediaControllerChanged() { }
    };

    /**
+53 −0
Original line number Diff line number Diff line
@@ -23,11 +23,14 @@ import android.app.ActivityOptions;
import android.app.IActivityManager;
import android.app.ITaskStackListener;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Rect;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
import android.os.Debug;
import android.os.Handler;
import android.os.RemoteException;
@@ -69,6 +72,7 @@ public class PipManager {

    private Context mContext;
    private IActivityManager mActivityManager;
    private MediaSessionManager mMediaSessionManager;
    private int mState = STATE_NO_PIP;
    private final Handler mHandler = new Handler();
    private List<Listener> mListeners = new ArrayList<>();
@@ -79,6 +83,8 @@ public class PipManager {
    private Rect mRecentsFocusedPipBounds;
    private boolean mInitialized;
    private int mPipTaskId = TASK_ID_NO_PIP;
    private ComponentName mPipComponentName;
    private MediaController mPipMediaController;
    private boolean mOnboardingShown;

    private boolean mIsRecentsShown;
@@ -100,10 +106,15 @@ public class PipManager {
            }
            if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
            mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
            mPipComponentName = ComponentName.unflattenFromString(
                    stackInfo.taskNames[stackInfo.taskNames.length - 1]);
            // Set state to overlay so we show it when the pinned stack animation ends.
            mState = STATE_PIP_OVERLAY;
            mCurrentPipBounds = mPipBounds;
            launchPipOnboardingActivityIfNeeded();
            mMediaSessionManager.addOnActiveSessionsChangedListener(
                    mActiveMediaSessionListener, null);
            updateMediaController(mMediaSessionManager.getActiveSessions(null));
        }
    };
    private final Runnable mOnTaskStackChanged = new Runnable() {
@@ -176,6 +187,13 @@ public class PipManager {

        }
    };
    private final MediaSessionManager.OnActiveSessionsChangedListener mActiveMediaSessionListener =
            new MediaSessionManager.OnActiveSessionsChangedListener() {
                @Override
                public void onActiveSessionsChanged(List<MediaController> controllers) {
                    updateMediaController(controllers);
                }
            };

    private PipManager() { }

@@ -215,6 +233,9 @@ public class PipManager {
        mContext.registerReceiver(mBroadcastReceiver, intentFilter);
        mOnboardingShown = Prefs.getBoolean(
                mContext, TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN, false);

        mMediaSessionManager =
                (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
    }

    /**
@@ -248,6 +269,8 @@ public class PipManager {
    private void closePipInternal(boolean removePipStack) {
        mState = STATE_NO_PIP;
        mPipTaskId = TASK_ID_NO_PIP;
        mPipMediaController = null;
        mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener);
        if (removePipStack) {
            try {
                mActivityManager.removeStack(PINNED_STACK_ID);
@@ -502,6 +525,34 @@ public class PipManager {
        }
    }

    private void updateMediaController(List<MediaController> controllers) {
        MediaController mediaController = null;
        if (controllers != null && mState != STATE_NO_PIP && mPipComponentName != null) {
            for (int i = controllers.size() - 1; i >= 0; i--) {
                MediaController controller = controllers.get(i);
                // We assumes that an app with PIPable activity
                // keeps the single instance of media controller especially when PIP is on.
                if (controller.getPackageName().equals(mPipComponentName.getPackageName())) {
                    mediaController = controller;
                    break;
                }
            }
        }
        if (mPipMediaController != mediaController) {
            mPipMediaController = mediaController;
            for (int i = mListeners.size() - 1; i >= 0; i--) {
                mListeners.get(i).onMediaControllerChanged();
            }
        }
    }

    /**
     * Gets the {@link android.media.session.MediaController} for the PIPed activity.
     */
    MediaController getMediaController() {
        return mPipMediaController;
    }

    private class TaskStackListener extends ITaskStackListener.Stub {
        @Override
        public void onTaskStackChanged() throws RemoteException {
@@ -542,6 +593,8 @@ public class PipManager {
        void onMoveToFullscreen();
        /** Invoked when we are above to start resizing the Pip. */
        void onPipResizeAboutToStart();
        /** Invoked when the MediaController on PIPed activity is changed. */
        void onMediaControllerChanged();
    }

    /**
+89 −11
Original line number Diff line number Diff line
@@ -18,34 +18,49 @@ package com.android.systemui.tv.pip;

import android.app.Activity;
import android.media.session.MediaController;
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.android.systemui.R;

import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.media.session.PlaybackState.ACTION_PAUSE;
import static android.media.session.PlaybackState.ACTION_PLAY;

/**
 * Activity to show the PIP menu to control PIP.
 */
public class PipMenuActivity extends Activity implements PipManager.Listener {
    private static final String TAG = "PipMenuActivity";
    private static final boolean DEBUG = false;

    private final PipManager mPipManager = PipManager.getInstance();
    private MediaController mMediaController;

    private View mFullButtonView;
    private View mFullDescriptionView;
    private View mPlayPauseButtonView;
    private View mPlayPauseDescriptionView;
    private View mPlayPauseView;
    private ImageView mPlayPauseButtonImageView;
    private TextView mPlayPauseDescriptionTextView;
    private View mCloseButtonView;
    private View mCloseDescriptionView;

    private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
        @Override
        public void onPlaybackStateChanged(PlaybackState state) {
            updatePlayPauseView(state);
        }
    };

    @Override
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.tv_pip_menu);
        mPipManager.addListener(this);
        mFullButtonView = findViewById(R.id.full);
        mFullButtonView = findViewById(R.id.full_button);
        mFullDescriptionView = findViewById(R.id.full_desc);
        mFullButtonView.setOnClickListener(new View.OnClickListener() {
            @Override
@@ -61,22 +76,33 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
            }
        });

        mPlayPauseButtonView = findViewById(R.id.play_pause);
        mPlayPauseDescriptionView = findViewById(R.id.play_pause_desc);
        mPlayPauseButtonView.setOnClickListener(new View.OnClickListener() {
        mPlayPauseView = findViewById(R.id.play_pause);
        mPlayPauseButtonImageView = (ImageView) findViewById(R.id.play_pause_button);
        mPlayPauseDescriptionTextView = (TextView) findViewById(R.id.play_pause_desc);
        mPlayPauseButtonImageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // TODO: Implement play/pause.
                if (mMediaController == null || mMediaController.getPlaybackState() == null) {
                    return;
                }
                long actions = mMediaController.getPlaybackState().getActions();
                int state = mMediaController.getPlaybackState().getState();
                if (((actions & ACTION_PLAY) != 0) && !isPlaying(state)) {
                    mMediaController.getTransportControls().play();
                } else if ((actions & ACTION_PAUSE) != 0 && isPlaying(state)) {
                    mMediaController.getTransportControls().pause();
                }
                // View will be updated later in {@link mMediaControllerCallback}
            }
        });
        mPlayPauseButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        mPlayPauseButtonImageView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                mPlayPauseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
                mPlayPauseDescriptionTextView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
            }
        });

        mCloseButtonView = findViewById(R.id.close);
        mCloseButtonView = findViewById(R.id.close_button);
        mCloseDescriptionView = findViewById(R.id.close_desc);
        mCloseButtonView.setOnClickListener(new View.OnClickListener() {
            @Override
@@ -91,6 +117,50 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
                mCloseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
            }
        });
        updateMediaController();
    }

    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);
            updatePlayPauseView(mMediaController.getPlaybackState());
        } else {
            updatePlayPauseView(null);
        }
    }

    private void updatePlayPauseView(PlaybackState playbackState) {
        if (playbackState != null
                && (playbackState.getActions() & (ACTION_PLAY | ACTION_PAUSE)) != 0) {
            mPlayPauseView.setVisibility(View.VISIBLE);
            if (isPlaying(playbackState.getState())) {
                mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_pause_button);
                mPlayPauseDescriptionTextView.setText(R.string.pip_pause);
            } else {
                mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_play_button);
                mPlayPauseDescriptionTextView.setText(R.string.pip_play);
            }
        } else {
            mPlayPauseView.setVisibility(View.GONE);
        }
    }

    private boolean isPlaying(int state) {
        return state == PlaybackState.STATE_BUFFERING
                || state == PlaybackState.STATE_CONNECTING
                || state == PlaybackState.STATE_PLAYING
                || state == PlaybackState.STATE_FAST_FORWARDING
                || state == PlaybackState.STATE_REWINDING
                || state == PlaybackState.STATE_SKIPPING_TO_PREVIOUS
                || state == PlaybackState.STATE_SKIPPING_TO_NEXT;
    }

    private void restorePipAndFinish() {
@@ -107,6 +177,9 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mMediaController != null) {
            mMediaController.unregisterCallback(mMediaControllerCallback);
        }
        mPipManager.removeListener(this);
        mPipManager.resumePipResizing(
                PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
@@ -130,6 +203,11 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
        finish();
    }

    @Override
    public void onMediaControllerChanged() {
        updateMediaController();
    }

    @Override
    public void onPipResizeAboutToStart() {
        finish();
Loading