| /* |
| * 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.PictureInPictureArgs |
| 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.view.View |
| import android.widget.ScrollView |
| import com.example.android.pictureinpicture.widget.MovieView |
| import java.util.* |
| |
| |
| /** |
| * Demonstrates usage of Picture-in-Picture mode on phones and tablets. |
| */ |
| class MainActivity : AppCompatActivity() { |
| |
| /** The arguments to be used for Picture-in-Picture mode. */ |
| private val mPictureInPictureArgs = PictureInPictureArgs() |
| |
| /** This shows the video. */ |
| private var mMovieView: MovieView? = null |
| |
| /** The bottom half of the screen; hidden on landscape */ |
| private var mScrollView: ScrollView? = null |
| |
| /** A [BroadcastReceiver] to receive action item events from Picture-in-Picture mode. */ |
| private var mReceiver: BroadcastReceiver? = null |
| |
| private val labelPlay: String by lazy { getString(R.string.play) } |
| private val labelPause: String by lazy { getString(R.string.pause) } |
| |
| private val mOnClickListener = View.OnClickListener { view -> |
| when (view.id) { |
| R.id.pip -> minimize() |
| } |
| } |
| |
| /** |
| * Callbacks from the [MovieView] showing the video playback. |
| */ |
| private val mMovieListener = object : MovieView.MovieListener() { |
| |
| override fun 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, labelPause, |
| CONTROL_TYPE_PAUSE, REQUEST_PAUSE) |
| } |
| |
| override fun 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, labelPlay, |
| CONTROL_TYPE_PLAY, REQUEST_PLAY) |
| } |
| |
| override fun 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 [.CONTROL_TYPE_PLAY] or |
| * * [.CONTROL_TYPE_PAUSE]. |
| * * |
| * @param requestCode The request code for the [PendingIntent]. |
| */ |
| internal fun updatePictureInPictureActions(@DrawableRes iconId: Int, title: String, |
| controlType: Int, requestCode: Int) { |
| val actions = ArrayList<RemoteAction>() |
| |
| // 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. |
| val intent = PendingIntent.getBroadcast(this@MainActivity, |
| requestCode, Intent(ACTION_MEDIA_CONTROL) |
| .putExtra(EXTRA_CONTROL_TYPE, controlType), 0) |
| val icon = Icon.createWithResource(this@MainActivity, iconId) |
| actions.add(RemoteAction(icon, title, title, intent)) |
| |
| // Another action item. This is a fixed action. |
| actions.add(RemoteAction( |
| Icon.createWithResource(this@MainActivity, R.drawable.ic_info_24dp), |
| getString(R.string.info), getString(R.string.info_description), |
| PendingIntent.getActivity(this@MainActivity, REQUEST_INFO, |
| Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.info_uri))), |
| 0))) |
| |
| mPictureInPictureArgs.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. |
| setPictureInPictureArgs(mPictureInPictureArgs) |
| } |
| |
| override fun onCreate(savedInstanceState: Bundle?) { |
| super.onCreate(savedInstanceState) |
| setContentView(R.layout.activity_main) |
| |
| // View references |
| mMovieView = findViewById(R.id.movie) as MovieView |
| mScrollView = findViewById(R.id.scroll) as ScrollView |
| |
| // Set up the video; it automatically starts. |
| mMovieView?.setMovieListener(mMovieListener) |
| findViewById(R.id.pip).setOnClickListener(mOnClickListener) |
| } |
| |
| override fun 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 fun onRestart() { |
| super.onRestart() |
| // Show the video controls so the video can be easily resumed. |
| mMovieView?.showControls() |
| } |
| |
| override fun onConfigurationChanged(newConfig: Configuration) { |
| super.onConfigurationChanged(newConfig) |
| adjustFullScreen(newConfig) |
| } |
| |
| override fun onWindowFocusChanged(hasFocus: Boolean) { |
| super.onWindowFocusChanged(hasFocus) |
| if (hasFocus) { |
| adjustFullScreen(resources.configuration) |
| } |
| } |
| |
| override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, |
| newConfig: Configuration) { |
| super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig) |
| if (isInPictureInPictureMode) { |
| // Starts receiving events from action items in PiP mode. |
| mReceiver = object : BroadcastReceiver() { |
| override fun onReceive(context: Context, intent: Intent?) { |
| if (intent == null || ACTION_MEDIA_CONTROL != intent.action) { |
| return |
| } |
| |
| // This is where we are called back from Picture-in-Picture action items. |
| val controlType = intent.getIntExtra(EXTRA_CONTROL_TYPE, 0) |
| when (controlType) { |
| CONTROL_TYPE_PLAY -> mMovieView?.play() |
| CONTROL_TYPE_PAUSE -> mMovieView?.pause() |
| } |
| } |
| } |
| registerReceiver(mReceiver, 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. |
| */ |
| internal fun minimize() { |
| if (mMovieView == null) { |
| return |
| } |
| // Hide the controls in picture-in-picture mode. |
| mMovieView!!.hideControls() |
| // Calculate the aspect ratio of the PiP screen. |
| val aspectRatio = mMovieView!!.width.toFloat() / mMovieView!!.height |
| mPictureInPictureArgs.setAspectRatio(aspectRatio) |
| enterPictureInPictureMode(mPictureInPictureArgs) |
| } |
| |
| /** |
| * Adjusts immersive full-screen flags depending on the screen orientation. |
| |
| * @param config The current [Configuration]. |
| */ |
| private fun adjustFullScreen(config: Configuration) { |
| val decorView = window.decorView |
| if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) { |
| decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or |
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or |
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or |
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or |
| View.SYSTEM_UI_FLAG_FULLSCREEN or |
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
| mScrollView?.visibility = View.GONE |
| mMovieView?.setAdjustViewBounds(false) |
| } else { |
| decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
| mScrollView?.visibility = View.VISIBLE |
| mMovieView?.setAdjustViewBounds(true) |
| } |
| } |
| |
| companion object { |
| |
| /** Intent action for media controls from Picture-in-Picture mode. */ |
| private val ACTION_MEDIA_CONTROL = "media_control" |
| |
| /** Intent extra for media controls from Picture-in-Picture mode. */ |
| private val EXTRA_CONTROL_TYPE = "control_type" |
| |
| /** The request code for play action PendingIntent. */ |
| private val REQUEST_PLAY = 1 |
| |
| /** The request code for pause action PendingIntent. */ |
| private val REQUEST_PAUSE = 2 |
| |
| /** The request code for info action PendingIntent. */ |
| private val REQUEST_INFO = 3 |
| |
| /** The intent extra value for play action. */ |
| private val CONTROL_TYPE_PLAY = 1 |
| |
| /** The intent extra value for pause action. */ |
| private val CONTROL_TYPE_PAUSE = 2 |
| } |
| |
| } |