Jetpack Window Manager for Xamarin
Tip
Xamarin.Forms users should reference the Xamarin.Forms.DualScreen NuGet for Surface Duo support, with its DualScreenInfo
and TwoPaneView
classes.
Jetpack Window Manager is intended for developers working with Xamarin.Android projects.
Jetpack Window Manager provides a standard API for working with all foldable devices. It contains two important classes:
- DisplayFeature - Identifies disruptions in the continuous flat screen surface such as hinges or folds. Window Manager will return a collection of display features from a layout change callback.
- FoldingFeature - Provides information about a specific feature of the device - while the Surface Duo only has one folding feature, it's possible that other devices might have more. The
FoldingFeature
class provides information about the state of that part of the device, with properties forBounds
andIsSeparating
, and methods forOcclusionType
,Orientation
, andState
.
Samples using Jetpack Window Manager are available in the surface-duo-sdk-xamarin-samples repo.
Note
The Xamarin.AndroidX.Window.WindowJava NuGet is intended to replace the need to add the Xamarin.DuoSDK NuGet to Xamarin.Android apps.
Instead of using the ScreenHelper
class to determine IsDualMode
or to GetHingeBoundsDip()
, you can use the methods and properties on WindowInfoTracker
and related classes directly.
To use WindowInfoTracker
in your code, follow the instructions below (from the Xamarin.Android Window Manager sample app):
Add dependency
To add the NuGet that provides Jetpack Window Manager features:
Right-click on your Xamarin.Android project and choose Manage NuGet Packages...
Search for Xamarin.AndroidX.Window.WindowJava.
Choose the highest version number to add to your project (1.0.0.7 is the first stable version of the API).
Use Jetpack Window Manager in your code
In the MainActivity class, declare a variable for the window information tracker:
public class MainActivity : AppCompatActivity, IConsumer { WindowInfoTrackerCallbackAdapter wit;
Ensure that the correct
using AndroidX.Window.Layout;
andusing AndroidX.Window.Java.Layout;
statements are added to the top of the file.Note
The activity also implements
IConsumer
, see step 4 below for the code for theAccept
method required by this interface.Initialize the window manager in your activity's
OnCreate
:protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); wit = new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.Companion.GetOrCreate(this));
Now create function that returns an
IExecutor
implementation so we provide it to the callback as the first parameter and it will be invoked using it. We are going to create one that runs on the UI thread, you can create a different one that doesn’t run on the UI thread if required.IExecutor runOnUiThreadExecutor() { return new MyExecutor(); } class MyExecutor : Java.Lang.Object, IExecutor { Handler handler = new Handler(Looper.MainLooper); public void Execute(IRunnable r) { handler.Post(r); } }
Define an inner class to handle the callback when the layout needs to change. The activity should have a TextView called
layoutChange
so that this method can update the displayed text:public void Accept(Java.Lang.Object newLayoutInfo) // Object will be WindowLayoutInfo { var newLayoutInfo = (newLayoutInfo as WindowLayoutInfo); // have to cast before use layoutChange.Text = newLayoutInfo.ToString(); configurationChanged.Text = "One logic/physical display - unspanned"; foreach (var displayFeature in newLayoutInfo.DisplayFeatures) { var foldingFeature = displayFeature.JavaCast<IFoldingFeature>(); if (foldingFeature != null) { alignViewToDeviceFeatureBoundaries(newLayoutInfo); if (foldingFeature.GetOcclusionType() == FoldingFeatureOcclusionType.None) { configurationChanged.Text = "App is spanned across a fold"; } if (foldingFeature.GetOcclusionType() == FoldingFeatureOcclusionType.Full) { configurationChanged.Text = "App is spanned across a hinge"; } configurationChanged.Text += "\nIsSeparating: " + foldingFeature.IsSeparating + "\nOrientation: " + foldingFeature.Orientation // FoldingFeatureOrientation.Vertical or Horizontal + "\nState: " + foldingFeature.State; // FoldingFeatureState.Flat or State.HalfOpened } else { Log.Info(TAG, "DisplayFeature is not a fold/hinge"); } } }
Note
The
WindowLayoutInfo
class has a collection ofDisplayFeature
items, one or more of which could be instances ofFoldingFeature
. Folding feature instances have properties forBounds
andIsSeparating
, and methods forOcclusionType
,Orientation
, andState
that you can query to make decisions about how to adjust your layout for the new state.In an
OnStart
override, register theAddWindowLayoutInfoListener
handler and pass the executor and a reference to the activity (because it implementsIConsumer
).protected override void OnStart() { base.OnStart(); wit.AddWindowLayoutInfoListener(this, runOnUiThreadExecutor(), this); // first `this` is the Activity context, second `this` is the IConsumer implementation }
Remember to remove the listener:
protected override void OnStop() { base.OnStop(); wit.RemoveWindowLayoutInfoListener(this); }
When this code is run, the activity will update with the current device posture and display features (if spanned across the fold or hinge). Add additional code to the callback to check for additional information in the
FoldingFeature
object.
Sample
The Window Manager sample shows device information on the screen as shown in this screenshot: