screen saver service fixes
This commit is contained in:
parent
d1a36d2d49
commit
59a8dbe311
2 changed files with 67 additions and 126 deletions
|
@ -1,8 +1,8 @@
|
||||||
package deckers.thibault.aves
|
package deckers.thibault.aves
|
||||||
|
|
||||||
import android.service.dreams.DreamService
|
import android.service.dreams.DreamService
|
||||||
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewTreeObserver
|
|
||||||
import app.loup.streams_channel.StreamsChannel
|
import app.loup.streams_channel.StreamsChannel
|
||||||
import deckers.thibault.aves.channel.calls.AccessibilityHandler
|
import deckers.thibault.aves.channel.calls.AccessibilityHandler
|
||||||
import deckers.thibault.aves.channel.calls.DeviceHandler
|
import deckers.thibault.aves.channel.calls.DeviceHandler
|
||||||
|
@ -14,40 +14,89 @@ import deckers.thibault.aves.channel.streams.ImageByteStreamHandler
|
||||||
import deckers.thibault.aves.channel.streams.MediaStoreStreamHandler
|
import deckers.thibault.aves.channel.streams.MediaStoreStreamHandler
|
||||||
import deckers.thibault.aves.utils.LogUtils
|
import deckers.thibault.aves.utils.LogUtils
|
||||||
import io.flutter.FlutterInjector
|
import io.flutter.FlutterInjector
|
||||||
import io.flutter.Log
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
import io.flutter.embedding.android.FlutterSurfaceView
|
import io.flutter.embedding.android.FlutterSurfaceView
|
||||||
import io.flutter.embedding.android.FlutterView
|
import io.flutter.embedding.android.FlutterView
|
||||||
import io.flutter.embedding.engine.FlutterEngine
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
import io.flutter.embedding.engine.dart.DartExecutor.DartEntrypoint
|
import io.flutter.embedding.engine.dart.DartExecutor.DartEntrypoint
|
||||||
import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister
|
import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister
|
||||||
import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener
|
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
|
||||||
// for FlutterView-level integration, cf https://docs.flutter.dev/development/add-to-app/android/add-flutter-view
|
// for FlutterView-level integration, cf https://docs.flutter.dev/development/add-to-app/android/add-flutter-view
|
||||||
class ScreenSaverService : DreamService() {
|
class ScreenSaverService : DreamService() {
|
||||||
private var flutterEngine: FlutterEngine? = null
|
private var flutterEngine: FlutterEngine? = null
|
||||||
private var flutterView: FlutterView? = null
|
private var flutterView: FlutterView? = null
|
||||||
private var activePreDrawListener: ViewTreeObserver.OnPreDrawListener? = null
|
|
||||||
private var isFlutterUiDisplayed = false
|
|
||||||
private var isFirstFrameRendered = false
|
|
||||||
private var isAttached = false
|
|
||||||
|
|
||||||
private val flutterUiDisplayListener: FlutterUiDisplayListener = object : FlutterUiDisplayListener {
|
override fun onAttachedToWindow() {
|
||||||
override fun onFlutterUiDisplayed() {
|
Log.i(LOG_TAG, "onAttachedToWindow")
|
||||||
isFlutterUiDisplayed = true
|
super.onAttachedToWindow()
|
||||||
isFirstFrameRendered = true
|
initDream()
|
||||||
|
createEngine()
|
||||||
|
setContentView(createView())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDreamingStarted() {
|
||||||
|
Log.i(LOG_TAG, "onDreamingStarted")
|
||||||
|
super.onDreamingStarted()
|
||||||
|
onStart()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDreamingStopped() {
|
||||||
|
Log.i(LOG_TAG, "onDreamingStopped")
|
||||||
|
release()
|
||||||
|
super.onDreamingStopped()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromWindow() {
|
||||||
|
Log.i(LOG_TAG, "onDetachedFromWindow")
|
||||||
|
destroyView()
|
||||||
|
super.onDetachedFromWindow()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initDream() {
|
||||||
|
isInteractive = false
|
||||||
|
isFullscreen = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createEngine() {
|
||||||
|
flutterEngine = flutterEngine ?: FlutterEngine(this, null, false)
|
||||||
|
GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine!!)
|
||||||
|
initChannels()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createView(): View {
|
||||||
|
flutterView = FlutterView(this, FlutterSurfaceView(this)).apply {
|
||||||
|
id = FlutterActivity.FLUTTER_VIEW_ID
|
||||||
|
attachToFlutterEngine(flutterEngine!!)
|
||||||
}
|
}
|
||||||
|
return flutterView!!
|
||||||
|
}
|
||||||
|
|
||||||
override fun onFlutterUiNoLongerDisplayed() {
|
private fun destroyView() {
|
||||||
isFlutterUiDisplayed = false
|
flutterEngine?.lifecycleChannel?.appIsDetached()
|
||||||
|
flutterView?.detachFromFlutterEngine()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun release() {
|
||||||
|
destroyView()
|
||||||
|
flutterEngine = null
|
||||||
|
flutterView = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onStart() {
|
||||||
|
flutterEngine!!.apply {
|
||||||
|
if (!dartExecutor.isExecutingDart) {
|
||||||
|
navigationChannel.setInitialRoute(DEFAULT_INITIAL_ROUTE)
|
||||||
|
val appBundlePathOverride = FlutterInjector.instance().flutterLoader().findAppBundlePath()
|
||||||
|
val entrypoint = DartEntrypoint(appBundlePathOverride, DEFAULT_DART_ENTRYPOINT)
|
||||||
|
dartExecutor.executeDartEntrypoint(entrypoint)
|
||||||
|
}
|
||||||
|
lifecycleChannel.appIsResumed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAttachedToWindow() {
|
|
||||||
super.onAttachedToWindow()
|
|
||||||
onAttach()
|
|
||||||
|
|
||||||
|
private fun initChannels() {
|
||||||
val messenger = flutterEngine!!.dartExecutor.binaryMessenger
|
val messenger = flutterEngine!!.dartExecutor.binaryMessenger
|
||||||
|
|
||||||
// dart -> platform -> dart
|
// dart -> platform -> dart
|
||||||
|
@ -71,123 +120,14 @@ class ScreenSaverService : DreamService() {
|
||||||
when (call.method) {
|
when (call.method) {
|
||||||
"getIntentData" -> {
|
"getIntentData" -> {
|
||||||
result.success(intentDataMap)
|
result.success(intentDataMap)
|
||||||
intentDataMap.clear()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// dream setup
|
|
||||||
isInteractive = false
|
|
||||||
isFullscreen = true
|
|
||||||
setContentView(createFlutterView())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDreamingStarted() {
|
|
||||||
super.onDreamingStarted()
|
|
||||||
onStart()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDreamingStopped() {
|
|
||||||
onDestroyView()
|
|
||||||
super.onDreamingStopped()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDetachedFromWindow() {
|
|
||||||
release()
|
|
||||||
super.onDetachedFromWindow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// from `FlutterActivityAndFragmentDelegate`
|
|
||||||
|
|
||||||
private fun createFlutterView(): View {
|
|
||||||
Log.d(LOG_TAG, "Creating FlutterView.")
|
|
||||||
val flutterSurfaceView = FlutterSurfaceView(this)
|
|
||||||
val pFlutterView = FlutterView(this, flutterSurfaceView)
|
|
||||||
flutterView = pFlutterView
|
|
||||||
|
|
||||||
// Add listener to be notified when Flutter renders its first frame.
|
|
||||||
pFlutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener)
|
|
||||||
Log.d(LOG_TAG, "Attaching FlutterEngine to FlutterView.")
|
|
||||||
pFlutterView.attachToFlutterEngine(flutterEngine!!)
|
|
||||||
pFlutterView.id = FlutterActivity.FLUTTER_VIEW_ID
|
|
||||||
delayFirstAndroidViewDraw(pFlutterView)
|
|
||||||
return pFlutterView
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun release() {
|
|
||||||
flutterEngine = null
|
|
||||||
flutterView = null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onAttach() {
|
|
||||||
if (flutterEngine == null) {
|
|
||||||
Log.d(LOG_TAG, "Setting up FlutterEngine.")
|
|
||||||
flutterEngine = FlutterEngine(
|
|
||||||
this,
|
|
||||||
null,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine!!)
|
|
||||||
isAttached = true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onStart() {
|
|
||||||
Log.d(LOG_TAG, "onStart()")
|
|
||||||
doInitialFlutterViewRun()
|
|
||||||
flutterView!!.visibility = View.VISIBLE
|
|
||||||
flutterEngine!!.lifecycleChannel.appIsResumed()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onDestroyView() {
|
|
||||||
Log.v(LOG_TAG, "onDestroyView()")
|
|
||||||
flutterView ?: return
|
|
||||||
|
|
||||||
val pFlutterView = flutterView!!
|
|
||||||
if (activePreDrawListener != null) {
|
|
||||||
pFlutterView.viewTreeObserver.removeOnPreDrawListener(activePreDrawListener)
|
|
||||||
activePreDrawListener = null
|
|
||||||
}
|
|
||||||
pFlutterView.detachFromFlutterEngine()
|
|
||||||
pFlutterView.removeOnFirstFrameRenderedListener(flutterUiDisplayListener)
|
|
||||||
|
|
||||||
flutterEngine!!.lifecycleChannel.appIsInactive()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun delayFirstAndroidViewDraw(flutterView: FlutterView) {
|
|
||||||
if (activePreDrawListener != null) {
|
|
||||||
flutterView.viewTreeObserver.removeOnPreDrawListener(activePreDrawListener)
|
|
||||||
}
|
|
||||||
activePreDrawListener = object : ViewTreeObserver.OnPreDrawListener {
|
|
||||||
override fun onPreDraw(): Boolean {
|
|
||||||
if (isFlutterUiDisplayed && activePreDrawListener != null) {
|
|
||||||
flutterView.viewTreeObserver.removeOnPreDrawListener(this)
|
|
||||||
activePreDrawListener = null
|
|
||||||
}
|
|
||||||
return isFlutterUiDisplayed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
flutterView.viewTreeObserver.addOnPreDrawListener(activePreDrawListener)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun doInitialFlutterViewRun() {
|
|
||||||
val pFlutterEngine = flutterEngine!!
|
|
||||||
if (pFlutterEngine.dartExecutor.isExecutingDart) {
|
|
||||||
// No warning is logged because this situation will happen on every config
|
|
||||||
// change if the developer does not choose to retain the Fragment instance.
|
|
||||||
// So this is expected behavior in many cases.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pFlutterEngine.navigationChannel.setInitialRoute(DEFAULT_INITIAL_ROUTE)
|
|
||||||
val appBundlePathOverride = FlutterInjector.instance().flutterLoader().findAppBundlePath()
|
|
||||||
val entrypoint = DartEntrypoint(appBundlePathOverride, DEFAULT_DART_ENTRYPOINT)
|
|
||||||
pFlutterEngine.dartExecutor.executeDartEntrypoint(entrypoint)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val LOG_TAG = LogUtils.createTag<ScreenSaverService>()
|
private val LOG_TAG = LogUtils.createTag<ScreenSaverService>()
|
||||||
private val intentDataMap: MutableMap<String, Any?> = hashMapOf(
|
private val intentDataMap: Map<String, Any?> = hashMapOf(
|
||||||
MainActivity.INTENT_DATA_KEY_ACTION to MainActivity.INTENT_ACTION_SCREEN_SAVER,
|
MainActivity.INTENT_DATA_KEY_ACTION to MainActivity.INTENT_ACTION_SCREEN_SAVER,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -177,6 +177,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
||||||
case AppLifecycleState.inactive:
|
case AppLifecycleState.inactive:
|
||||||
case AppLifecycleState.paused:
|
case AppLifecycleState.paused:
|
||||||
case AppLifecycleState.detached:
|
case AppLifecycleState.detached:
|
||||||
|
viewerController.autopilot = false;
|
||||||
pauseVideoControllers();
|
pauseVideoControllers();
|
||||||
break;
|
break;
|
||||||
case AppLifecycleState.resumed:
|
case AppLifecycleState.resumed:
|
||||||
|
|
Loading…
Reference in a new issue