Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] Xamarin.Forms.Device.Idiom return Desktop instead of Phone while Xamarin.Essentials.DeviceInfo.Idiom return idiom correct #11166

Closed
kcrg opened this issue Jun 22, 2020 · 11 comments · Fixed by #11206
Assignees
Labels
4.5.0 regression on 4.5.0 e/2 🕑 2 i/regression in-progress This issue has an associated pull request that may resolve it! p/Android t/bug 🐛

Comments

@kcrg
Copy link

kcrg commented Jun 22, 2020

Description

Returns inconsistent values from Forms and Essentials.

Steps to Reproduce

  1. Compare returns values from Xamarin.Essentials.DeviceInfo.Idiom and Xamarin.Forms.Device.Idiom on Xiaomi Mi9 SE

Expected Behavior

Xamarin.Forms.Device.Idiom and Xamarin.Essentials.DeviceInfo.Idiom behave in same way.

Actual Behavior

Xamarin.Forms.Device.Idiom returns TargetIdiom.Desktop
Xamarin.Essentials.DeviceInfo.Idiom returns DeviceIdiom.Phone

Basic Information

  • Versions with issue: XF 4.5, 4.6, 4.7
  • Last known good version: XF 4.4
  • Nuget Packages: Xamarin.Essentials 1.5.3.2 and tested on XF 4.4-4.7
  • Affected Devices: Xiaomi Mi9 SE with MIUI 12 - Android 10

Screenshots

XF 4.7
Screenshot 2020-06-22 at 16 19 42
Screenshot_2020-06-22-16-18-42-189_com companyname idiomtest

XF 4.4
Screenshot_2020-06-22-16-16-39-629_com companyname idiomtest

Additional Info

It's a similar issue to xamarin/Essentials#1027

@kcrg kcrg added s/unverified New report that has yet to be verified t/bug 🐛 labels Jun 22, 2020
@pauldipietro pauldipietro added this to New in Triage Jun 22, 2020
@samhouts samhouts added this to the 4.5.0 milestone Jun 23, 2020
@samhouts samhouts added the in-progress This issue has an associated pull request that may resolve it! label Jun 25, 2020
@samhouts samhouts added this to In Progress in 4.7.0 Jun 25, 2020
@samhouts samhouts removed this from In Progress in 4.7.0 Jun 26, 2020
@samhouts samhouts added e/2 🕑 2 and removed s/unverified New report that has yet to be verified labels Jun 26, 2020
@samhouts samhouts moved this from New to Ready For Work in Triage Jun 26, 2020
@samhouts samhouts removed this from Ready For Work in Triage Jun 29, 2020
@samhouts samhouts removed this from the 4.5.0 milestone Jul 8, 2020
@samhouts samhouts added this to In Progress in vCurrent (4.8.0) Jul 30, 2020
@samhouts samhouts moved this from To do to In progress in Android Ready For Work Jul 30, 2020
@PawKanarek
Copy link
Contributor

PawKanarek commented Aug 5, 2020

