[all] Window Manager (alpha09) (#16)
* [TwoPage] test AndroidX.Window NuGet alpha01 quick hack - not ready for release * change theme for WindowManager migrated samples to help recognize/differentiate during testing * Update to alpha05 * Update to alpha05.1 with updated binding Still hacky code paths but Window Manager seems to be working * remove test/debug code * [DualView] AndroidX.Window alpha05 * [CompanionPane] AndroidX.Window alpha05 * [ListDetail] AndroidX.Window alpha05 * [IntentToSecondScreen] remove DuoSDK (not used) * [DragAndDrop] AndroidX.Window alpha05 * [WindowManager] new sample Based on the Kotlin version * add reference to original source https://github.com/googlecodelabs/android-foldable-codelab/tree/main/window-manager * Update to WM alpha06, add isSeparating method call test * fix layouts when spanned * additional data display * [TwoPage] alpha06 * [ListDetail] alpha06 * [DualView] alpha06 * [CompanionPane] alpha06 * update DisplayFeature parsing * formatting fixes * [WindowManager] add README and screenshot * update all to alpha07 * update all to alpha09 API surface changed in this binding update https://github.com/xamarin/AndroidX/issues/325#issuecomment-882718035 * new alphas can be registered in OnStart previously crashed if registered before OnAttachedToWindow
|
@ -135,19 +135,19 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Xamarin.AndroidX.AppCompat">
|
||||
<Version>1.1.0</Version>
|
||||
<Version>1.2.0.5</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.CardView">
|
||||
<Version>1.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.ConstraintLayout">
|
||||
<Version>1.1.3</Version>
|
||||
<Version>2.0.4.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.RecyclerView">
|
||||
<Version>1.1.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.DuoSdk">
|
||||
<Version>0.0.3.4</Version>
|
||||
<PackageReference Include="Xamarin.AndroidX.Window">
|
||||
<Version>1.0.0-alpha09</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
|
||||
using Android.Content.Res;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Util;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using AndroidX.AppCompat.App;
|
||||
using AndroidX.Core.Util;
|
||||
using AndroidX.Fragment.App;
|
||||
using AndroidX.Window;
|
||||
using CompanionPane.Fragments;
|
||||
using Microsoft.Device.Display;
|
||||
using Java.Lang;
|
||||
using Java.Util.Concurrent;
|
||||
using System.Linq;
|
||||
|
||||
namespace CompanionPane
|
||||
|
@ -18,14 +20,17 @@ namespace CompanionPane
|
|||
RoundIcon = "@mipmap/ic_launcher_round",
|
||||
Theme = "@style/AppTheme",
|
||||
MainLauncher = true,
|
||||
ConfigurationChanges = Android.Content.PM.ConfigChanges.ScreenSize | Android.Content.PM.ConfigChanges.ScreenLayout | Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.SmallestScreenSize)]
|
||||
public class MainActivity : AppCompatActivity, BaseFragment.IOnItemSelectedListener
|
||||
ConfigurationChanges = Android.Content.PM.ConfigChanges.ScreenSize | Android.Content.PM.ConfigChanges.ScreenLayout | Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.SmallestScreenSize | Android.Content.PM.ConfigChanges.UiMode)]
|
||||
public class MainActivity : AppCompatActivity, BaseFragment.IOnItemSelectedListener, IConsumer
|
||||
{
|
||||
const string TAG = "JWM"; // Jetpack Window Manager
|
||||
WindowManager wm;
|
||||
FoldingFeature.Orientation hingeOrientation = FoldingFeature.Orientation.Vertical;
|
||||
bool isDuo, isDualMode;
|
||||
|
||||
SinglePortrait singlePortrait;
|
||||
DualPortrait dualPortrait;
|
||||
DualLandscape dualLandscape;
|
||||
ScreenHelper screenHelper;
|
||||
bool isDuo;
|
||||
int currentPosition;
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
|
@ -34,10 +39,6 @@ namespace CompanionPane
|
|||
|
||||
SetContentView(Resource.Layout.activity_main);
|
||||
|
||||
screenHelper = new ScreenHelper();
|
||||
|
||||
isDuo = screenHelper.Initialize(this);
|
||||
|
||||
var slides = Slide.Slides.ToList();
|
||||
|
||||
currentPosition = 0;
|
||||
|
@ -47,9 +48,68 @@ namespace CompanionPane
|
|||
dualPortrait.RegisterOnItemSelectedListener(this);
|
||||
dualLandscape = DualLandscape.NewInstance(slides);
|
||||
dualLandscape.RegisterOnItemSelectedListener(this);
|
||||
|
||||
wm = new WindowManager(this);
|
||||
|
||||
SetupLayout();
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void Accept(Java.Lang.Object newLayoutInfo) // Object will be WindowLayoutInfo
|
||||
{
|
||||
Log.Info(TAG, "===LayoutStateChangeCallback.Accept");
|
||||
var wli = newLayoutInfo as WindowLayoutInfo;
|
||||
if (wli.DisplayFeatures.Count == 0)
|
||||
{ // no hinge found
|
||||
isDualMode = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var df in wli.DisplayFeatures)
|
||||
{
|
||||
Log.Info(TAG, "Bounds:" + df.Bounds);
|
||||
var ff = df as FoldingFeature;
|
||||
if (!(ff is null))
|
||||
{ // a hinge exists
|
||||
Log.Info(TAG, "Orientation: " + ff.GetOrientation());
|
||||
isDualMode = true;
|
||||
hingeOrientation = ff.GetOrientation();
|
||||
isDuo = true; //HACK: set first time we see the hinge, never un-set
|
||||
}
|
||||
else
|
||||
{ // no hinge found
|
||||
isDualMode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
SetupLayout();
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
wm.RegisterLayoutChangeCallback(runOnUiThreadExecutor(), this);
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
base.OnStop();
|
||||
wm.UnregisterLayoutChangeCallback(this);
|
||||
}
|
||||
|
||||
void ShowFragment(Fragment fragment)
|
||||
{
|
||||
var fragmentTransaction = SupportFragmentManager.BeginTransaction();
|
||||
|
@ -69,19 +129,9 @@ namespace CompanionPane
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnConfigurationChanged(Configuration newConfig)
|
||||
void UseDualMode(FoldingFeature.Orientation hingeOrientation)
|
||||
{
|
||||
base.OnConfigurationChanged(newConfig);
|
||||
|
||||
if(ScreenHelper.IsDualScreenDevice(this))
|
||||
screenHelper.Update();
|
||||
|
||||
SetupLayout();
|
||||
}
|
||||
|
||||
void UseDualMode(SurfaceOrientation rotation)
|
||||
{
|
||||
if (rotation == SurfaceOrientation.Rotation90 || rotation == SurfaceOrientation.Rotation270)
|
||||
if (hingeOrientation == FoldingFeature.Orientation.Horizontal)
|
||||
{
|
||||
dualLandscape.setCurrentPosition(currentPosition);
|
||||
ShowFragment(dualLandscape);
|
||||
|
@ -107,12 +157,10 @@ namespace CompanionPane
|
|||
|
||||
void SetupLayout()
|
||||
{
|
||||
var rotation = ScreenHelper.GetRotation(this);
|
||||
|
||||
if (isDuo)
|
||||
{
|
||||
if (screenHelper.IsDualMode)
|
||||
UseDualMode(rotation);
|
||||
if (isDualMode)
|
||||
UseDualMode(hingeOrientation);
|
||||
else
|
||||
UseSingleMode();
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#008577"
|
||||
android:fillColor="#1976d2"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#008577</color>
|
||||
<color name="colorPrimaryDark">#00574B</color>
|
||||
<color name="colorAccent">#D81B60</color>
|
||||
<color name="lightGray">#e3e3e3</color>
|
||||
<color name="colorPrimary">#1976d2</color>
|
||||
<color name="colorPrimaryDark">#004ba0</color>
|
||||
<color name="colorAccent">#ffb300</color>
|
||||
<color name="lightGray">#e3e3e3</color>
|
||||
</resources>
|
||||
|
|
|
@ -101,15 +101,15 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Xamarin.AndroidX.AppCompat">
|
||||
<Version>1.1.0</Version>
|
||||
<Version>1.2.0.5</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.ConstraintLayout">
|
||||
<Version>1.1.3</Version>
|
||||
<Version>2.0.4.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.DuoSdk">
|
||||
<Version>0.0.3.4</Version>
|
||||
<PackageReference Include="Xamarin.AndroidX.Window">
|
||||
<Version>1.0.0-alpha09</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.1.0-rc2" />
|
||||
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.4.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-hdpi\surface_duo.png" />
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
using Android.App;
|
||||
using Android.Content.Res;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Widget;
|
||||
using AndroidX.AppCompat.App;
|
||||
using AndroidX.Fragment.App;
|
||||
using Google.Android.Material.FloatingActionButton;
|
||||
using Microsoft.Device.Display;
|
||||
using Fragment = AndroidX.Fragment.App.Fragment;
|
||||
using AndroidX.Window;
|
||||
using AndroidX.Core.Util;
|
||||
using Java.Util.Concurrent;
|
||||
using Java.Lang;
|
||||
using Android.Util;
|
||||
using Android.Views;
|
||||
using Android.Content;
|
||||
using Java.Interop;
|
||||
|
||||
namespace DragAndDrop
|
||||
{
|
||||
[Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
|
||||
public class MainActivity : AppCompatActivity
|
||||
public class MainActivity : AppCompatActivity, IConsumer
|
||||
{
|
||||
ScreenHelper screenHelper;
|
||||
bool isDuo = false;
|
||||
const string TAG = "JWM"; // Jetpack Window Manager
|
||||
WindowManager wm;
|
||||
bool isDuo, isDualMode;
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
|
@ -24,8 +29,7 @@ namespace DragAndDrop
|
|||
// Set our view from the "main" layout resource
|
||||
SetContentView(Resource.Layout.activity_main);
|
||||
|
||||
screenHelper = new ScreenHelper();
|
||||
isDuo = screenHelper.Initialize(this);
|
||||
wm = new WindowManager(this);
|
||||
|
||||
SetupLayout();
|
||||
|
||||
|
@ -33,12 +37,70 @@ namespace DragAndDrop
|
|||
fab.Click += (s, e) => Recreate();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void Accept(Java.Lang.Object newLayoutInfo) // Object will be WindowLayoutInfo
|
||||
{
|
||||
Log.Info(TAG, "===LayoutStateChangeCallback.Accept");
|
||||
var wli = newLayoutInfo as WindowLayoutInfo;
|
||||
if (wli.DisplayFeatures.Count == 0)
|
||||
{ // no hinge found
|
||||
isDualMode = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var df in wli.DisplayFeatures)
|
||||
{
|
||||
Log.Info(TAG, "Bounds:" + df.Bounds);
|
||||
var ff = df as FoldingFeature;
|
||||
if (!(ff is null))
|
||||
{ // a hinge exists
|
||||
Log.Info(TAG, "Orientation: " + ff.GetOrientation());
|
||||
isDualMode = true;
|
||||
isDuo = true; //HACK: set first time we see the hinge, never un-set
|
||||
}
|
||||
else
|
||||
{ // no hinge found
|
||||
isDualMode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
SetupLayout();
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
wm.RegisterLayoutChangeCallback(runOnUiThreadExecutor(), this);
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
base.OnStop();
|
||||
wm.UnregisterLayoutChangeCallback(this);
|
||||
}
|
||||
|
||||
void SetupLayout()
|
||||
{
|
||||
var bundle = new Bundle();
|
||||
var rotation = ScreenHelper.GetRotation(this);
|
||||
|
||||
if (isDuo && screenHelper.IsDualMode)
|
||||
var wm = this.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
|
||||
var rotation = SurfaceOrientation.Rotation0;
|
||||
if (wm != null)
|
||||
rotation = wm.DefaultDisplay.Rotation;
|
||||
|
||||
if (isDuo && isDualMode)
|
||||
{
|
||||
switch (rotation)
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#008577"
|
||||
android:fillColor="#1976d2"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
~ Licensed under the MIT License.
|
||||
-->
|
||||
<resources>
|
||||
<color name="colorPrimary">#008577</color>
|
||||
<color name="colorPrimaryDark">#00574B</color>
|
||||
<color name="colorAccent">#D81B60</color>
|
||||
<color name="light_gray">#ededed</color>
|
||||
<color name="colorPrimary">#1976d2</color>
|
||||
<color name="colorPrimaryDark">#004ba0</color>
|
||||
<color name="colorAccent">#ffb300</color>
|
||||
<color name="light_gray">#ededed</color>
|
||||
</resources>
|
||||
|
|
|
@ -121,13 +121,13 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Xamarin.AndroidX.AppCompat">
|
||||
<Version>1.1.0</Version>
|
||||
<Version>1.2.0.5</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.ConstraintLayout">
|
||||
<Version>1.1.3</Version>
|
||||
<Version>2.0.4.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.DuoSdk">
|
||||
<Version>0.0.3.4</Version>
|
||||
<PackageReference Include="Xamarin.AndroidX.Window">
|
||||
<Version>1.0.0-alpha09</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
|
|
|
@ -5,8 +5,12 @@ using Android.Views;
|
|||
using Android.Widget;
|
||||
using AndroidX.AppCompat.App;
|
||||
using DualView.Fragments;
|
||||
using Microsoft.Device.Display;
|
||||
using System.Collections.Generic;
|
||||
using AndroidX.Window;
|
||||
using AndroidX.Core.Util;
|
||||
using Java.Util.Concurrent;
|
||||
using Java.Lang;
|
||||
using Android.Util;
|
||||
|
||||
namespace DualView
|
||||
{
|
||||
|
@ -16,12 +20,15 @@ namespace DualView
|
|||
RoundIcon = "@mipmap/ic_launcher_round",
|
||||
Theme = "@style/AppTheme",
|
||||
MainLauncher = true,
|
||||
ConfigurationChanges = Android.Content.PM.ConfigChanges.ScreenSize | Android.Content.PM.ConfigChanges.ScreenLayout | Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.SmallestScreenSize)]
|
||||
ConfigurationChanges = Android.Content.PM.ConfigChanges.ScreenSize | Android.Content.PM.ConfigChanges.ScreenLayout | Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.SmallestScreenSize | Android.Content.PM.ConfigChanges.UiMode)]
|
||||
|
||||
public class MainActivity : AppCompatActivity, BaseFragment.IOnItemSelectedListener
|
||||
public class MainActivity : AppCompatActivity, BaseFragment.IOnItemSelectedListener, IConsumer
|
||||
{
|
||||
ScreenHelper screenHelper;
|
||||
bool isDuo;
|
||||
const string TAG = "JWM"; // Jetpack Window Manager
|
||||
WindowManager wm;
|
||||
FoldingFeature.Orientation hingeOrientation = FoldingFeature.Orientation.Vertical;
|
||||
bool isDuo, isDualMode;
|
||||
|
||||
Dictionary<string, BaseFragment> fragmentMap;
|
||||
int currentSelectedPosition = -1;
|
||||
|
||||
|
@ -32,8 +39,6 @@ namespace DualView
|
|||
{
|
||||
base.OnCreate(savedInstanceState);
|
||||
SetContentView(Resource.Layout.activity_main);
|
||||
screenHelper = new ScreenHelper();
|
||||
isDuo = screenHelper.Initialize(this);
|
||||
var items = Item.Items;
|
||||
fragmentMap = new Dictionary<string, BaseFragment>();
|
||||
|
||||
|
@ -49,9 +54,66 @@ namespace DualView
|
|||
dualLandscape.RegisterOnItemSelectedListener(this);
|
||||
fragmentMap[GetSimpleName<DualLandscape>()] = dualLandscape;
|
||||
|
||||
wm = new WindowManager(this);
|
||||
|
||||
SetupLayout();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void Accept(Java.Lang.Object newLayoutInfo) // Object will be WindowLayoutInfo
|
||||
{
|
||||
Log.Info(TAG, "===LayoutStateChangeCallback.Accept");
|
||||
var wli = newLayoutInfo as WindowLayoutInfo;
|
||||
if (wli.DisplayFeatures.Count == 0)
|
||||
{ // no hinge found
|
||||
isDualMode = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var df in wli.DisplayFeatures)
|
||||
{
|
||||
Log.Info(TAG, "Bounds:" + df.Bounds);
|
||||
var ff = df as FoldingFeature;
|
||||
if (!(ff is null))
|
||||
{ // a hinge exists
|
||||
Log.Info(TAG, "Orientation: " + ff.GetOrientation());
|
||||
isDualMode = true;
|
||||
hingeOrientation = ff.GetOrientation();
|
||||
isDuo = true; //HACK: set first time we see the hinge, never un-set
|
||||
}
|
||||
else
|
||||
{ // no hinge found
|
||||
isDualMode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
SetupLayout();
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
wm.RegisterLayoutChangeCallback(runOnUiThreadExecutor(), this);
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
base.OnStop();
|
||||
wm.UnregisterLayoutChangeCallback(this);
|
||||
}
|
||||
|
||||
void UseSingleMode()
|
||||
{
|
||||
var baseFragment = fragmentMap[GetSimpleName<SinglePortrait>()];
|
||||
|
@ -59,32 +121,33 @@ namespace DualView
|
|||
ShowFragment(baseFragment);
|
||||
}
|
||||
|
||||
void UseDualMode(SurfaceOrientation rotation)
|
||||
void UseDualMode(FoldingFeature.Orientation hingeOrientation)
|
||||
{
|
||||
switch (rotation)
|
||||
{
|
||||
case SurfaceOrientation.Rotation90:
|
||||
case SurfaceOrientation.Rotation270:
|
||||
// Setting layout for double landscape
|
||||
var baseFragment = fragmentMap[GetSimpleName<DualLandscape>()];
|
||||
if (baseFragment != null)
|
||||
ShowFragment(baseFragment);
|
||||
break;
|
||||
default:
|
||||
var baseFragment1 = fragmentMap[GetSimpleName<DualPortrait>()];
|
||||
if (baseFragment1 != null)
|
||||
ShowFragment(baseFragment1);
|
||||
break;
|
||||
if (hingeOrientation == FoldingFeature.Orientation.Horizontal)
|
||||
{ // hinge horizontal - setting layout for double landscape
|
||||
var baseFragment = fragmentMap[GetSimpleName<DualLandscape>()];
|
||||
if (baseFragment != null)
|
||||
{
|
||||
ShowFragment(baseFragment);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //includes FoldingFeature.Orientation.Vertical
|
||||
// hinge vertical - setting layout for double portrait
|
||||
var baseFragment1 = fragmentMap[GetSimpleName<DualPortrait>()];
|
||||
if (baseFragment1 != null)
|
||||
{
|
||||
ShowFragment(baseFragment1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetupLayout()
|
||||
{
|
||||
var rotation = ScreenHelper.GetRotation(this);
|
||||
if (isDuo)
|
||||
{
|
||||
if (screenHelper.IsDualMode)
|
||||
UseDualMode(rotation);
|
||||
if (isDualMode)
|
||||
UseDualMode(hingeOrientation);
|
||||
else
|
||||
UseSingleMode();
|
||||
}
|
||||
|
@ -94,16 +157,6 @@ namespace DualView
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnConfigurationChanged(Configuration newConfig)
|
||||
{
|
||||
base.OnConfigurationChanged(newConfig);
|
||||
|
||||
if (ScreenHelper.IsDualScreenDevice(this))
|
||||
screenHelper.Update();
|
||||
|
||||
SetupLayout();
|
||||
}
|
||||
|
||||
void ShowFragment(BaseFragment fragment)
|
||||
{
|
||||
var fragmentTransaction = SupportFragmentManager.BeginTransaction();
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#008577"
|
||||
android:fillColor="#1976d2"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
-->
|
||||
|
||||
<resources>
|
||||
<color name="colorPrimary">#008577</color>
|
||||
<color name="colorPrimaryDark">#00574B</color>
|
||||
<color name="colorAccent">#5ed8af</color>
|
||||
<color name="colorPrimary">#1976d2</color>
|
||||
<color name="colorPrimaryDark">#004ba0</color>
|
||||
<color name="colorAccent">#ffb300</color>
|
||||
</resources>
|
||||
|
|
|
@ -92,10 +92,10 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Xamarin.AndroidX.AppCompat">
|
||||
<Version>1.1.0</Version>
|
||||
<Version>1.2.0.5</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.ConstraintLayout">
|
||||
<Version>1.1.3</Version>
|
||||
<Version>2.0.4.2</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -99,13 +99,10 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Xamarin.AndroidX.AppCompat">
|
||||
<Version>1.1.0</Version>
|
||||
<Version>1.2.0.5</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.ConstraintLayout">
|
||||
<Version>1.1.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.DuoSdk">
|
||||
<Version>0.0.3.4</Version>
|
||||
<Version>2.0.4.2</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -98,16 +98,16 @@
|
|||
<Version>12.0.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.AppCompat">
|
||||
<Version>1.1.0</Version>
|
||||
<Version>1.2.0.5</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.CardView">
|
||||
<Version>1.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.ConstraintLayout">
|
||||
<Version>1.1.3</Version>
|
||||
<Version>2.0.4.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.DuoSdk">
|
||||
<Version>0.0.3.4</Version>
|
||||
<PackageReference Include="Xamarin.AndroidX.Window">
|
||||
<Version>1.0.0-alpha09</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -3,11 +3,14 @@ using Android.Content.Res;
|
|||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using AndroidX.AppCompat.App;
|
||||
using AndroidX.Fragment.App;
|
||||
using ListDetail.Fragments;
|
||||
using Microsoft.Device.Display;
|
||||
using AndroidX.Window;
|
||||
using AndroidX.Core.Util;
|
||||
using Java.Util.Concurrent;
|
||||
using Java.Lang;
|
||||
using Android.Util;
|
||||
|
||||
namespace ListDetail
|
||||
{
|
||||
|
@ -18,10 +21,13 @@ namespace ListDetail
|
|||
Theme = "@style/AppTheme",
|
||||
MainLauncher = true,
|
||||
ConfigurationChanges = Android.Content.PM.ConfigChanges.ScreenSize | Android.Content.PM.ConfigChanges.ScreenLayout | Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.SmallestScreenSize)]
|
||||
public class MainActivity : AppCompatActivity
|
||||
public class MainActivity : AppCompatActivity, IConsumer
|
||||
{
|
||||
ScreenHelper screenHelper;
|
||||
bool isDuo;
|
||||
const string TAG = "JWM"; // Jetpack Window Manager
|
||||
WindowManager wm;
|
||||
FoldingFeature.Orientation hingeOrientation = FoldingFeature.Orientation.Vertical;
|
||||
bool isDuo, isDualMode;
|
||||
|
||||
SinglePortrait singlePortrait;
|
||||
DualPortrait dualPortrait;
|
||||
|
||||
|
@ -29,39 +35,92 @@ namespace ListDetail
|
|||
{
|
||||
base.OnCreate(savedInstanceState);
|
||||
SetContentView(Resource.Layout.activity_main);
|
||||
screenHelper = new ScreenHelper();
|
||||
isDuo = screenHelper.Initialize(this);
|
||||
var items = Item.Items;
|
||||
singlePortrait = SinglePortrait.NewInstance(items);
|
||||
dualPortrait = DualPortrait.NewInstance(items);
|
||||
|
||||
wm = new WindowManager(this);
|
||||
|
||||
SetupLayout();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void Accept(Java.Lang.Object newLayoutInfo) // Object will be WindowLayoutInfo
|
||||
{
|
||||
Log.Info(TAG, "===LayoutStateChangeCallback.Accept");
|
||||
var wli = newLayoutInfo as WindowLayoutInfo;
|
||||
if (wli.DisplayFeatures.Count == 0)
|
||||
{ // no hinge found
|
||||
isDualMode = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var df in wli.DisplayFeatures)
|
||||
{
|
||||
Log.Info(TAG, "Bounds:" + df.Bounds);
|
||||
var ff = df as FoldingFeature;
|
||||
if (!(ff is null))
|
||||
{ // a hinge exists
|
||||
Log.Info(TAG, "Orientation: " + ff.GetOrientation());
|
||||
isDualMode = true;
|
||||
hingeOrientation = ff.GetOrientation();
|
||||
isDuo = true; //HACK: set first time we see the hinge, never un-set
|
||||
}
|
||||
else
|
||||
{ // no hinge found
|
||||
isDualMode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
SetupLayout();
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
wm.RegisterLayoutChangeCallback(runOnUiThreadExecutor(), this);
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
base.OnStop();
|
||||
wm.UnregisterLayoutChangeCallback(this);
|
||||
}
|
||||
|
||||
void UseSingleMode()
|
||||
=> ShowFragment(singlePortrait);
|
||||
|
||||
void UseDualMode(SurfaceOrientation rotation)
|
||||
void UseDualMode(FoldingFeature.Orientation hingeOrientation)
|
||||
{
|
||||
switch (rotation)
|
||||
if (hingeOrientation == FoldingFeature.Orientation.Horizontal)
|
||||
{
|
||||
case SurfaceOrientation.Rotation90:
|
||||
case SurfaceOrientation.Rotation270:
|
||||
// Setting layout for double landscape
|
||||
UseSingleMode();
|
||||
break;
|
||||
default:
|
||||
ShowFragment(dualPortrait);
|
||||
break;
|
||||
// hinge horizontal - setting layout for double landscape
|
||||
UseSingleMode();
|
||||
} else {
|
||||
//includes FoldingFeature.Orientation.Vertical
|
||||
// hinge vertical - setting layout for double portrait
|
||||
ShowFragment(dualPortrait);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupLayout()
|
||||
{
|
||||
var rotation = ScreenHelper.GetRotation(this);
|
||||
if (isDuo)
|
||||
{
|
||||
if (screenHelper.IsDualMode)
|
||||
UseDualMode(rotation);
|
||||
if (isDualMode)
|
||||
UseDualMode(hingeOrientation);
|
||||
else
|
||||
UseSingleMode();
|
||||
}
|
||||
|
@ -71,16 +130,6 @@ namespace ListDetail
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnConfigurationChanged(Configuration newConfig)
|
||||
{
|
||||
base.OnConfigurationChanged(newConfig);
|
||||
|
||||
if (ScreenHelper.IsDualScreenDevice(this))
|
||||
screenHelper.Update();
|
||||
|
||||
SetupLayout();
|
||||
}
|
||||
|
||||
void ShowFragment(Fragment fragment)
|
||||
{
|
||||
var fragmentTransaction = SupportFragmentManager.BeginTransaction();
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#008577"
|
||||
android:fillColor="#1976d2"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
-->
|
||||
|
||||
<resources>
|
||||
<color name="colorPrimary">#008577</color>
|
||||
<color name="colorPrimaryDark">#00574B</color>
|
||||
<color name="colorAccent">#008577</color>
|
||||
<color name="colorPrimary">#1976d2</color>
|
||||
<color name="colorPrimaryDark">#004ba0</color>
|
||||
<color name="colorAccent">#ffb300</color>
|
||||
<color name="yellow">#FFD54F</color>
|
||||
</resources>
|
||||
|
|
После Ширина: | Высота: | Размер: 43 KiB |
|
@ -5,8 +5,31 @@ using Android.Views;
|
|||
using Android.Widget;
|
||||
using AndroidX.AppCompat.App;
|
||||
using AndroidX.ViewPager.Widget;
|
||||
using Microsoft.Device.Display;
|
||||
using AndroidX.Window;
|
||||
using AndroidX.Core;
|
||||
using AndroidX.Core.Util;
|
||||
using AndroidX.Annotations;
|
||||
using AndroidX.Collection;
|
||||
using Java.Lang;
|
||||
using Java.Util.Concurrent;
|
||||
using Android.Util;
|
||||
/*
|
||||
15-Apr-21 Use androidx.window-1.0.0-alpha01
|
||||
This is a terrible hack that just aims to get the basics of Window Manager working
|
||||
It doesn't properly handle rotation - just single-portrait/dual-portrait
|
||||
|
||||
19-Apr-21 Update to androidx.window-1.0.0-alpha05
|
||||
Discovered methods are missing from the binding
|
||||
|
||||
20-Apr-21 Update to androidx.window-1.0.0.1-alpha05 (adds RegisterLayoutChangeCallback method)
|
||||
Registering and receiving events works, but the orientation value seems off
|
||||
IConsumer.Accept is added to the Activity so that it can receive method calls on layout state changed
|
||||
|
||||
21-Apr-21 Refactor out test code, seems to work...
|
||||
19-Jul-21 Update to androidx.window-1.0.0-apha09
|
||||
FoldingFeature API changes - some properties became methods (GetOrientation, GetState, GetOcclusionType) and their types became "enums" (static class fields)
|
||||
Use OnStart/Stop instead of OnAttachedToWindow/OnDetached
|
||||
*/
|
||||
namespace TwoPage
|
||||
{
|
||||
[Android.App.Activity(
|
||||
|
@ -16,16 +39,23 @@ namespace TwoPage
|
|||
Theme = "@style/AppTheme",
|
||||
MainLauncher = true,
|
||||
ConfigurationChanges = Android.Content.PM.ConfigChanges.ScreenSize | Android.Content.PM.ConfigChanges.ScreenLayout | Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.SmallestScreenSize)]
|
||||
public class MainActivity : AppCompatActivity, ViewPager.IOnPageChangeListener
|
||||
public class MainActivity : AppCompatActivity, ViewPager.IOnPageChangeListener, IConsumer
|
||||
{
|
||||
const string TAG = "JWM"; // Jetpack Window Manager
|
||||
WindowManager wm;
|
||||
FoldingFeature.Orientation hingeOrientation = FoldingFeature.Orientation.Vertical;
|
||||
bool isDuo, isDualMode;
|
||||
|
||||
ViewPager viewPager;
|
||||
PagerAdapter pagerAdapter;
|
||||
ScreenHelper screenHelper;
|
||||
|
||||
/// <summary>Page number</summary>
|
||||
int position = 0;
|
||||
bool isDuo;
|
||||
View single;
|
||||
View dual;
|
||||
|
||||
//LayoutStateChangeCallback layoutStateChangeCallback = new LayoutStateChangeCallback();
|
||||
|
||||
public bool ShowTwoPages { get; set; } = false;
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
|
@ -33,13 +63,94 @@ namespace TwoPage
|
|||
base.OnCreate(savedInstanceState);
|
||||
var fragments = TestFragment.Fragments;
|
||||
pagerAdapter = new PagerAdapter(SupportFragmentManager, fragments);
|
||||
screenHelper = new ScreenHelper();
|
||||
isDuo = screenHelper.Initialize(this);
|
||||
|
||||
wm = new WindowManager(this);
|
||||
|
||||
single = LayoutInflater.Inflate(Resource.Layout.activity_main, null);
|
||||
dual = LayoutInflater.Inflate(Resource.Layout.double_landscape_layout, null);
|
||||
SetupLayout();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
[System.Obsolete("Sample uses the activity as the callback method host - keeping this for demonstration purposes only")]
|
||||
class LayoutStateChangeCallback : Java.Lang.Object, IConsumer
|
||||
{
|
||||
public void Accept(Java.Lang.Object newLayoutInfo) // Object will be WindowLayoutInfo
|
||||
{
|
||||
Log.Info(TAG, newLayoutInfo.ToString());
|
||||
var wli = newLayoutInfo as WindowLayoutInfo; // cast to Type
|
||||
foreach (var df in wli.DisplayFeatures)
|
||||
{
|
||||
Log.Info(TAG, "Bounds:" + df.Bounds);
|
||||
//var ff = df as FoldingFeature;
|
||||
if ((df is FoldingFeature ff))
|
||||
{
|
||||
Log.Info(TAG, "IsSeparating: " + ff.IsSeparating);
|
||||
Log.Info(TAG, "OcclusionMode: " + ff.GetOcclusionType());
|
||||
Log.Info(TAG, "Orientation: " + ff.GetOrientation());
|
||||
Log.Info(TAG, "State: " + ff.GetState());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Accept(Java.Lang.Object newLayoutInfo) // Object will be WindowLayoutInfo
|
||||
{
|
||||
Log.Info(TAG, "===LayoutStateChangeCallback.Accept");
|
||||
Log.Info(TAG, newLayoutInfo.ToString());
|
||||
var wli = newLayoutInfo as WindowLayoutInfo;
|
||||
if (wli.DisplayFeatures.Count == 0)
|
||||
{ // no hinge found
|
||||
isDualMode = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var df in wli.DisplayFeatures)
|
||||
{
|
||||
Log.Info(TAG, "Bounds:" + df.Bounds);
|
||||
Log.Info(TAG, "(deprecated)Type: {df.Type} (FOLD or HINGE)");
|
||||
var ff = df as FoldingFeature;
|
||||
if (!(ff is null))
|
||||
{ // a hinge exists
|
||||
Log.Info(TAG, "IsSeparating: " + ff.IsSeparating);
|
||||
Log.Info(TAG, "OcclusionMode: " + ff.GetOcclusionType());
|
||||
Log.Info(TAG, "Orientation: " + ff.GetOrientation());
|
||||
Log.Info(TAG, "State: " + ff.GetState());
|
||||
isDualMode = true;
|
||||
hingeOrientation = ff.GetOrientation();
|
||||
isDuo = true; //HACK: set first time we see the hinge, never un-set
|
||||
}
|
||||
else
|
||||
{ // no hinge found
|
||||
isDualMode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
SetupLayout();
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
wm.RegisterLayoutChangeCallback(runOnUiThreadExecutor(), this);
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
base.OnStop();
|
||||
wm.UnregisterLayoutChangeCallback(this);
|
||||
}
|
||||
|
||||
void UseSingleMode()
|
||||
{
|
||||
//Setting layout for single portrait
|
||||
|
@ -48,32 +159,28 @@ namespace TwoPage
|
|||
SetupViewPager();
|
||||
}
|
||||
|
||||
void UseDualMode(SurfaceOrientation rotation)
|
||||
void UseDualMode(FoldingFeature.Orientation hingeOrientation)
|
||||
{
|
||||
switch (rotation)
|
||||
{
|
||||
case SurfaceOrientation.Rotation90:
|
||||
case SurfaceOrientation.Rotation270:
|
||||
// Setting layout for double landscape
|
||||
SetContentView(dual);
|
||||
ShowTwoPages = false;
|
||||
break;
|
||||
default:
|
||||
// Setting layout for double portrait
|
||||
SetContentView(single);
|
||||
ShowTwoPages = true;
|
||||
break;
|
||||
if (hingeOrientation == FoldingFeature.Orientation.Horizontal)
|
||||
{ // hinge horizontal - setting layout for double landscape
|
||||
SetContentView(dual);
|
||||
ShowTwoPages = false;
|
||||
}
|
||||
else
|
||||
{ //includes FoldingFeature.OrientationVertical
|
||||
// hinge vertical - setting layout for double portrait
|
||||
SetContentView(single);
|
||||
ShowTwoPages = true;
|
||||
}
|
||||
SetupViewPager();
|
||||
}
|
||||
|
||||
void SetupLayout()
|
||||
{
|
||||
var rotation = ScreenHelper.GetRotation(this);
|
||||
if (isDuo)
|
||||
{
|
||||
if (screenHelper.IsDualMode)
|
||||
UseDualMode(rotation);
|
||||
if (isDualMode)
|
||||
UseDualMode(hingeOrientation);
|
||||
else
|
||||
UseSingleMode();
|
||||
}
|
||||
|
@ -83,17 +190,7 @@ namespace TwoPage
|
|||
}
|
||||
}
|
||||
|
||||
public override void OnConfigurationChanged(Configuration newConfig)
|
||||
{
|
||||
base.OnConfigurationChanged(newConfig);
|
||||
|
||||
if (ScreenHelper.IsDualScreenDevice(this))
|
||||
screenHelper.Update();
|
||||
|
||||
SetupLayout();
|
||||
}
|
||||
|
||||
void SetupViewPager()
|
||||
void SetupViewPager()
|
||||
{
|
||||
pagerAdapter.ShowTwoPages = ShowTwoPages;
|
||||
if (viewPager != null)
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#008577"
|
||||
android:fillColor="#1976d2"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
-->
|
||||
|
||||
<resources>
|
||||
<color name="colorPrimary">#008577</color>
|
||||
<color name="colorPrimaryDark">#00574B</color>
|
||||
<color name="colorAccent">#D81B60</color>
|
||||
<color name="colorPrimary">#1976d2</color>
|
||||
<color name="colorPrimaryDark">#004ba0</color>
|
||||
<color name="colorAccent">#ffb300</color>
|
||||
</resources>
|
||||
|
|
|
@ -93,13 +93,13 @@
|
|||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Xamarin.AndroidX.AppCompat">
|
||||
<Version>1.1.0</Version>
|
||||
<Version>1.2.0.5</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.ConstraintLayout">
|
||||
<Version>1.1.3</Version>
|
||||
<Version>2.0.4.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.DuoSdk">
|
||||
<Version>0.0.3.4</Version>
|
||||
<PackageReference Include="Xamarin.AndroidX.Window">
|
||||
<Version>1.0.0-alpha09</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
Any raw assets you want to be deployed with your application can be placed in
|
||||
this directory (and child directories) and given a Build Action of "AndroidAsset".
|
||||
|
||||
These files will be deployed with your package and will be accessible using Android's
|
||||
AssetManager, like this:
|
||||
|
||||
public class ReadAsset : Activity
|
||||
{
|
||||
protected override void OnCreate (Bundle bundle)
|
||||
{
|
||||
base.OnCreate (bundle);
|
||||
|
||||
InputStream input = Assets.Open ("my_asset.txt");
|
||||
}
|
||||
}
|
||||
|
||||
Additionally, some Android functions will automatically load asset files:
|
||||
|
||||
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");
|
|
@ -0,0 +1,190 @@
|
|||
using Android.App;
|
||||
using Android.Graphics;
|
||||
using Android.OS;
|
||||
using Android.Util;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using AndroidX.AppCompat.App;
|
||||
using AndroidX.ConstraintLayout.Widget;
|
||||
using AndroidX.Core.Util;
|
||||
using AndroidX.Window;
|
||||
using Java.Lang;
|
||||
using Java.Util.Concurrent;
|
||||
|
||||
/*
|
||||
This sample is a C# port of this Kotlin code
|
||||
https://github.com/googlecodelabs/android-foldable-codelab/tree/main/window-manager
|
||||
which is part of a Google Codelab that explains how to use Window Manager
|
||||
|
||||
19-Jul-21 Update to androidx.window-1.0.0-apha09
|
||||
FoldingFeature API changes - some properties became methods (GetOrientation, GetState, GetOcclusionType) and their types became "enums" (static class fields)
|
||||
Use OnStart/Stop instead of OnAttachedToWindow/OnDetached
|
||||
*/
|
||||
namespace WindowManagerDemo
|
||||
{
|
||||
[Activity(Label = "@string/app_name",
|
||||
Theme = "@style/AppTheme",
|
||||
MainLauncher = true)]//, // HACK: for some reason the Window Manager doesn't work when configuration changes are being handled
|
||||
//ConfigurationChanges = Android.Content.PM.ConfigChanges.ScreenSize | Android.Content.PM.ConfigChanges.ScreenLayout | Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.SmallestScreenSize)]
|
||||
public class MainActivity : AppCompatActivity, IConsumer
|
||||
{
|
||||
const string TAG = "JWM"; // Jetpack Window Manager
|
||||
WindowManager wm;
|
||||
|
||||
ConstraintLayout constraintLayout;
|
||||
TextView windowMetrics, layoutChange, configurationChanged;
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreate(savedInstanceState);
|
||||
|
||||
wm = new WindowManager(this);
|
||||
|
||||
// Set our view from the "main" layout resource
|
||||
SetContentView(Resource.Layout.activity_main);
|
||||
constraintLayout = FindViewById<ConstraintLayout>(Resource.Id.constraint_layout);
|
||||
windowMetrics = FindViewById<TextView>(Resource.Id.window_metrics);
|
||||
layoutChange = FindViewById<TextView>(Resource.Id.layout_change);
|
||||
configurationChanged = FindViewById<TextView>(Resource.Id.configuration_changed);
|
||||
}
|
||||
|
||||
void printLayoutStateChange(WindowLayoutInfo newLayoutInfo)
|
||||
{
|
||||
Log.Info(TAG, wm.CurrentWindowMetrics.Bounds.ToString());
|
||||
Log.Info(TAG, wm.MaximumWindowMetrics.Bounds.ToString());
|
||||
windowMetrics.Text = $"CurrentWindowMetrics: {wm.CurrentWindowMetrics.Bounds}\n" +
|
||||
$"MaximumWindowMetrics: {wm.MaximumWindowMetrics.Bounds}";
|
||||
|
||||
layoutChange.Text = newLayoutInfo.ToString();
|
||||
|
||||
configurationChanged.Text = "One logic/physical display - unspanned";
|
||||
|
||||
foreach (var displayFeature in newLayoutInfo.DisplayFeatures)
|
||||
{
|
||||
if (displayFeature is FoldingFeature foldingFeature)
|
||||
{
|
||||
alignViewToDeviceFeatureBoundaries(newLayoutInfo);
|
||||
|
||||
if (foldingFeature.GetOcclusionType() == FoldingFeature.OcclusionType.None)
|
||||
{
|
||||
configurationChanged.Text = "App is spanned across a fold";
|
||||
}
|
||||
if (foldingFeature.GetOcclusionType() == FoldingFeature.OcclusionType.Full)
|
||||
{
|
||||
configurationChanged.Text = "App is spanned across a hinge";
|
||||
}
|
||||
configurationChanged.Text += "\nIsSeparating: " + foldingFeature.IsSeparating
|
||||
+ "\nOrientation: " + foldingFeature.GetOrientation() // FoldingFeature.Orientation.Vertical or Horizontal
|
||||
+ "\nState: " + foldingFeature.GetState(); // FoldingFeature.StateFlat or StateHalfOpened
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Info(TAG, "DisplayFeature is not a fold or hinge");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void alignViewToDeviceFeatureBoundaries(WindowLayoutInfo newLayoutInfo)
|
||||
{
|
||||
var set = new ConstraintSet();
|
||||
set.Clone(constraintLayout); // existing constraints baseline
|
||||
var foldFeature = newLayoutInfo.DisplayFeatures[0] as FoldingFeature;
|
||||
//We get the display feature bounds.
|
||||
var rect = foldFeature.Bounds;
|
||||
//Set the red hinge indicator's width and height using the Bounds
|
||||
set.ConstrainHeight(Resource.Id.device_feature, rect.Bottom - rect.Top);
|
||||
set.ConstrainWidth(Resource.Id.device_feature, rect.Right - rect.Left);
|
||||
|
||||
set.Connect(
|
||||
Resource.Id.device_feature, ConstraintSet.Start,
|
||||
ConstraintSet.ParentId, ConstraintSet.Start, 0
|
||||
);
|
||||
|
||||
set.Connect(
|
||||
Resource.Id.device_feature, ConstraintSet.Top,
|
||||
ConstraintSet.ParentId, ConstraintSet.Top, 0
|
||||
);
|
||||
|
||||
if (foldFeature.GetOrientation() == FoldingFeature.Orientation.Vertical)
|
||||
{
|
||||
// Device feature is placed vertically
|
||||
set.SetMargin(Resource.Id.device_feature, ConstraintSet.Start, rect.Left);
|
||||
set.Connect(
|
||||
Resource.Id.layout_change, ConstraintSet.End,
|
||||
Resource.Id.device_feature, ConstraintSet.Start, 0
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Device feature is placed horizontally
|
||||
var statusBarHeight = calculateStatusBarHeight();
|
||||
var toolBarHeight = calculateToolbarHeight();
|
||||
|
||||
set.SetMargin(
|
||||
Resource.Id.device_feature, ConstraintSet.Top,
|
||||
rect.Top - statusBarHeight - toolBarHeight
|
||||
);
|
||||
set.Connect(
|
||||
Resource.Id.layout_change, ConstraintSet.Top,
|
||||
Resource.Id.device_feature, ConstraintSet.Bottom, 0
|
||||
);
|
||||
}
|
||||
|
||||
//Set the view to visible and apply constraints
|
||||
set.SetVisibility(Resource.Id.device_feature, (int)SystemUiFlags.Visible); // public static final int VISIBLE = 0x00000000;
|
||||
set.ApplyTo(constraintLayout);
|
||||
}
|
||||
|
||||
int calculateToolbarHeight()
|
||||
{
|
||||
var typedValue = new TypedValue();
|
||||
if (Theme.ResolveAttribute(Android.Resource.Attribute.ActionBarSize, typedValue, true))
|
||||
{
|
||||
return TypedValue.ComplexToDimensionPixelSize(typedValue.Data, Resources.DisplayMetrics);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int calculateStatusBarHeight()
|
||||
{
|
||||
var rect = new Rect();
|
||||
Window.DecorView.GetWindowVisibleDisplayFrame(rect);
|
||||
return rect.Top;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void Accept(Java.Lang.Object newLayoutInfo) // Object will be WindowLayoutInfo
|
||||
{
|
||||
Log.Info(TAG, "===LayoutStateChangeCallback.Accept");
|
||||
Log.Info(TAG, newLayoutInfo.ToString());
|
||||
printLayoutStateChange(newLayoutInfo as WindowLayoutInfo);
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
wm.RegisterLayoutChangeCallback(runOnUiThreadExecutor(), this);
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
base.OnStop();
|
||||
wm.UnregisterLayoutChangeCallback(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.windowmanager" android:installLocation="auto">
|
||||
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="30" />
|
||||
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"></application>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
</manifest>
|
|
@ -0,0 +1,26 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Android.App;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("WindowManager")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("WindowManager")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,11 @@
|
|||
# Window Manager sample for Surface Duo with Xamarin.Android
|
||||
|
||||
Visit the [Window Manager for Xamarin docs](https://docs.microsoft.com/dual-screen/xamarin/) for more information on using Window Manager in your apps.
|
||||
|
||||
![Window Manager example spanned across two screens](Screenshots/xamarin-window-manager.png)
|
||||
|
||||
## Related links
|
||||
|
||||
- [Introduction to dual-screen devices](https://docs.microsoft.com/dual-screen/introduction)
|
||||
- [Get the Surface Duo emulator](https://docs.microsoft.com/dual-screen/android/emulator/)
|
||||
- [Xamarin.Android documentation](https://docs.microsoft.com/xamarin/android/)
|
|
@ -0,0 +1,44 @@
|
|||
Images, layout descriptions, binary blobs and string dictionaries can be included
|
||||
in your application as resource files. Various Android APIs are designed to
|
||||
operate on the resource IDs instead of dealing with images, strings or binary blobs
|
||||
directly.
|
||||
|
||||
For example, a sample Android app that contains a user interface layout (main.xml),
|
||||
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
|
||||
would keep its resources in the "Resources" directory of the application:
|
||||
|
||||
Resources/
|
||||
drawable/
|
||||
icon.png
|
||||
|
||||
layout/
|
||||
main.xml
|
||||
|
||||
values/
|
||||
strings.xml
|
||||
|
||||
In order to get the build system to recognize Android resources, set the build action to
|
||||
"AndroidResource". The native Android APIs do not operate directly with filenames, but
|
||||
instead operate on resource IDs. When you compile an Android application that uses resources,
|
||||
the build system will package the resources for distribution and generate a class called "R"
|
||||
(this is an Android convention) that contains the tokens for each one of the resources
|
||||
included. For example, for the above Resources layout, this is what the R class would expose:
|
||||
|
||||
public class R {
|
||||
public class drawable {
|
||||
public const int icon = 0x123;
|
||||
}
|
||||
|
||||
public class layout {
|
||||
public const int main = 0x456;
|
||||
}
|
||||
|
||||
public class strings {
|
||||
public const int first_string = 0xabc;
|
||||
public const int second_string = 0xbcd;
|
||||
}
|
||||
}
|
||||
|
||||
You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main
|
||||
to reference the layout/main.xml file, or R.strings.first_string to reference the first
|
||||
string in the dictionary file values/strings.xml.
|
|
@ -0,0 +1,34 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
|
@ -0,0 +1,170 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#1976d2"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/constraint_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/window_metrics"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="20dp"
|
||||
android:text="@string/window_metrics"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/layout_change"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/layout_change"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="20dp"
|
||||
android:text="@string/layout_change_text"
|
||||
android:textSize="20sp"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/configuration_changed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/window_metrics" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/configuration_changed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="20dp"
|
||||
android:text="@string/configuration_changed"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/layout_change" />
|
||||
|
||||
<!-- It's not important where this view is placed by default, it will be adjusted dynamically in runtime -->
|
||||
<View
|
||||
android:id="@+id/device_feature"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@android:color/holo_red_dark"
|
||||
android:visibility="gone"
|
||||
tools:ignore="MissingConstraints" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
После Ширина: | Высота: | Размер: 2.9 KiB |
После Ширина: | Высота: | Размер: 1.4 KiB |
После Ширина: | Высота: | Размер: 4.8 KiB |
После Ширина: | Высота: | Размер: 2.0 KiB |
После Ширина: | Высота: | Размер: 958 B |
После Ширина: | Высота: | Размер: 2.7 KiB |
После Ширина: | Высота: | Размер: 4.4 KiB |
После Ширина: | Высота: | Размер: 2.0 KiB |
После Ширина: | Высота: | Размер: 6.7 KiB |
После Ширина: | Высота: | Размер: 6.2 KiB |
После Ширина: | Высота: | Размер: 3.3 KiB |
После Ширина: | Высота: | Размер: 10 KiB |
После Ширина: | Высота: | Размер: 8.9 KiB |
После Ширина: | Высота: | Размер: 4.8 KiB |
После Ширина: | Высота: | Размер: 15 KiB |
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#1976d2</color>
|
||||
<color name="colorPrimaryDark">#004ba0</color>
|
||||
<color name="colorAccent">#ffb300</color>
|
||||
</resources>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#2C3E50</color>
|
||||
</resources>
|
|
@ -0,0 +1,6 @@
|
|||
<resources>
|
||||
<string name="app_name">WindowManager09</string>
|
||||
<string name="window_metrics">window metrics</string>
|
||||
<string name="layout_change_text">layout change text</string>
|
||||
<string name="configuration_changed">Using one logic/physical display - unspanned</string>
|
||||
</resources>
|
|
@ -0,0 +1,11 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,130 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{244F483B-70E4-4D11-A0E9-355C9FBF9748}</ProjectGuid>
|
||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<TemplateGuid>{122416d6-6b49-4ee2-a1e8-b825f31c79fe}</TemplateGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>WindowManagerDemo</RootNamespace>
|
||||
<AssemblyName>WindowManagerDemo</AssemblyName>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>True</Deterministic>
|
||||
<AndroidApplication>True</AndroidApplication>
|
||||
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
|
||||
<AndroidResgenClass>Resource</AndroidResgenClass>
|
||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
|
||||
<TargetFrameworkVersion>v11.0</TargetFrameworkVersion>
|
||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
|
||||
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
|
||||
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
|
||||
<AndroidUseAapt2>true</AndroidUseAapt2>
|
||||
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>False</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
|
||||
<AndroidLinkMode>None</AndroidLinkMode>
|
||||
<EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>True</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidManagedSymbols>true</AndroidManagedSymbols>
|
||||
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
||||
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
|
||||
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="Mono.Android" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="MainActivity.cs" />
|
||||
<Compile Include="Resources\Resource.designer.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources\AboutResources.txt" />
|
||||
<None Include="Properties\AndroidManifest.xml" />
|
||||
<None Include="Assets\AboutAssets.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\layout\activity_main.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\values\colors.xml" />
|
||||
<AndroidResource Include="Resources\values\ic_launcher_background.xml" />
|
||||
<AndroidResource Include="Resources\values\strings.xml" />
|
||||
<AndroidResource Include="Resources\values\styles.xml" />
|
||||
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
|
||||
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher_round.xml" />
|
||||
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher.png" />
|
||||
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_foreground.png" />
|
||||
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_round.png" />
|
||||
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher.png" />
|
||||
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_foreground.png" />
|
||||
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_round.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_foreground.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_round.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_foreground.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_round.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_foreground.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_round.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.2.0.5" />
|
||||
<PackageReference Include="Xamarin.AndroidX.ConstraintLayout">
|
||||
<Version>2.0.4.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.AndroidX.Window">
|
||||
<Version>1.0.0-alpha09</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.4.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\ic_launcher_background.xml">
|
||||
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-v24\ic_launcher_foreground.xml">
|
||||
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -25,6 +25,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DragAndDrop", "DragAndDrop\
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ListDetail", "ListDetail\ListDetail.csproj", "{F8DB8953-1A58-4EC1-A971-4976036B4BBE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowManager", "WindowManager\WindowManager.csproj", "{244F483B-70E4-4D11-A0E9-355C9FBF9748}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -82,6 +84,12 @@ Global
|
|||
{F8DB8953-1A58-4EC1-A971-4976036B4BBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F8DB8953-1A58-4EC1-A971-4976036B4BBE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F8DB8953-1A58-4EC1-A971-4976036B4BBE}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{244F483B-70E4-4D11-A0E9-355C9FBF9748}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{244F483B-70E4-4D11-A0E9-355C9FBF9748}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{244F483B-70E4-4D11-A0E9-355C9FBF9748}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{244F483B-70E4-4D11-A0E9-355C9FBF9748}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{244F483B-70E4-4D11-A0E9-355C9FBF9748}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{244F483B-70E4-4D11-A0E9-355C9FBF9748}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -96,6 +104,7 @@ Global
|
|||
{4D77AA39-048C-4733-BE7F-BD41C8EE278B} = {CB956396-560E-4974-9CE4-0EBC33AB1E12}
|
||||
{9B727405-CB01-4FCD-9134-9040F230F741} = {CB956396-560E-4974-9CE4-0EBC33AB1E12}
|
||||
{F8DB8953-1A58-4EC1-A971-4976036B4BBE} = {CB956396-560E-4974-9CE4-0EBC33AB1E12}
|
||||
{244F483B-70E4-4D11-A0E9-355C9FBF9748} = {CB956396-560E-4974-9CE4-0EBC33AB1E12}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3B8A9A9E-B7A6-4318-BA6C-715546DFFFB6}
|
||||
|
|