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 for Bounds and IsSeparating, and methods for OcclusionType, Orientation, and State.

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:

  1. Right-click on your Xamarin.Android project and choose Manage NuGet Packages...

  2. Search for Xamarin.AndroidX.Window.WindowJava.

  3. 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

  1. 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; and using 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 the Accept method required by this interface.

  2. 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));
    
  3. 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);
        }
    }
    
  4. 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 of DisplayFeature items, one or more of which could be instances of FoldingFeature. Folding feature instances have properties for Boundsand IsSeparating, and methods for OcclusionType, Orientation, and State that you can query to make decisions about how to adjust your layout for the new state.

  5. In an OnStart override, register the AddWindowLayoutInfoListener handler and pass the executor and a reference to the activity (because it implements IConsumer).

    protected override void OnStart()
    {
        base.OnStart();
        wit.AddWindowLayoutInfoListener(this, runOnUiThreadExecutor(), this);
        // first `this` is the Activity context, second `this` is the IConsumer implementation
    }
    
  6. Remember to remove the listener:

    protected override void OnStop()
    {
        base.OnStop();
        wit.RemoveWindowLayoutInfoListener(this);
    }
    
  7. 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:

Surface Duo showing Window Manager sample running, and showing device info on the screen

Resources