Great! Another Xiaomi device and another results... This destroys my app completely :(
Xamarin.Froms.Device.Idiom == "Desktop"
Xamarin.Essentials.DeviceInfo.Idiom == "Phone"

image

Code used to render page:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reactive.Linq;
using Xamarin.Forms;

namespace Pages
{
    public partial class DiagnosticPage : ContentPage
    {
        public List<string> List = new List<string>(); 
        public DiagnosticPage()
        {
            this.List.Add($"XF.Device.Info.CurrentOrientation: {Device.Info.CurrentOrientation}");
            this.List.Add($"XF.Device.Info.ScalingFactor: {Device.Info.ScalingFactor}");
            this.List.Add($"XF.Device.Info.PixelScreenSize: {Device.Info.PixelScreenSize}");
            this.List.Add($"XF.Device.Info.ScaledScreenSize: {Device.Info.ScaledScreenSize}");
            this.List.Add($"XF.Device.Idiom: {Device.Idiom}");
            this.List.Add($"XF.Device.RuntimePlatform: {Device.RuntimePlatform}");
            this.List.Add($"Essentials.DeviceInfo.Idiom: {Xamarin.Essentials.DeviceInfo.Idiom}");
            this.List.Add($"Essentials.DeviceInfo.Manufacturer: {Xamarin.Essentials.DeviceInfo.Manufacturer}");
            this.List.Add($"Essentials.DeviceInfo.Model: {Xamarin.Essentials.DeviceInfo.Model}");
            this.List.Add($"Essentials.DeviceInfo.Name: {Xamarin.Essentials.DeviceInfo.Name}");
            this.List.Add($"Essentials.DeviceInfo.Platform: {Xamarin.Essentials.DeviceInfo.Platform}");
            this.List.Add($"Essentials.DeviceInfo.Version: {Xamarin.Essentials.DeviceInfo.Version}");
            this.List.Add($"Essentials.DeviceInfo.DeviceType: {Xamarin.Essentials.DeviceInfo.DeviceType}");
            this.List.Add($"Essentials.DeviceInfo.VersionString: {Xamarin.Essentials.DeviceInfo.VersionString}");
            this.InitializeComponent();
            BindableLayout.SetItemsSource(this.Layout, this.List);
        }
    }
}


@@ -0,0 +1,17 @@
<ContentPage x:Class="Pages.DiagnosticPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    BackgroundColor="Black">

    <Grid AutomationId="LoadingPageId" CompressedLayout.IsHeadless="True" BackgroundColor="Black">
        <StackLayout x:Name="Layout">
            <BindableLayout.ItemTemplate>
                <DataTemplate>
                    <Label Text="{Binding .}" TextColor="White"/>
                </DataTemplate>
            </BindableLayout.ItemTemplate>
        </StackLayout>
    </Grid>
</ContentPage>

Can you fix this in Xamarin Forms 4.5?

@EmilAlipiev
Copy link
Contributor

Does it only occur for Xaomi devices?

@lucas-zimerman
Copy link

lucas-zimerman commented Aug 5, 2020

Interesting, I suffer from the same issue, it was working just fine on Xamarin Forms 4.4.0.991640, and when we updated to 4.6.0.800 that happened.

Here's the list of affected devices by this bug on my side

  1. Xiaomi Redmi Note 8T running Android 9
  2. Xiaomi Redmi Note 8 Pro running Android 10
  3. Xiaomi Redmi Note 8 running Android 9
  4. Xiaomi Redmi Note 7 running Android 9 or Android 10
  5. Xiaomi POCOPHONE F1 running Android 10
  6. Xiaomi Mi Note 10 running Android 10
  7. Xiaomi MI 8 Lite running Android 10
  8. Samsung SM-T865 running Android 10

and it's weird that it only affects some specific devices, like a user X with a Redmi Note 8 returns the Phone value while the user Y with a Redmi Note 8 returns a value that's not the one defined on the Phone idiom.

Sadly for me, because it's returning a non defined value it's crashing my app, here's an attached list showing that for some users the app is working just fine (receiving the Phone value) while others crash the app (by not receiving a Phone value)
image

Sadly, we have a Redmi Note 8 and a MI 8 Lite and both are returning the correct Phone values

@kcrg
Copy link
Author

kcrg commented Aug 10, 2020

@jsuarezruiz, Can you speed up the merge of your fixes? This bug causes a lot of crashes in my recently released project.

@PawKanarek
Copy link
Contributor

PawKanarek commented Aug 10, 2020

Devices where bug occurs, just for now more than 1 thousand crashes, and rising (2 days old app) :/
Application is bricked completely on those devices as we use Device.Idom in our core sdk.

Xiaomi Redmi Note 8 Pro
Xiaomi Redmi Note 4
Xiaomi Redmi Note 8T
Xiaomi Redmi Note 7
Xiaomi Redmi 6
Xiaomi Redmi Note 6 Pro
Xiaomi Redmi Note 5
Xiaomi Redmi 4A
Xiaomi MI 8
Xiaomi Redmi Note 8
Xiaomi Redmi 7
Xiaomi Redmi 5 Plus
rockchip X96 plus 4s.01.d4
Xiaomi Redmi 7A
Xiaomi POCOPHONE F1
Xiaomi Mi Note 10 Lite
Xiaomi Mi Note 10
Xiaomi Redmi 8
Xiaomi MI 8 Lite
Xiaomi Redmi Note 9 Pro
Xiaomi M2003J15SC
Xiaomi Redmi 8A
Xiaomi Mi MIX 2S
rockchip MXR PRO
Xiaomi Redmi Note 9S
Xiaomi Redmi 5
Xiaomi Mi 9 Lite
Xiaomi Redmi 4X
Xiaomi Redmi S2
rockchip MX10.o.00.d4
Xiaomi MI MAX 3
rockchip MBOX
rockchip MX10.11.p2.0.00.d4
rockchip H96Max RK3318
Xiaomi Redmi Note 5A Prime
Xiaomi Mi MIX 2
rockchip N5NOVA
Xiaomi Redmi Note 3
rockchip MiniA5X_Plus.hxj.p2.0.00.d4
rockchip H96 Max RK3318
Xiaomi Mi 9T Pro
rockchip A5X MAX00
rockchip X96 plus 4s.00.d4
rockchip MX9
Xiaomi Redmi 4
Xiaomi Redmi 5A
Xiaomi MI MAX
Xiaomi MI 5s
rockchip V4.SY.01.d4
Xiaomi MI MAX 2
Xiaomi Redmi 6 Pro
Xiaomi MI 9
Xiaomi Mi 9T
rockchip X88MAX.p2.0.01.d4

@lucas-zimerman
Copy link

Workaround: Set the Default value for OnIdiom

@PawKanarek
Copy link
Contributor

PawKanarek commented Aug 10, 2020

For now i found solution to override OnIdiom for every occurence in my code...

 public class OnIdiomOverride<T>
    {
        T _phone;
        T _tablet;
        T _desktop;
        T _tV;
        T _watch;
        T _default;
        bool _isPhoneSet;
        bool _isTabletSet;
        bool _isDesktopSet;
        bool _isTVSet;
        bool _isWatchSet;
        bool _isDefaultSet;

        public T Phone
        {
            get => _phone;
            set
            {
                _phone = value;
                _isPhoneSet = true;
            }
        }
        public T Tablet
        {
            get => _tablet;
            set
            {
                _tablet = value;
                _isTabletSet = true;
            }
        }
        public T Desktop
        {
            get => _desktop;
            set
            {
                _desktop = value;
                _isDesktopSet = true;
            }
        }
        public T TV
        {
            get => _tV;
            set
            {
                _tV = value;
                _isTVSet = true;
            }
        }
        public T Watch
        {
            get => _watch;
            set
            {
                _watch = value;
                _isWatchSet = true;
            }
        }
        public T Default
        {
            get => _default;
            set
            {
                _default = value;
                _isDefaultSet = true;
            }
        }

        public static implicit operator T(OnIdiomOverride<T> onIdiom)
        {
            // LOOK AT Xamarin.Essentials.DeviceInfo, because Xamarin.Forms.Device.Idiom is buggy
            if (DeviceInfo.Idiom == DeviceIdiom.Phone)
            {
                return onIdiom._isPhoneSet ? onIdiom.Phone : (onIdiom._isDefaultSet ? onIdiom.Default : default(T));
            }
            if (DeviceInfo.Idiom == DeviceIdiom.Tablet)
            {
                return onIdiom._isTabletSet ? onIdiom.Tablet : (onIdiom._isDefaultSet ? onIdiom.Default : default(T));
            }
            if (DeviceInfo.Idiom == DeviceIdiom.Desktop)
            {
                return onIdiom._isDesktopSet ? onIdiom.Desktop : (onIdiom._isDefaultSet ? onIdiom.Default : default(T));
            }
            if (DeviceInfo.Idiom == DeviceIdiom.TV)
            {
                return onIdiom._isTVSet ? onIdiom.TV : (onIdiom._isDefaultSet ? onIdiom.Default : default(T));
            }
            if (DeviceInfo.Idiom == DeviceIdiom.Watch)
            {
                return onIdiom._isWatchSet ? onIdiom.Watch : (onIdiom._isDefaultSet ? onIdiom.Default : default(T));
            }
            return default;
        }
    }

And use this like that:

<xaml 
xmlns:infrastructure="clr-namespace:hot-fix.Infrastructure;assembly=hotfix">
    <Grid.IsVisible>
            <infrastructure:OnIdiomOverride
                x:TypeArguments="x:Boolean"
                Phone="True"
                Tablet="False" />
        </Grid.IsVisible>

Its lame but for me its working, On c# usage is like this:

 if (Application.Current.Resources.TryGetValue(maxItemsPerRowKey, out var maxItemsPerRowObject) && maxItemsPerRowObject is Hot.Fix.Namespace.OnIdiomOverride<int> maxItemsPerRowOnIdiom)
            {
                maxItemsPerRow = (int)maxItemsPerRowOnIdiom;
            }

@lucas-zimerman
That would work, but my app would still crash as DeviceIdom is sometimes Uknown, Watch or Destkop. And my SDK would simply do other things that it supposed to do. Best way is to override OnIdom, so devices can use Xamarin.Essentials implementation

@PawKanarek
Copy link
Contributor

PawKanarek commented Aug 10, 2020

@samhouts i think i found problem. here you are cheking for

static TargetIdiom DetectIdiom(UiMode uiMode)
{
var returnValue = TargetIdiom.Unsupported;
if (uiMode.HasFlag(UiMode.TypeNormal))
returnValue = TargetIdiom.Unsupported;
else if (uiMode.HasFlag(UiMode.TypeTelevision))
returnValue = TargetIdiom.TV;
else if (uiMode.HasFlag(UiMode.TypeDesk))
returnValue = TargetIdiom.Desktop;
else if (SdkInt >= BuildVersionCodes.KitkatWatch && uiMode.HasFlag(UiMode.TypeWatch))
returnValue = TargetIdiom.Watch;
Device.SetIdiom(returnValue);
return returnValue;
}
with .HasFlag() method on enum that definietly should return just single value. this: https://developer.android.com/reference/android/app/UiModeManager#getCurrentModeType() isn't flagable.

@PawKanarek
Copy link
Contributor

PawKanarek commented Aug 10, 2020

Essentials do right thing. Forms don't.
https://github.com/xamarin/Essentials/blob/3ab48b96240e7844a4abd68d5801b181151dc796/Xamarin.Essentials/DeviceInfo/DeviceInfo.android.cs#L73-L85

EDIT: I've just saw that you have opened PR that fixes this. Its a over month and still not merged ... :(

@samhouts samhouts added this to the 5.0.0 milestone Aug 13, 2020
@PawKanarek
Copy link
Contributor

PawKanarek commented Aug 17, 2020

Bump, do you plan to merge this PR?
I'm just leaving this issue that ideally sums, that you don't care about community bugs as much as you could care. dotnet/maui#109

@PawKanarek
Copy link
Contributor

i found cleaner workaround for this issue. Simply set Device.Idiom property via reflection on app start

 public static class FixXamarin
    {
        public static void FixDevice()
        {
            var map = new Dictionary<DeviceIdiom, TargetIdiom>()
            {
                [DeviceIdiom.Desktop] = TargetIdiom.Desktop,
                [DeviceIdiom.Phone] = TargetIdiom.Phone,
                [DeviceIdiom.Tablet] = TargetIdiom.Tablet,
                [DeviceIdiom.Unknown] = TargetIdiom.Unsupported,
                [DeviceIdiom.Watch] = TargetIdiom.Watch,
                [DeviceIdiom.TV] = TargetIdiom.TV,
            };
            
            var deviceIdiomProperty = typeof(Device).GetProperty("Idiom", BindingFlags.Public | BindingFlags.Static);
            var mappedIdiom = map[Xamarin.Essentials.DeviceInfo.Idiom];
            deviceIdiomProperty?.SetValue(null, mappedIdiom);
        }
    }
protected override void OnCreate(Bundle bundle)
{
      FixXamarin.FixDevice(); // before init if forms using Incorrect values from Device.Idiom
      Forms.Init(this, bundle);
      FixXamarin.FixDevice(); // and after init for extra safety if Forms reseted value Device.Idiom 
}

@samhouts samhouts removed this from the 5.0.0 milestone Nov 2, 2020
@rmarinho rmarinho linked a pull request Nov 6, 2020 that will close this issue
2 tasks
@rmarinho rmarinho moved this from In Progress to In Review in vCurrent (4.8.0) Nov 10, 2020
@rmarinho rmarinho moved this from In Review to Done in vCurrent (4.8.0) Nov 10, 2020
Android Ready For Work automation moved this from In progress to Done Nov 10, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
4.5.0 regression on 4.5.0 e/2 🕑 2 i/regression in-progress This issue has an associated pull request that may resolve it! p/Android t/bug 🐛
Projects
Development

Successfully merging a pull request may close this issue.

6 participants