DisplayManager.kt 6.88 KB
Newer Older
Geoffrey Métais's avatar
Geoffrey Métais committed
1
2
3
4
5
package org.videolan.vlc.gui.video

import android.annotation.TargetApi
import android.app.Activity
import android.app.Presentation
6
import android.arch.lifecycle.Observer
Geoffrey Métais's avatar
Geoffrey Métais committed
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import android.content.Context
import android.content.DialogInterface
import android.graphics.PixelFormat
import android.media.MediaRouter
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.Display
import android.view.SurfaceView
import android.view.WindowManager
import android.widget.FrameLayout
import org.videolan.libvlc.RendererItem
import org.videolan.libvlc.util.AndroidUtil
import org.videolan.vlc.BuildConfig
import org.videolan.vlc.R
import org.videolan.vlc.RendererDelegate
23
import org.videolan.vlc.util.AndroidDevices
Geoffrey Métais's avatar
Geoffrey Métais committed
24
25

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
26
class DisplayManager(private val activity: Activity, cloneMode: Boolean) {
Geoffrey Métais's avatar
Geoffrey Métais committed
27
28
29
30
31
32
33
34
35
36

    enum class DisplayType { PRIMARY, PRESENTATION, RENDERER }

    val displayType: DisplayType
    // Presentation
    private val mediaRouter: MediaRouter? by lazy { if (AndroidUtil.isJellyBeanMR1OrLater) activity.applicationContext.getSystemService(Context.MEDIA_ROUTER_SERVICE) as MediaRouter else null }
    private var mediaRouterCallback: MediaRouter.SimpleCallback? = null
    var presentation: SecondaryDisplay? = null
    private var presentationDisplayId = -1
    ///Renderers
37
    private var rendererItem: RendererItem? = RendererDelegate.selectedRenderer.value
Geoffrey Métais's avatar
Geoffrey Métais committed
38
39
40

    val isPrimary: Boolean
        get() = displayType == DisplayType.PRIMARY
41
42
    val isOnRenderer: Boolean
        get() = displayType == DisplayType.RENDERER
Geoffrey Métais's avatar
Geoffrey Métais committed
43
44
45
46
47
48
49
50
51
52
53

    /**
     * Listens for when presentations are dismissed.
     */
    private val mOnDismissListener = DialogInterface.OnDismissListener { dialog ->
        if (dialog == presentation) {
            if (BuildConfig.DEBUG) Log.i(TAG, "Presentation was dismissed.")
            presentation = null
            presentationDisplayId = -1
        }
    }
54
55
56
57
    private val rendererObs by lazy(LazyThreadSafetyMode.NONE) { Observer<RendererItem> {
        rendererItem = it
        updateDisplayType()
    }}
Geoffrey Métais's avatar
Geoffrey Métais committed
58
59

    init {
60
        presentation = if (AndroidUtil.isJellyBeanMR1OrLater) createPresentation(cloneMode) else null
Geoffrey Métais's avatar
Geoffrey Métais committed
61
        displayType = getCurrentType()
62
        if (!AndroidDevices.isChromeBook) RendererDelegate.selectedRenderer.observeForever(rendererObs)
Geoffrey Métais's avatar
Geoffrey Métais committed
63
64
65
    }

    companion object {
66
        private const val TAG = "VLC/DisplayManager"
Geoffrey Métais's avatar
Geoffrey Métais committed
67
68
69
70
71
72
73
    }

    fun release() {
        if (displayType == DisplayType.PRESENTATION) {
            presentation?.dismiss()
            presentation = null
        }
74
        if (!AndroidDevices.isChromeBook) RendererDelegate.selectedRenderer.removeObserver(rendererObs)
Geoffrey Métais's avatar
Geoffrey Métais committed
75
76
77
    }

    private fun updateDisplayType() {
78
        if (getCurrentType() != displayType && activity is VideoPlayerActivity) activity.recreate()
Geoffrey Métais's avatar
Geoffrey Métais committed
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
    }

