From 675a94fd7685663fe694def51f89b84a0833e0eb Mon Sep 17 00:00:00 2001 From: Yuichi Araki Date: Fri, 19 May 2017 11:51:48 -0700 Subject: Tidy up some Kotlin implementation Test: Existing tests pass Change-Id: Id24d314798be483ea4682358882539bf9da72e7f --- .../android/pictureinpicture/MainActivity.kt | 121 +++++++-------- .../android/pictureinpicture/widget/MovieView.kt | 170 +++++++++------------ 2 files changed, 132 insertions(+), 159 deletions(-) (limited to 'media') diff --git a/media/PictureInPicture/kotlinApp/app/src/main/java/com/example/android/pictureinpicture/MainActivity.kt b/media/PictureInPicture/kotlinApp/app/src/main/java/com/example/android/pictureinpicture/MainActivity.kt index a09e5f53..c1fc2ba2 100644 --- a/media/PictureInPicture/kotlinApp/app/src/main/java/com/example/android/pictureinpicture/MainActivity.kt +++ b/media/PictureInPicture/kotlinApp/app/src/main/java/com/example/android/pictureinpicture/MainActivity.kt @@ -40,27 +40,61 @@ import java.util.* */ class MainActivity : AppCompatActivity() { + 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 + + } + /** The arguments to be used for Picture-in-Picture mode. */ private val mPictureInPictureArgs = PictureInPictureArgs() /** This shows the video. */ - private var mMovieView: MovieView? = null + private lateinit var mMovieView: MovieView /** The bottom half of the screen; hidden on landscape */ - private var mScrollView: ScrollView? = null + private lateinit var mScrollView: ScrollView /** 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 mReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent?) { + intent?.let { intent -> + if (intent.action != ACTION_MEDIA_CONTROL) { + return + } - private val mOnClickListener = View.OnClickListener { view -> - when (view.id) { - R.id.pip -> minimize() + // 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() + } + } } } + private val labelPlay: String by lazy { getString(R.string.play) } + private val labelPause: String by lazy { getString(R.string.pause) } + /** * Callbacks from the [MovieView] showing the video playback. */ @@ -137,22 +171,22 @@ class MainActivity : AppCompatActivity() { mScrollView = findViewById(R.id.scroll) as ScrollView // Set up the video; it automatically starts. - mMovieView?.setMovieListener(mMovieListener) - findViewById(R.id.pip).setOnClickListener(mOnClickListener) + mMovieView.setMovieListener(mMovieListener) + findViewById(R.id.pip).setOnClickListener { minimize() } } 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() + mMovieView.pause() super.onStop() } override fun onRestart() { super.onRestart() // Show the video controls so the video can be easily resumed. - if (!isInPictureInPictureMode()) { - mMovieView?.showControls() + if (!isInPictureInPictureMode) { + mMovieView.showControls() } } @@ -173,28 +207,13 @@ class MainActivity : AppCompatActivity() { 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() + if (!mMovieView.isPlaying) { + mMovieView.showControls() } } } @@ -203,14 +222,10 @@ class MainActivity : AppCompatActivity() { * Enters Picture-in-Picture mode. */ internal fun minimize() { - if (mMovieView == null) { - return - } // Hide the controls in picture-in-picture mode. - mMovieView!!.hideControls() + mMovieView.hideControls() // Calculate the aspect ratio of the PiP screen. - val aspectRatio = mMovieView!!.width.toFloat() / mMovieView!!.height - mPictureInPictureArgs.setAspectRatio(aspectRatio) + mPictureInPictureArgs.setAspectRatio(mMovieView.width.toFloat() / mMovieView.height) enterPictureInPictureMode(mPictureInPictureArgs) } @@ -228,37 +243,13 @@ class MainActivity : AppCompatActivity() { 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) + mScrollView.visibility = View.GONE + mMovieView.setAdjustViewBounds(false) } else { decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE - mScrollView?.visibility = View.VISIBLE - mMovieView?.setAdjustViewBounds(true) + 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 - } - } diff --git a/media/PictureInPicture/kotlinApp/app/src/main/java/com/example/android/pictureinpicture/widget/MovieView.kt b/media/PictureInPicture/kotlinApp/app/src/main/java/com/example/android/pictureinpicture/widget/MovieView.kt index e6081c72..04f10aae 100644 --- a/media/PictureInPicture/kotlinApp/app/src/main/java/com/example/android/pictureinpicture/widget/MovieView.kt +++ b/media/PictureInPicture/kotlinApp/app/src/main/java/com/example/android/pictureinpicture/widget/MovieView.kt @@ -47,6 +47,18 @@ class MovieView @JvmOverloads constructor(context: Context, attrs: AttributeSet? defStyleAttr: Int = 0) : RelativeLayout(context, attrs, defStyleAttr) { + companion object { + + private val TAG = "MovieView" + + /** The amount of time we are stepping forward or backward for fast-forward and fast-rewind. */ + private val FAST_FORWARD_REWIND_INTERVAL = 5000 // ms + + /** The amount of time until we fade out the controls. */ + private val TIMEOUT_CONTROLS = 3000L // ms + + } + /** * Monitors all events related to [MovieView]. */ @@ -101,12 +113,12 @@ class MovieView @JvmOverloads constructor(context: Context, attrs: AttributeSet? // Inflate the content View.inflate(context, R.layout.view_movie, this) - mSurfaceView = findViewById(R.id.surface) as SurfaceView + mSurfaceView = findViewById(R.id.surface) mShade = findViewById(R.id.shade) - mToggle = findViewById(R.id.toggle) as ImageButton - mFastForward = findViewById(R.id.fast_forward) as ImageButton - mFastRewind = findViewById(R.id.fast_rewind) as ImageButton - mMinimize = findViewById(R.id.minimize) as ImageButton + mToggle = findViewById(R.id.toggle) + mFastForward = findViewById(R.id.fast_forward) + mFastRewind = findViewById(R.id.fast_rewind) + mMinimize = findViewById(R.id.minimize) // Attributes val a = context.obtainStyledAttributes(attrs, R.styleable.MovieView, @@ -125,14 +137,16 @@ class MovieView @JvmOverloads constructor(context: Context, attrs: AttributeSet? R.id.minimize -> mMovieListener?.onMovieMinimized() } // Start or reset the timeout to hide controls - if (mMediaPlayer != null) { + mMediaPlayer?.let { player -> if (mTimeoutHandler == null) { mTimeoutHandler = TimeoutHandler(this@MovieView) } - mTimeoutHandler!!.removeMessages(TimeoutHandler.MESSAGE_HIDE_CONTROLS) - if (mMediaPlayer!!.isPlaying) { - mTimeoutHandler!!.sendEmptyMessageDelayed( - TimeoutHandler.MESSAGE_HIDE_CONTROLS, TIMEOUT_CONTROLS.toLong()) + mTimeoutHandler?.let { handler -> + handler.removeMessages(TimeoutHandler.MESSAGE_HIDE_CONTROLS) + if (player.isPlaying) { + handler.sendEmptyMessageDelayed(TimeoutHandler.MESSAGE_HIDE_CONTROLS, + TIMEOUT_CONTROLS) + } } } } @@ -148,23 +162,22 @@ class MovieView @JvmOverloads constructor(context: Context, attrs: AttributeSet? openVideo(holder.surface) } - override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { + override fun surfaceChanged(holder: SurfaceHolder, format: Int, + width: Int, height: Int) { // Do nothing } override fun surfaceDestroyed(holder: SurfaceHolder) { - if (mMediaPlayer != null) { - mSavedCurrentPosition = mMediaPlayer!!.currentPosition - } + mMediaPlayer?.let { mSavedCurrentPosition = it.currentPosition } closeVideo() } }) } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - if (mMediaPlayer != null) { - val videoWidth = mMediaPlayer!!.videoWidth - val videoHeight = mMediaPlayer!!.videoHeight + mMediaPlayer?.let { player -> + val videoWidth = player.videoWidth + val videoHeight = player.videoHeight if (videoWidth != 0 && videoHeight != 0) { val aspectRatio = videoHeight.toFloat() / videoWidth val width = View.MeasureSpec.getSize(widthMeasureSpec) @@ -172,11 +185,13 @@ class MovieView @JvmOverloads constructor(context: Context, attrs: AttributeSet? val height = View.MeasureSpec.getSize(heightMeasureSpec) val heightMode = View.MeasureSpec.getMode(heightMeasureSpec) if (mAdjustViewBounds) { - if (widthMode == View.MeasureSpec.EXACTLY && heightMode != View.MeasureSpec.EXACTLY) { + if (widthMode == View.MeasureSpec.EXACTLY + && heightMode != View.MeasureSpec.EXACTLY) { super.onMeasure(widthMeasureSpec, View.MeasureSpec.makeMeasureSpec((width * aspectRatio).toInt(), View.MeasureSpec.EXACTLY)) - } else if (widthMode != View.MeasureSpec.EXACTLY && heightMode == View.MeasureSpec.EXACTLY) { + } else if (widthMode != View.MeasureSpec.EXACTLY + && heightMode == View.MeasureSpec.EXACTLY) { super.onMeasure(View.MeasureSpec.makeMeasureSpec((height / aspectRatio).toInt(), View.MeasureSpec.EXACTLY), heightMeasureSpec) } else { @@ -202,10 +217,8 @@ class MovieView @JvmOverloads constructor(context: Context, attrs: AttributeSet? } override fun onDetachedFromWindow() { - if (mTimeoutHandler != null) { - mTimeoutHandler!!.removeMessages(TimeoutHandler.MESSAGE_HIDE_CONTROLS) - mTimeoutHandler = null - } + mTimeoutHandler?.removeMessages(TimeoutHandler.MESSAGE_HIDE_CONTROLS) + mTimeoutHandler = null super.onDetachedFromWindow() } @@ -276,24 +289,18 @@ class MovieView @JvmOverloads constructor(context: Context, attrs: AttributeSet? * Fast-forward the video. */ fun fastForward() { - if (mMediaPlayer == null) { - return - } - mMediaPlayer!!.seekTo(mMediaPlayer!!.currentPosition + FAST_FORWARD_REWIND_INTERVAL) + mMediaPlayer?.let { it.seekTo(it.currentPosition + FAST_FORWARD_REWIND_INTERVAL) } } /** * Fast-rewind the video. */ fun fastRewind() { - if (mMediaPlayer == null) { - return - } - mMediaPlayer!!.seekTo(mMediaPlayer!!.currentPosition - FAST_FORWARD_REWIND_INTERVAL) + mMediaPlayer?.let { it.seekTo(it.currentPosition - FAST_FORWARD_REWIND_INTERVAL) } } val isPlaying: Boolean - get() = mMediaPlayer != null && mMediaPlayer!!.isPlaying + get() = mMediaPlayer?.isPlaying ?: false fun play() { if (mMediaPlayer == null) { @@ -302,9 +309,7 @@ class MovieView @JvmOverloads constructor(context: Context, attrs: AttributeSet? mMediaPlayer!!.start() adjustToggleState() keepScreenOn = true - if (mMovieListener != null) { - mMovieListener!!.onMovieStarted() - } + mMovieListener?.onMovieStarted() } fun pause() { @@ -315,9 +320,7 @@ class MovieView @JvmOverloads constructor(context: Context, attrs: AttributeSet? mMediaPlayer!!.pause() adjustToggleState() keepScreenOn = false - if (mMovieListener != null) { - mMovieListener!!.onMovieStopped() - } + mMovieListener?.onMovieStopped() } internal fun openVideo(surface: Surface) { @@ -325,52 +328,42 @@ class MovieView @JvmOverloads constructor(context: Context, attrs: AttributeSet? return } mMediaPlayer = MediaPlayer() - mMediaPlayer!!.setSurface(surface) - try { - resources.openRawResourceFd(mVideoResourceId).use { fd -> - mMediaPlayer!!.setDataSource(fd) - mMediaPlayer!!.setOnPreparedListener { mediaPlayer -> - // Adjust the aspect ratio of this view - requestLayout() - if (mSavedCurrentPosition > 0) { - mediaPlayer.seekTo(mSavedCurrentPosition) - mSavedCurrentPosition = 0 - } else { - // Start automatically - play() + mMediaPlayer?.let { player -> + player.setSurface(surface) + try { + resources.openRawResourceFd(mVideoResourceId).use { fd -> + player.setDataSource(fd) + player.setOnPreparedListener { mediaPlayer -> + // Adjust the aspect ratio of this view + requestLayout() + if (mSavedCurrentPosition > 0) { + mediaPlayer.seekTo(mSavedCurrentPosition) + mSavedCurrentPosition = 0 + } else { + // Start automatically + play() + } } - } - mMediaPlayer!!.setOnCompletionListener { - adjustToggleState() - keepScreenOn = false - if (mMovieListener != null) { - mMovieListener!!.onMovieStopped() + player.setOnCompletionListener { + adjustToggleState() + keepScreenOn = false + mMovieListener?.onMovieStopped() } + player.prepare() } - mMediaPlayer!!.prepare() + } catch (e: IOException) { + Log.e(TAG, "Failed to open video", e) } - } catch (e: IOException) { - Log.e(TAG, "Failed to open video", e) } - } internal fun closeVideo() { - if (mMediaPlayer != null) { - mMediaPlayer!!.release() - mMediaPlayer = null - } + mMediaPlayer?.release() + mMediaPlayer = null } internal fun toggle() { - if (mMediaPlayer == null) { - return - } - if (mMediaPlayer!!.isPlaying) { - pause() - } else { - play() - } + mMediaPlayer?.let { if (it.isPlaying) pause() else play() } } internal fun toggleControls() { @@ -382,24 +375,25 @@ class MovieView @JvmOverloads constructor(context: Context, attrs: AttributeSet? } internal fun adjustToggleState() { - if (mMediaPlayer != null && mMediaPlayer!!.isPlaying) { - mToggle.contentDescription = resources.getString(R.string.pause) - mToggle.setImageResource(R.drawable.ic_pause_64dp) - } else { - mToggle.contentDescription = resources.getString(R.string.play) - mToggle.setImageResource(R.drawable.ic_play_arrow_64dp) + mMediaPlayer?.let { + if (it.isPlaying) { + mToggle.contentDescription = resources.getString(R.string.pause) + mToggle.setImageResource(R.drawable.ic_pause_64dp) + } else { + mToggle.contentDescription = resources.getString(R.string.play) + mToggle.setImageResource(R.drawable.ic_play_arrow_64dp) + } } } - private class TimeoutHandler internal constructor(view: MovieView) : Handler() { + private class TimeoutHandler(view: MovieView) : Handler() { private val mMovieViewRef: WeakReference = WeakReference(view) override fun handleMessage(msg: Message) { when (msg.what) { MESSAGE_HIDE_CONTROLS -> { - val movieView = mMovieViewRef.get() - movieView?.hideControls() + mMovieViewRef.get()?.hideControls() } else -> super.handleMessage(msg) } @@ -411,16 +405,4 @@ class MovieView @JvmOverloads constructor(context: Context, attrs: AttributeSet? } - companion object { - - private val TAG = "MovieView" - - /** The amount of time we are stepping forward or backward for fast-forward and fast-rewind. */ - private val FAST_FORWARD_REWIND_INTERVAL = 5000 // ms - - /** The amount of time until we fade out the controls. */ - private val TIMEOUT_CONTROLS = 3000 // ms - - } - } -- cgit v1.2.3