Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xamarin automatic sign-in credentials for Google Play's pre-launch report

I am building an Android application with Xamarin Forms 3 in Visual Studio 2017 and publishing to Google Play. Google Play offers automated testing (pre-launch report) but it cannot get past my sign-in screen.

Google Play has a place to enter the "username resource name", "password resource name", and "sign-in button resource name". I provided the Xamarin x:name for each control (txtEmail, txtPass, and btnSignIn), but apparently that is not correct, because the automated tester still can't get in and isn't even trying.

Here is my SignIn.xaml:

<Entry x:Name="txtEmail" Placeholder="email address" Keyboard="Email" />
<Entry x:Name="txtPass" IsPassword="True" />
<Button x:Name="btnSignIn" Text="Sign In" Clicked="OnSignInClicked" />

I've found these SO questions (here, here, and here) but they do not address my question from within a Xamarin Forms context. The first link seems particularly helpful but I do not know where to put that XML code.

It seems to me that Xamarin/C# is "compiled" into Android code and generates these XML files. How can I work with this process?

I understand an .APK file is just a ZIP file. Renaming the APK file I send to Google Play to .zip I'm able to browse the contents, but I do not see any XML code that makes sense.

For the moment, I added an extra "demo" button that, on click, puts a username and password into the fields and clicks the Sign In button. Google's automated tester likes this and can get in, but it's a terrible idea for human testing and I'd like to remove this workaround.

like image 879
Eric Avatar asked Aug 23 '18 12:08

Eric


2 Answers

I've faced the same issue recently. I submitted an alpha build, enabled pre-launch report and got a policy violation error saying, that sign-in credentials are not provided. As it's stated in the question, I should have provided resource names along with the test credentials, but it wasn't obvious how to do it in Xamarin.Forms project. Nevertheless, I was able to workaround it using renderers. I know, it's a bit extreme to create a separate renderer and a control just to set id, but I really didn't want to change UI significantly, so, I created 3 empty controls:

public class UsernameEntry : Entry { }
public class PasswordEntry : Entry { }
public class LoginButton : Button { }

And used each of them in corresponding places on the login screen, instead of plain entries and the button.

Then I added ids.xml file into the Android project (Resource/values/ids.xml) and added 3 ids, one for every control:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <item name="username_entry" type="id"/>
  <item name="password_entry" type="id"/>
  <item name="login_button" type="id"/>
</resources>

Finally, I created 3 renderers in the native Android project, one for each control. For example, this is how the UsernameEntryRenderer looks:

[assembly: ExportRenderer(typeof(UsernameEntry), typeof(UsernameEntryRenderer))]
namespace Android.Renderers
{
    public class UsernameEntryRenderer : EntryRenderer
    {
        public UsernameEntryRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);

            if (Control != null)
            {
                // here I can reference the id created previously in ids.xml file
                // and set the native android id property
                Control.Id = Resource.Id.username_entry;
            }
        }
    }
}

This is not the perfect way, but it worked well in my case. I didn't need to change the UI drastically or to introduce some hidden UI elements. Hopefully, this will be helpful for anyone facing similar problem.

like image 151
Yehor Hromadskyi Avatar answered Nov 10 '22 11:11

Yehor Hromadskyi


Inspired by Yehor's answer, I was able to come up with a solution that uses Xamarin Effects without using custom controls.

First add ids.xml file into the Android project (Resource/values/ids.xml) and add 3 ids, one for every control. Make sure the Build action is set to AndroidResource and the Custom Tool is set to MSBuild:UpdateGeneratedFiles so the C# files can be generated automatically for the styles:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <item name="username_entry" type="id"/>
  <item name="password_entry" type="id"/>
  <item name="login_button" type="id"/>
</resources>

Then define an enum so the effects can be defined easily:

public enum CustomIdType
{
    None,
    Username,
    Password,
    LoginButton
}

For the Android custom renderer, implement the following assign the android ID based on the custom effects.

[assembly: ResolutionGroupName("CustomRendererHandler")]
[assembly: ExportEffect(typeof(AndroidCustomIdEffect), "CustomIdEffect")]
namespace YOUR_ANDROID_NAMESPACE
{
    public class AndroidCustomIdEffect : PlatformEffect
    {
        protected override void OnAttached()
        {
            // get autofill effect
            var effect = (CustomIdEffect)Element.Effects
                .FirstOrDefault(e => e is CustomIdEffect);

            // assign android ID if custom ID effect is not null
            if (effect != null) {
                switch (effect.Type) {
                    case CustomIdType.Username:
                        Control.Id = Resource.Id.username_entry;
                        break;
                    case CustomIdType.Password:
                        Control.Id = Resource.Id.password_entry;
                        break;
                    case CustomIdType.LoginButton:
                        Control.Id = Resource.Id.login_button;
                        break;
                }
            }
        }
        
        protected override void OnDetached()
        {
        }
    }
}

Then define a Routing Effect in your shared code to consume the Android custom renderer:

public class CustomIdEffect : RoutingEffect
{
    public CustomIdType Type { get; set; }

    public CustomIdEffect() : base("CustomRendererHandler." + nameof(CustomIdEffect))
    {
    }
}

Finally, to consume the Effects in your login page, simply add the Effects to the controls in XAML:

<Entry PlaceHolder="Enter Username">
    <Entry.Effects>
        <local:CustomIdEffect Type="Username"/>
    </Entry.Effects>
</Entry>

<Entry PlaceHolder="Enter Password">
    <Entry.Effects>
        <local:CustomIdEffect Type="Password"/>
    </Entry.Effects>
</Entry>

<Button Text="Login">
    <Button.Effects>
        <local:CustomIdEffect Type="LoginButton"/>
    </Button.Effects>
</Button>

After that Google Pre-launch Report should be able to get pass the login screen if you provide the right sign-in credentials.

like image 36
Patrick Avatar answered Nov 10 '22 11:11

Patrick