    private fun getCurrentType() = when {
        presentationDisplayId != -1 -> DisplayType.PRESENTATION
        rendererItem !== null -> DisplayType.RENDERER
        else -> DisplayType.PRIMARY
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    private fun createPresentation(cloneMode: Boolean): SecondaryDisplay? {
        if (mediaRouter === null || cloneMode) return null

        // Get the current route and its presentation display.
        val route = mediaRouter?.getSelectedRoute(MediaRouter.ROUTE_TYPE_LIVE_VIDEO)
        val presentationDisplay = route?.presentationDisplay
        if (presentationDisplay !== null) {
            // Show a new presentation if possible.
96
            if (BuildConfig.DEBUG) Log.i(TAG, "Showing presentation on display: $presentationDisplay")
Geoffrey Métais's avatar
Geoffrey Métais committed
97
98
99
100
101
102
103
104
            val presentation = SecondaryDisplay(activity, presentationDisplay)
            presentation.setOnDismissListener(mOnDismissListener)
            try {
                presentation.show()
                presentationDisplayId = presentationDisplay.displayId
                return presentation
            } catch (ex: WindowManager.InvalidDisplayException) {
                if (BuildConfig.DEBUG) Log.w(TAG, "Couldn't show presentation!  Display was removed in " + "the meantime.", ex)
105
                presentationDisplayId = -1
Geoffrey Métais's avatar
Geoffrey Métais committed
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
            }
        } else if (BuildConfig.DEBUG) Log.i(TAG, "No secondary display detected")
        return null
    }

    /**
     * Add or remove MediaRouter callbacks. This is provided for version targeting.
     *
     * @param add true to add, false to remove
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    fun mediaRouterAddCallback(add: Boolean) {
        if (!AndroidUtil.isJellyBeanMR1OrLater || mediaRouter === null
                || add == (mediaRouterCallback !== null))
            return
        if (add) {
            mediaRouterCallback = object : MediaRouter.SimpleCallback() {
                override fun onRoutePresentationDisplayChanged(
                        router: MediaRouter, info: MediaRouter.RouteInfo) {
125
                    if (BuildConfig.DEBUG) Log.d(TAG, "onRoutePresentationDisplayChanged: info=$info")
Geoffrey Métais's avatar
Geoffrey Métais committed
126
127
128
                    val newDisplayId = info.presentationDisplay?.displayId ?: -1
                    if (newDisplayId == presentationDisplayId) return
                    presentationDisplayId = newDisplayId
129
                    if (newDisplayId == -1) removePresentation() else updateDisplayType()
Geoffrey Métais's avatar
Geoffrey Métais committed
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
                }
            }
            mediaRouter?.addCallback(MediaRouter.ROUTE_TYPE_LIVE_VIDEO, mediaRouterCallback)
        } else {
            mediaRouter?.removeCallback(mediaRouterCallback)
            mediaRouterCallback = null
        }
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    private fun removePresentation() {
        if (mediaRouter === null) return

        // Dismiss the current presentation if the display has changed.
        if (BuildConfig.DEBUG) Log.i(TAG, "Dismissing presentation because the current route no longer " + "has a presentation display.")
        presentation?.dismiss()
        presentation = null
        updateDisplayType()
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    class SecondaryDisplay(context: Context, display: Display) : Presentation(context, display) {

        lateinit var surfaceView: SurfaceView
        lateinit var subtitlesSurfaceView: SurfaceView
        lateinit var surfaceFrame: FrameLayout

        companion object {
            val TAG = "VLC/SecondaryDisplay"
        }

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.player_remote)
            surfaceView = findViewById(R.id.remote_player_surface)
            subtitlesSurfaceView = findViewById(R.id.remote_subtitles_surface)
            surfaceFrame = findViewById(R.id.remote_player_surface_frame)
            subtitlesSurfaceView.setZOrderMediaOverlay(true)
            subtitlesSurfaceView.holder.setFormat(PixelFormat.TRANSLUCENT)
            if (BuildConfig.DEBUG) Log.i(TAG, "Secondary display created")
        }
    }
}