| /* |
| * Copyright (C) 2017 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.example.android.pictureinpicture; |
| |
| import android.app.PendingIntent; |
| import android.app.PictureInPictureParams; |
| import android.app.RemoteAction; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.res.Configuration; |
| import android.graphics.drawable.Icon; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.support.annotation.DrawableRes; |
| import android.support.v7.app.AppCompatActivity; |
| import android.util.Rational; |
| import android.view.View; |
| import android.widget.Button; |
| import android.widget.ScrollView; |
| |
| import com.example.android.pictureinpicture.widget.MovieView; |
| |
| import java.util.ArrayList; |
| |
| /** Demonstrates usage of Picture-in-Picture mode on phones and tablets. */ |
| public class MainActivity extends AppCompatActivity { |
| |
| /** Intent action for media controls from Picture-in-Picture mode. */ |
| private static final String ACTION_MEDIA_CONTROL = "media_control"; |
| |
| /** Intent extra for media controls from Picture-in-Picture mode. */ |
| private static final String EXTRA_CONTROL_TYPE = "control_type"; |
| |
| /** The request code for play action PendingIntent. */ |
| private static final int REQUEST_PLAY = 1; |
| |
| /** The request code for pause action PendingIntent. */ |
| private static final int REQUEST_PAUSE = 2; |
| |
| /** The request code for info action PendingIntent. */ |
| private static final int REQUEST_INFO = 3; |
| |
| /** The intent extra value for play action. */ |
| private static final int CONTROL_TYPE_PLAY = 1; |
| |
| /** The intent extra value for pause action. */ |
| private static final int CONTROL_TYPE_PAUSE = 2; |
| |
| /** The arguments to be used for Picture-in-Picture mode. */ |
| private final PictureInPictureParams.Builder mPictureInPictureParamsBuilder = |
| new PictureInPictureParams.Builder(); |
| |
| /** This shows the video. */ |
| private MovieView mMovieView; |
| |
| /** The bottom half of the screen; hidden on landscape */ |
| private ScrollView mScrollView; |
| |
| /** A {@link BroadcastReceiver} to receive action item events from Picture-in-Picture mode. */ |
| private BroadcastReceiver mReceiver; |
| |
| private String mPlay; |
| private String mPause; |
| |
| private final View.OnClickListener mOnClickListener = |
| new View.OnClickListener() { |
| @Override |
| public void onClick(View view) { |
| switch (view.getId()) { |
| case R.id.pip: |
| minimize(); |
| break; |
| } |
| } |
| }; |
| |
| /** Callbacks from the {@link MovieView} showing the video playback. */ |
| private MovieView.MovieListener mMovieListener = |
| new MovieView.MovieListener() { |
| |
| @Override |
| public void onMovieStarted() { |
| // We are playing the video now. In PiP mode, we want to show an action item to |
| // pause |
| // the video. |
| updatePictureInPictureActions( |
| R.drawable.ic_pause_24dp, mPause, CONTROL_TYPE_PAUSE, REQUEST_PAUSE); |
| } |
| |
| @Override |
| public void onMovieStopped() { |
| // The video stopped or reached its end. In PiP mode, we want to show an action |
| // item to play the video. |
| updatePictureInPictureActions( |
| R.drawable.ic_play_arrow_24dp, mPlay, CONTROL_TYPE_PLAY, REQUEST_PLAY); |
| } |
| |
| @Override |
| public void onMovieMinimized() { |
| // The MovieView wants us to minimize it. We enter Picture-in-Picture mode now. |
| minimize(); |
| } |
| }; |
| |
| /** |
| * Update the state of pause/resume action item in Picture-in-Picture mode. |
| * |
| * @param iconId The icon to be used. |
| * @param title The title text. |
| * @param controlType The type of the action. either {@link #CONTROL_TYPE_PLAY} or {@link |
| * #CONTROL_TYPE_PAUSE}. |
| * @param requestCode The request code for the {@link PendingIntent}. |
| */ |
| void updatePictureInPictureActions( |
| @DrawableRes int iconId, String title, int controlType, int requestCode) { |
| final ArrayList<RemoteAction> actions = new ArrayList<>(); |
| |
| // This is the PendingIntent that is invoked when a user clicks on the action item. |
| // You need to use distinct request codes for play and pause, or the PendingIntent won't |
| // be properly updated. |
| final PendingIntent intent = |
| PendingIntent.getBroadcast( |
| MainActivity.this, |
| requestCode, |
| new Intent(ACTION_MEDIA_CONTROL).putExtra(EXTRA_CONTROL_TYPE, controlType), |
| 0); |
| final Icon icon = Icon.createWithResource(MainActivity.this, iconId); |
| actions.add(new RemoteAction(icon, title, title, intent)); |
| |
| // Another action item. This is a fixed action. |
| actions.add( |
| new RemoteAction( |
| Icon.createWithResource(MainActivity.this, R.drawable.ic_info_24dp), |
| getString(R.string.info), |
| getString(R.string.info_description), |
| PendingIntent.getActivity( |
| MainActivity.this, |
| REQUEST_INFO, |
| new Intent( |
| Intent.ACTION_VIEW, |
| Uri.parse(getString(R.string.info_uri))), |
| 0))); |
| |
| mPictureInPictureParamsBuilder.setActions(actions); |
| |
| // This is how you can update action items (or aspect ratio) for Picture-in-Picture mode. |
| // Note this call can happen even when the app is not in PiP mode. In that case, the |
| // arguments will be used for at the next call of #enterPictureInPictureMode. |
| setPictureInPictureParams(mPictureInPictureParamsBuilder.build()); |
| } |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.activity_main); |
| |
| // Prepare string resources for Picture-in-Picture actions. |
| mPlay = getString(R.string.play); |
| mPause = getString(R.string.pause); |
| |
| // View references |
| mMovieView = findViewById(R.id.movie); |
| mScrollView = findViewById(R.id.scroll); |
| |
| Button switchExampleButton = findViewById(R.id.switch_example); |
| switchExampleButton.setText(getString(R.string.switch_media_session)); |
| switchExampleButton.setOnClickListener(new SwitchActivityOnClick()); |
| |
| // Set up the video; it automatically starts. |
| mMovieView.setMovieListener(mMovieListener); |
| findViewById(R.id.pip).setOnClickListener(mOnClickListener); |
| } |
| |
| @Override |
| protected void onStop() { |
| // On entering Picture-in-Picture mode, onPause is called, but not onStop. |
| // For this reason, this is the place where we should pause the video playback. |
| mMovieView.pause(); |
| super.onStop(); |
| } |
| |
| @Override |
| protected void onRestart() { |
| super.onRestart(); |
| if (!isInPictureInPictureMode()) { |
| // Show the video controls so the video can be easily resumed. |
| mMovieView.showControls(); |
| } |
| } |
| |
| @Override |
| public void onConfigurationChanged(Configuration newConfig) { |
| super.onConfigurationChanged(newConfig); |
| adjustFullScreen(newConfig); |
| } |
| |
| @Override |
| public void onWindowFocusChanged(boolean hasFocus) { |
| super.onWindowFocusChanged(hasFocus); |
| if (hasFocus) { |
| adjustFullScreen(getResources().getConfiguration()); |
| } |
| } |
| |
| @Override |
| public void onPictureInPictureModeChanged( |
| boolean isInPictureInPictureMode, Configuration configuration) { |
| super.onPictureInPictureModeChanged(isInPictureInPictureMode, configuration); |
| if (isInPictureInPictureMode) { |
| // Starts receiving events from action items in PiP mode. |
| mReceiver = |
| new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| if (intent == null |
| || !ACTION_MEDIA_CONTROL.equals(intent.getAction())) { |
| return; |
| } |
| |
| // This is where we are called back from Picture-in-Picture action |
| // items. |
| final int controlType = intent.getIntExtra(EXTRA_CONTROL_TYPE, 0); |
| switch (controlType) { |
| case CONTROL_TYPE_PLAY: |
| mMovieView.play(); |
| break; |
| case CONTROL_TYPE_PAUSE: |
| mMovieView.pause(); |
| break; |
| } |
| } |
| }; |
| registerReceiver(mReceiver, new IntentFilter(ACTION_MEDIA_CONTROL)); |
| } else { |
| // We are out of PiP mode. We can stop receiving events from it. |
| unregisterReceiver(mReceiver); |
| mReceiver = null; |
| // Show the video controls if the video is not playing |
| if (mMovieView != null && !mMovieView.isPlaying()) { |
| mMovieView.showControls(); |
| } |
| } |
| } |
| |
| /** Enters Picture-in-Picture mode. */ |
| void minimize() { |
| if (mMovieView == null) { |
| return; |
| } |
| // Hide the controls in picture-in-picture mode. |
| mMovieView.hideControls(); |
| // Calculate the aspect ratio of the PiP screen. |
| Rational aspectRatio = new Rational(mMovieView.getWidth(), mMovieView.getHeight()); |
| mPictureInPictureParamsBuilder.setAspectRatio(aspectRatio).build(); |
| enterPictureInPictureMode(mPictureInPictureParamsBuilder.build()); |
| } |
| |
| /** |
| * Adjusts immersive full-screen flags depending on the screen orientation. |
| * |
| * @param config The current {@link Configuration}. |
| */ |
| private void adjustFullScreen(Configuration config) { |
| final View decorView = getWindow().getDecorView(); |
| if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) { |
| decorView.setSystemUiVisibility( |
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
| | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
| | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
| | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
| | View.SYSTEM_UI_FLAG_FULLSCREEN |
| | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); |
| mScrollView.setVisibility(View.GONE); |
| mMovieView.setAdjustViewBounds(false); |
| } else { |
| decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); |
| mScrollView.setVisibility(View.VISIBLE); |
| mMovieView.setAdjustViewBounds(true); |
| } |
| } |
| |
| /** Launches {@link MediaSessionPlaybackActivity} and closes this activity. */ |
| private class SwitchActivityOnClick implements View.OnClickListener { |
| @Override |
| public void onClick(View view) { |
| startActivity(new Intent(view.getContext(), MediaSessionPlaybackActivity.class)); |
| finish(); |
| } |
| } |
| } |