Surface Duo Screen Manager

Important

This article describes functionality and guidance that is in public preview and may be substantially modified before it's generally available. Microsoft makes no warranties, express or implied, with respect to the information provided here.

SurfaceDuoScreenManager is the common protocol for two different screen manager implementations, one using the Microsoft DisplayMask library and the other one using the Google WindowManager library. This interface defines methods that you should use to register and unregister screen info listeners.

In order to create the screen manager, you should use ScreenManagerProvider.getScreenManager(), which will return the singleton instance of SurfaceDuoScreenManager implementation. Before calling this method, you should call ScreenManagerProvider.init(Application) inside your Application.onCreate() to initialize the singleton instance of SurfaceDuoScreenManager. This call is required; otherwise, an IllegalStateException("SurfaceDuoScreenManager must be initialized inside Application#onCreate()") exception will be thrown. After you have the instance of SurfaceDuoScreenManager, you can register the ScreenInfoListener callback using SurfaceDuoScreenManager.addScreenInfoListener(ScreenInfoListener?) in order to be notified when the screen mode was changed. Keep in mind that you can also add multiple listeners, so you should unregister them using SurfaceDuoScreenManager.removeScreenInfoListener(ScreenInfoListener?) in order to avoid memory leaks.

Also, If you decided to handle the configuration changes, then you should call SurfaceDuoScreenManager.onConfigurationChanged(Configuration) from your Activity; otherwise, the callback will not be triggered.

class SampleApp : Application() {
    override fun onCreate() {
        super.onCreate()
        ScreenManagerProvider.init(this)
    }
}
class SampleActivity : AppCompatActivity(), ScreenInfoListener {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sample)
    }

    override fun onScreenInfoChanged(screenInfo: ScreenInfo) {
        if (screenInfo.isDualMode()) {
            // TODO: Add dual-screen behavior.
        } else {
            // TODO: Add single-screen behavior.
        }
    }

    override fun onResume() {
        super.onResume()
        ScreenManagerProvider.getScreenManager().addScreenInfoListener(this)
    }

    override fun onPause() {
        super.onPause()
        ScreenManagerProvider.getScreenManager().removeScreenInfoListener(this)
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        ScreenManagerProvider.getScreenManager().onConfigurationChanged()
    }
}

Another option that you have to retrieve the ScreenInfo object without the need to register a callback is to use the ScreenInfoProvider.getScreenInfo(Context) method. Keep in mind that you should call this method after the view is attached to the window; otherwise, some methods from ScreenInfo will return null or will throw exceptions.

class SampleActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sample)

        window?.decorView?.rootView?.let { rootView ->
            if (ViewCompat.isAttachedToWindow(rootView)) {
                val screenInfo = ScreenInfoProvider.getScreenInfo(this)
            } else {
                rootView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
                    override fun onViewAttachedToWindow(view: View) {
                        rootView.removeOnAttachStateChangeListener(this)
                        val screenInfo = ScreenInfoProvider.getScreenInfo(this)
                    }

                    override fun onViewDetachedFromWindow(view: View) {}
                })
            }
        }
    }
}

If you are using the Core Ktx library, then:

class SampleActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sample)

        window?.decorView?.rootView?.doOnAttach {
            val screenInfo = ScreenInfoProvider.getScreenInfo(this)
        }
    }
}