Android - James Montemagno https://staging.montemagno.com/tag/android/ https://staging.montemagno.com/content/images/2017/08/MonogramIcon_Orange_NoBuffer.png Android - James Montemagno https://staging.montemagno.com/tag/android/ Hugo -- gohugo.io Tue, 13 Oct 2026 20:48:55 GMT 60 Using Java SDK 11 on Xamarin Android Builds on Azure DevOps & GitHub Actions https://staging.montemagno.com/java-sdk-11-azure-devops-github-actions/ 6578dab01270dd000126e748 Tue, 12 Dec 2023 22:45:42 GMT Everything is building great in your CI/CD pipeline and then BOOM your build fails for no reason. You didn't change anything in your code!?!?! That was me this morning as I was updating my InAppBilling Plugin for .NET. I got hit with this beauty:

error XA0031: Java SDK 11.0 or above is required when using $(TargetFrameworkVersion) v13.0

What is going on?!?!? Starting with Visual Studio 2022 17.8, which was just released, the Xamarin.Andriod tooling requires Java SDK 11.... but the default images on Azure DevOps still have the default set to an earlier version. No worries! as you can manually set this with the Java tool installer task as the first step in the process.

Here is the YAML:

steps:
- task: JavaToolInstaller@0
  displayName: 'Use Java 11'
  inputs:
    versionSpec: 11
    jdkArchitectureOption: x64
    jdkSourceOption: PreInstalled

If you are using GitHub Actions, you can use the actions/setup-java action first: https://github.com/actions/setup-java

After that you are good to go!

]]>
Developing Android Apps on Windows Arm Devices https://staging.montemagno.com/developing-android-apps-on-windows-arm/ 644961ecfaa091003df357e4 Thu, 27 Apr 2023 23:00:11 GMT

I have gone all in on Arm devices! My main MAC machine is a Mac Book Air M1 and now my main driver at work is a Windows Dev Kit 2023 (aka Project Volterra). One tricky thing for developers is getting all the software we use to work properly on Arm. Things have come a long way since the original release of Arm devices for Mac and Windows. I have a full setup guide for my M1 for Xamarin and now .NET MAUI development, but what about Windows?

Visual Studio 2022 recently released a full native Arm version and they have been adding more workloads for developers to use. .NET MAUI was recently introduced, which got me excited but I right away ran into an issue, which was that there are no compatible Android emulators for Windows Arm and apparently no plans from Google :(. So, what are our options? Two come to mind.

Deploy to a device

Well, it may be obvious, but you can just plug in a device and use something like Vysor to mirror your device. I am always a fan of deploying and testing on devices as you have full access to everything including Google Play services. As a bonus you don't drag down any resources on your main machine. If you don't have a device, you are still in luck!

Windows Subsystem for Android

That's right! I have blogged and talked about it a bunch, but the Windows Subsystem for Android(WSA) is a great way to deploy and debug applications on any Windows 11 machine. It has expanded into more regions since its first release, which is awesome. If you haven't heard about WSA, it is a mode that you can enable that allows you to run Android applications directly on your Windows machine. There are limitations of course, the largest being no access to Google Play services, but for a lot of developers this will get you off the ground running. Checkout my full video:

If you are using Visual Studio 2022 there is a great extension that will help you get up and running and automatically connect.

Once you have it all set up, it is ready and available!

So, there you go! Two ways to get deploying Android apps if you are developing on a Windows Arm device.

]]>
Goodbye Android Emulators, the Windows Subsystem for Android is Here! https://staging.montemagno.com/goodbye-android-emulators-windows-subsytem-for-android-is-here/ 6172b75df2a01a003b1a2b76 Fri, 22 Oct 2021 17:39:00 GMT

Android Emulators have been a pain in everyone's side for pretty much ever. Intel HAXM is great, but limited to Intel, then there were custom emulators like Xamarin Android Player and Genymotion, but required additional installs, and finally Hyper-V support, which brought with it AMD support, and side by side Docker support as well. But now, Windows 11 is here and so is a brand new Insiders Preview that brings with it Android app support, which is CRAZY AWESOME. More so is that you can use the new Windows Subsystem for Android to develop, debug, and deploy your Android apps from your favorite IDE! Let's walk through what you need to do to get it all setup. However, if you want to just watch a video I have that for you as well!

Windows Subsystem for Android

So, what is this thing? Well, it is like the Windows Subsystem for Linux enabling you to have a full subsystem available for Android instead of Linux. Now at this point, you don't have access to 100% Android. It is not a device just sitting there, but instead think of Windows being the OS that can run Android apps. There is no Android launcher because Windows is the launcher of the apps.

Get Windows Insiders Beta

The first thing that you will need is the Windows Insiders Beta build (not Dev) that matches Windows 11 Build 22000.xxx. You can read all about how to turn on Windows Insiders on their blog.

Windows 11 21H21
Windows 11 21H21

Install Windows Subsystem for Android

Now it is time to install the Windows Subsystem for Android, which also comes with the Amazon App Store. This is done directly from the Microsoft Store on Windows and will require a reboot and some final installation steps.

App Store Listing for WSA
App Store Listing for WSA

Once it is installed you will want to search and pin the Windows Subsystem for Android app.

Search for Android
Search for Android

Developer Mode Setup

Inside of the WSA app you will find all sorts of great options, but a few things to turn on are "Continuous" mode, so the subsystem is always ready to go.

Then, flip on Developer mode:

Notice here that once you turn on developer mode you get instructions on how to connect to adb.

ADB Connect

I am going to steal this part directly from the documentation. To connect to the Windows Subsystem for Android VM for debugging, you have two options:

Recommended Method:

  • Use localhost for connecting to debugging. The IP address of the localhost is: 127.0.0.1:58526. Windows Subsystem for Android must be running in order to connect, the best way to launch Windows Subsystem for Android is by launching an Android app that was installed with the Amazon Appstore.
  • To connect to the localhost address of Windows Subsystem for Android, enter: adb connect 127.0.0.1:58526

Alternative Method: Use the Windows Subsystem for Android Settings app to get the IP address.

  • Launch the Settings app. (Use Windows Search to select and launch.)
  • The IP address will be displayed under the IP address section. If there is no IP address being displayed, launch an Android app that was installed using the Amazon Appstore, then select Refresh on the IP address button in the Settings app.

Now that you have the IP address to connect to the Windows Subsystem for Android VM, connect using adb connect: adb connect 172.22.137.166

Originally, I had to connect through the IP address, however after a reboot and relaunching WSA, I got a firewall prompt and then 127.0.0.1 started working for me.

Debug!

Now that you are connected via ADB, WSA will just show up in your IDE of choice because it is a device!

Debug away!

Your Apps are on Windows

That is right, your apps are on Windows! You don't need to boot things up to see your app because they are literally in the start menu!

Also, when you open the app setting, they are the Android app settings! How cool is that!?!?!

Limitations

There are a few things to be aware of with WSA. There is no Google Play Services, so if you are testing out maps or other services that is not going to work. It isn't an emulator, which means you don't get to control the skin or conditions under where your app runs since it is running under WSA, not an emulator. Not every sensor is connected, for example it looks like Bluetooth isn't forwarded to WSA from your Windows machine. I also haven't found a way for apps running under WSA to talk about to the local machine for debugging web servers, but I am sure it is a matter of time before someone figures it out.

However, this is the very first preview of WSA and I am extremely happy with it! I love it and never want to open an Android emulator ever again!

Checkout my videos on YouTube

]]>
Switching from Android to iPhone - 1 Month Later https://staging.montemagno.com/switching-from-android-to-iphone-1-month-later/ 5f2245007308790039e50e26 Thu, 30 Jul 2020 17:42:23 GMT

Last month for my birthday I decided to make the big switch from Android to iPhone after being an Android exclusive user for nearly 11 years. My life has mostly been infused with Android/Google devices, which have included my long love of Android phones, Android TV, and Android based smart displays. For the last two years I have been rocking the Pixel 2 XL, which is been a solid device with an amazing camera. I have been waiting and waiting for Google to release something great in their main or budget Pixel lines, but months went by and nothing was announced. Then Apple decided to launch a new version of the iPhone SE for just $400. So, I decided to put my Best Buy reward certificates to use and just like that I activated my new iPhone SE 2nd Gen as my main device.

Listing of an iPhone SE 2nd Gen for $400 on Best Buy

It has been one month since I made the full switch, so let's talk about it.

iPhone SE (2nd Gen)

Finally a device that isn't huge! I have carried around the Pixel 2 XL for over 2 years and I am officially tired of huge phones. My wife has the iPhone 11 Pro and it is a great device, but I think that the size of the iPhone SE is near perfect. Yes, there are large borders on the top and bottom, and yes there is no FaceID, but that is alright. The borders don't bother me and I love a good old fashion TouchID sensor! I will say though that iOS does not make it easy to enter your PIN when you are riding a bike with gloves on. I often have to pull over and unlock the device or pre-set everything before I take off. I am still new to iOS so maybe someone can ping me on Twitter and tell me how to work this device. Beyond that the device size, weight, and shape all feel great to me. The only real negative is that the glass back is super slippery and you have to watch it or it will go sliding off the couch.

Performance

The iPhone SE (2nd Gen) is a snappy device. It has the same exact A13 chip that the iPhone 11 devices have. There hasn't been anything that I have thrown at it where it felt slow at all.

Single Camera

I have been spoiled with the Pixel 2 XL and its amazing single lens camera. Every new device on the market now has multiple cameras, but not the iPhone SE (2nd Gen)... single camera it is! It is a 12MP Wide camera with a ƒ/1.8 aperture and digital zoom up to 5x. Even though it is very similar to the Pixel 2 XL (12.3 MP/ƒ/2) the differences are noticeable. Daylight photos seem to be pretty solid overall with very vibrant?exposed?intense? colors. Notice in these photos that our walls are completely white and yet there is a pink to them, but Pikachu looks absolutely fantastic. The Pixel 2 XL looks a bit more washed out, but the details on the Nintendo DS neon are very nice.

Side by side iPhone SE vs Pixel 2 XL pictures of Pikachu, pokeball, and neon sign on wood

Now, let's go to night time with the lights off in the room. The difference is pretty shocking as the Pixel 2 XL really blows it out of the water.

Side by side iPhone SE vs Pixel 2 XL pictures of Pikachu, pokeball, and neon sign on wood at night

That being said it is a $400 phone and my wife's iPhone 11 Pro takes stunning night time shots. I am still a bit disappointed to be taking a step back with the SE.

iMessage

Is $400 worth a great messaging app? Yes, yes it is.

imessage icon

I am not really blown away by iMessage's UI design, but I am very impressed with the capabilities. If everyone in your life has Android devices then this isn't a big deal, but in my life I am the only Android user. Moving to iPhone and iMessage means that I am finally a blue bubble and with that comes amazing support for sending photos, videos, and super fast messages even over Wi-Fi. Yes, Google has started to roll out RCS messaging for Android devices, but it isn't encrypted by default and is on select devices and networks. Unless Apple adopts RCS for their SMS protocol there isn't much use. I am now finally able able to react to messages, share photos, and get that little chat bubble that someone is responding. Delightful.

Apps

The big debate between operating systems is usually around the apps available and how good they are. Well it is 2020 and pretty much every app is available on both iOS and Android. When it comes to UI design and performance of apps I usually hear that Android lags far behind, but I have not found that to be very accurate. Nearly every app is fairly identical and offer the same features between the two operating systems. I haven't really found a third party app that is "that much better" on iOS. That said, I think that this is a good thing overall for consumers. Switching between devices is super simple as you just need to re-download the apps you want and sign into your services. In fact, if you are switching from Android you are in luck because a bulk of Google's apps are available on iOS.

Google Apps

Let's talk about those Google apps, because while it is awesome they are here, they are actually a bit lack luster. Looking at my phone I have the following Google apps:

  • Gmail
  • Google Authenticator
  • Google Keep
  • Google Keyboard
  • Google Photos
  • Google Play Music
  • Google Voice
  • Google Wi-Fi
  • YouTube

Wow, that is a lot of Google! I am really glad that they are all here so I can access my previous subscriptions and some of my favorite features from Android. On the flip side since Google decided to implement a single design paradigm not using native controls their apps feel a bit out of place with the rest of the apps, but more importantly a bit "slow". I say "slow" in quotes because the apps are not slow, but they just feel a bit "sluggish". Not slow sluggish, but just off in some way. Things feel like they don't react or scroll exactly like they should even compared to their Android counter parts. It is not a deal breaker, but I will most likely over time migrate away from some of the apps.

One thing that is a deal break and is very upsetting is lack of Picture-in-Picture and proper background PIP for YouTube on iOS! This is a killer feature on Android for YouTube Premium members (like myself) and I can't believe that Google hasn't added it.

Android-Picture-in-Picture

I am still on iOS 13 and I know that Apple is adding official PIP support in iOS 14, so hopefully this gets fixed up.

Notifications

Let's talk about iOS notifications for a quick second because they are a mess. My friend Jesse told me to get ready for just how bad that are and I apparently was not prepared enough. I am not trying to say that Android notifications are the best thing since sourdough bread, but they have gotten pretty good over the years. iOS has alerts and a notification center that sometimes show the same or different things, and group items together and you can't really see them all well. There is such little information in the notifications and images are so tiny I am not sure why they exist. There is really great granular control over notifications compared to Android, but you might as well turn off as many as you can because they aren't going to be useful at all.

iOS-Notifications

The notifications are also very inconsistent when it comes to taking actions from them. For example you can type a reply to an iMessage, but if you don't finish you may accidentally dismiss the message. You can't really have a full conversation without opening the app and the functionality is also pretty limiting in typing. It just feels a bit half baked for iOS at this point. Maybe iOS 15.

Fitness Tracking

I have been on a security kick in the last year and have deleted a plethora of accounts where I willingly gave GPS data, photos, check-ins, and other data to companies. That is why I am sticking with Apple Maps for my mapping solution on my iPhone and it really isn't that bad! However, when it comes to fitness tracking I was spoiled that the Google Fit app tracked a lot of things for me automatically such as bike rides. This was very useful after I deleted Strava earlier this year. I was excited to use the Health app from Apple as I knew it would be secure, but after taking my first bike ride and realizing that it only tracked steps out of the box I was disappointed. I took to Twitter to confirm this with all of you, and it seems that my suspicion was correct and I would need to either install a third party app or get an Apple Watch....

My-new-Apple-Watch

Apple Watch Arrives

So, I ordered an Apple Watch Series 3 (GPS, 38mm). It was actually a decent price and I told myself I would turn off all notifications and solely use it as a fitness tracker. Thus far I have and I really really like it! I don't even like watches, but the 38mm feels just about right and isn't very intrusive at all as I type. The integration with iOS is also very solid compared to my days with Android wearOS devices.

Family

The last thing I want to talk about is how the iPhone integrated nicely with my family. Like I said earlier I was the only Android user in our family and that meant I was always the reason group messaging went over normal SMS, the reason videos/photos came through at low resolution, and the only family member that couldn't FaceTime. In the first 30 days of using the iPhone as my primary device I no longer have these issues and it feel pretty nice.

Wrap-up

There you have it. The first 30 days are down and I am pretty excited for the next 30 days and beyond with this device.

]]>
Shrink Your Android App Size with One Setting https://staging.montemagno.com/shrink-your-android-app-size-with-one-setting/ 5ec9f85a9894740045506cee Tue, 26 May 2020 19:18:00 GMT When you are developing Android apps your app size can grow really fast.  We can get so caught up in the day to day that we forget how important it is to keep down the app size of our app. It may not seem like a 20MB APK is that much, but when you have limited internet or reception it can be a real pain.

As an Android developer you have to develop for multiple ABIs, add a ton of resources and translations, and of course you have you have all of your source code and dependencies. You may have a lot of other settings turned on in your app such as startup tracing, AOT, and obfuscation and this can all lead to larger sizes. But do not fear because App Bundles are here to save you!

Hopefully this is not new to you, but if it is, well you are in for a treat because Android App Bundles solves a bunch of issues when it comes to packaging APKs. When you use App Bundles you package up your app into a special format and Google Play will then optimize your app for your users on demand. What does this mean? It means that with one tiny little setting your app is greatly reduced in size.

I just launched Island Tracker for Animal Crossing and it was my first app that I published with App Bundles and my mind was blown away. When I package up my APK by itself I package for both arm64-v8a & armeabi-v7a. I also use a lot of packages including Sharpnado, Syncfusion, App Center, Xamarin.Forms, Xamarin.Essentials, PancakeView, and more. I also have turned on startup tracing, so for all of that my app is around 30MB as an APK.

When I toggle on App Bundles and submit it to Google Play.... 12.1-12.5MB!!!!!!! That is a reduction of 67%!

That is a crazy reduction and it took me all of 10 seconds to enable because I am using App Center to build my app. When you setup your configuration, simply turn on "Build an App Bundle":

Just like that you are good to go! You of course you can do this manually and publish it or even turn it on in other CI services, but you need to do this ASAP if you ever thought your app was to big.

]]>
Setting Android Status Bar Background & Icon Colors https://staging.montemagno.com/setting-android-status-bar-background-icon-colors/ 5dd3091684e2c7003870dfcf Thu, 05 Dec 2019 20:15:00 GMT Over the past few months, I have been attempting to figure out the best ways to implement a light theme and dark theme in my Hanselman.Forms app. Working closely with my friend Kym Phillpotts we came up with a very nice theme changer for Xamarin.Forms, that allowed us to change all of the colors dynamically. The result is a very nice looking application that allows the users to pick their theme or use system defaults.

Picking themes in Hanselman.Forms between light and dark theme

One thing that bugged me in the original implementation for Android, was the status bar color that was a single dark blue color. Modern apps will set the status bar to match the background color when toggling between light and dark. Since we are allowing the user to set the theme, we must also set the color of the status bar.

This can be accomplished with the following code:

public void SetStatusBarColor(System.Drawing.Color color, bool darkStatusBarTint)
{
    if (Build.VERSION.SdkInt < Android.OS.BuildVersionCodes.Lollipop)
        return;

    var activity = Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity;
    var window = activity.Window;
    window.AddFlags(Android.Views.WindowManagerFlags.DrawsSystemBarBackgrounds);
    window.ClearFlags(Android.Views.WindowManagerFlags.TranslucentStatus);
    window.SetStatusBarColor(color.ToPlatformColor());
}

This uses my Current Activity Plugin to get the Window of the app, and also Xamarin.Essentials Color Converters to set the color to light or dark.

Screenshot of the status bar in dark theme with matching color

This works really great unless I set it to a light color where the icon colors remain white.

Screenshot of the status bar in light them where icons are white too

We can also control this with the following code:

if(Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.M)
{
    var flag = (Android.Views.StatusBarVisibility)Android.Views.SystemUiFlags.LightStatusBar;
    window.DecorView.SystemUiVisibility = darkStatusBarTint ? flag : 0;
}

Now it looks beautiful, and we are in full control!

Screenshot of the status bar in light them where icons are dark color

Get the code

All of the source code for Hanselman.Forms can be found on GitHub and you can watch me code live on Twitch. I also archive all the videos on my YouTube channel.

]]>
Investing Time in the Xamarin Linker for Smaller App Sizes https://staging.montemagno.com/investing-time-in-the-xamarin-linker-for-smaller-app-sizes/ 5dd314f084e2c7003870dfd4 Mon, 02 Dec 2019 19:00:00 GMT When developing an app, 99% of our time goes into writing the code, testing the functionality, and making small UI tweaks to please our end users. Most of this is done in debug mode which optimizes compilation and app size for speed, which makes sense. When we flip that flag to Release with the default settings, we often pay very little attention as to what is happening to our app. We get a nice APK or IPA and ship it up to the app store and we are done. However, if we just spent a little time fine tuning our settings, we could really reduce the overall app size. I recently sat down for 1 hour and reduced Hanselman.Forms by 4 MB on Android by optimizing the linker in Full Link mode. When I flipped on Startup Tracing for comparison the app size was reduced by 10MB!

App Before and After Optimizing the Linker

I have written many times about using & understanding the linker, however today I want to talk about how to debug the linker.

My recommendation is to flip on Link "Sdk & User Assemblies" and run your application. See if everything works or if your application crashes. Most likely, it will crash and you will see an error such as:

Getting: System.InvalidOperationException: 'A suitable constructor for type 'Shiny.Jobs.JobManager' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.'

If you see this, it means it is time to debug through.

Debug the Linker

You should NEVER have the linker on when debugging an app.... unless you are debugging the linker settings! So, turn on that linker in debug and see what happens. Like I said above, you will probably see a crash with the linker stripping away too much. That is usually because of reflection or the main project not referencing items from your .NET Standard project. Hopefully library creators have optimized their libraries to handle the linker, however if they haven't you can easily setup assembly skipping in your csproj file. Here is what it looks like to completely skip over Xamarin.Forms:

// iOS
 <MtouchExtraArgs>--linkskip=Xamarin.Forms.Platform.iOS --linkskip=Xamarin.Forms.Platform --linkskip=Xamarin.Forms.Core --linkskip=Xamarin.Forms.Xaml --linkskip=Samples</MtouchExtraArgs>

// Android
<AndroidLinkSkip>Xamarin.Forms.Platform.Android;Xamarin.Forms.Platform;Xamarin.Forms.Core;Xamarin.Forms.Xaml;FormsViewGroup;</AndroidLinkSkip>

We can easily add assemblies into this list until the app runs. Here is what Hanselman.Forms looks like:

<AndroidLinkSkip>Xamarin.Forms.Platform.Android;Xamarin.Forms.Platform;Xamarin.Forms.Core;Xamarin.Forms.Xaml;FormsViewGroup;Shiny.Core;MediaManager;MediaManager.Forms;</AndroidLinkSkip>

Don't Skip, Inform the Linker

Have you ever wondered why you have all this those Init calls in your app's startup?

Forms.SetFlags("CarouselView_Experimental");
Forms.Init(this, bundle);
FormsMaterial.Init(this, bundle);
Xamarin.Essentials.Platform.Init(this, bundle);
CrossMediaManager.Current.Init(this);
ImageCircleRenderer.Init();
Android.Glide.Forms.Init(this);

This is to help the linker become aware that you are using this library, so don't remove it. If a library doesn't have an Init, you can easily inform the linker you are using a class that is getting linked out like this:

var pancake = typeof(PancakeView);
var circle = new CircleImage();

Depending on how complex the dependency is then you may want to link skip assemblies out since we don't have access to the source code.

Custom Preserve Flags

If you are getting exceptions in classes that are in your own code then you can add a special attribute to the class to tell the linker to preserve it.

In your shared code (usually .NET Standard) add a new file called PreserveAttribute:

public sealed class PreserveAttribute : System.Attribute
{
    public bool AllMembers;
    public bool Conditional;
}

Now, on any class that is getting stripped out add the following attribute:

[Preserve(AllMembers = true)]
public partial class Tweet
{
}

Keep doing this until your code is linker safe and everything is working as expected. In no time at all you will have your app ready to be fully linker optimized and the app size will be reduced :)

Cleanup Before Committing Code

When debugging linker issues, I like to have the same settings mirrored acrossed debug and release. However, leaving these settings on in debug will slow down your builds and will turn off XAML Hot Reload. So, NEVER turn on linking in debug unless you are doing this.

Read More

Now that you have an idea of how to debug and customize yoru code for the linker be sure to read the in-depth documentation for even more goodies. I would also read the following blogs on shrinking your app size, optimizing Android builds, and faster startup times with startup tracing.

]]>
Azure DevOps Settings for Xamarin iOS 13 and Android 10 Apps https://staging.montemagno.com/azure-devops-settings-for-xamarin-ios-13-and-android-10-apps/ 5dd43e2784e2c7003870dfe2 Wed, 20 Nov 2019 20:12:00 GMT

Settings up continuous integration can be tricky when there are new versions coming out. Recently Visual Studio 2019 updated to support Xcode 11, iOS 13, Android 10, .NET Core 3, and C# 8. What a whirlwind of new updates that CI servers got all around the same time! This means that as you were updating your apps your hosted machines also got updated and probably broke your builds :(. My good friend Jonathan Peppers wrote an amazing tool called Boots that allows you to manually install dependencies and previews of Xamarin and Mono.

Boots task in Azure DevOps installing Mono 6.4.0

This is great, but it also means that your build takes a little bit longer to manually install items. Did you know that Azure DevOps has all of the latest bits installed on their hosted agents and even have simple scripts enabling you to decide what version of Xamarin and Mono to run? Here is what you need to know to build your apps in Azure DevOps with C# 8, iOS 13, and Android 10.

Use macOS 10.14

Be sure that you select the latest version of macOS to build your iOS and Android apps on. You can simply specify macOS-latest or macOS-10.14 in your Pipeline settings.

macOS-version selection in Azure DevOps

You can find what software is installed here.

Install .NET Core

This may sound funny to install .NET Core for a Xamarin build, but the latest reference assemblies and build system require it. You can find what version of .NET Core is compatible with Visual Studio for Mac on the docs site.

You can add the Use .NET Core task to install .NET Core 2.2.x or 3.0.x. Either work just fine.

.NET-Core version task in Azure DevOps

Set Xamarin & Mono Version

This next task is very important as it will tell the build agent what version of Xamarin and Mono to use when compiling the app. I like to define a variable with the version number in the format Major_Minor_Revision. For the latest we will use 6_4_0:

Variable in Azure DevOps specifying Mono 6.4.0

Then we will add a task to run a bash script to set the version of Xamarin. This is done with the following inline script:

sudo $AGENT_HOMEDIRECTORY/scripts/select-xamarin-sdk.sh $(MonoVersion)

Bash script setting the version of Xamarin to use

That is it! Now you are good to go to build your apps with C# 8, iOS 13, and Android 10!

More DevOps!

Looking for more DevOps? Checkout my Channel 9 series on DevOps for Mobile Developers where I walk you through everything you need to know about DevOps with my friend Abel.

]]>
Is the MacBook Pro 16-inch the Ultimate Developer Laptop? https://staging.montemagno.com/is-the-macbook-pro-16-inch-the-ultimate-developer-machine/ 5dcddce884e2c7003870dfbc Sat, 16 Nov 2019 00:30:31 GMT

It seems as though Apple has finally fixed the keyboard on the latest MacBook 16-inch and as a bonus, brought back the escape key! This seems to be what everyone is talking about, but what is really important is the return to PRO for the MacBook Pro. What I mean by this is that finally we can fully spec out a MacBook Pro with as much RAM and SSD space that we could ever want and at a decent price. If you head over to the configuration page you will be able to spec out your MacBook Pro with up to 64GB of RAM and 8TB of SSD! That is right 8TB!!!!!!!!

This is a big deal for developers that need a super powerful laptop on the go, especially if you are doing mobile development for iOS and Android. More importantly, is that for the first time I think we have a MacBook Pro that can actually handle running macOS and Windows (in Parallels) without compromise! Yes, you will be sharing the GPU, but there are 8-cores built into this puppy! I actually don't think you need to fully spec out the machine either. Here is the build I would go for:

That is the upgraded 8-core i9, 64GB RAM, and 2TB SSD, all for just around $4,000! That does seem expensive, but it is basically 3 laptops in 1! I am very impressed with this offering and could feel very confident dedicating 32GB of RAM to Parallels, for that super butter smooth development experience between macOS and Windows!

Yes, $4,000 is very expensive and obviously you do not need to spend this much on a machine. However, if you are looking for a machine to last the next 6-8 years and do all of your heavy lifting on macOS and Windows 10 then it is a pretty solid option.

What do you think? Leave a comment below if you think this is genius or if I am crazy.

]]>
Opt-out of Dark Mode on iOS, Android, UWP, and macOS https://staging.montemagno.com/opt-out-of-dark-mode-on-ios-android-uwp-and-macos/ 5da372290980fc00385d9b78 Mon, 14 Oct 2019 19:17:00 GMT Everyone can't stop talking about dark mode now that iOS 13 and Android 10 are officially released. To my surprise I have actually really been enjoying dark mode on my desktop and mobile devices. I really want to go through all my apps and add proper dark mode support, however I just haven't had the time. When running apps on iOS 13 or macOS and flipping on dark mode can make your app look.... not so great:

image_from_ios

This can depend on the framework you are using, hard coded values, or a bunch of other things. You may be in this situation or perhaps you have a completely custom theme and you don't want the operating system to touch it. Well, you are in luck because you can opt-out!

iOS

You have two options here. The easiest is to update your Info.plist and add a new entry named UIUserInterfaceStyle. You can manually set this to Light or Dark to apply a theme to your entire app.

<key>UIUserInterfaceStyle</key>
<string>Light</string>

If your app is basically ready to go and just a few screens require a specific mode you can set the OverrideUserInterfaceStyle propety. This can be done on any UIView, UIViewController, or UIWindow.

public override void ViewDidLoad()
{
    base.ViewDidLoad();
    if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
        OverrideUserInterfaceStyle = UIUserInterfaceStyle.Light;
}

macOS

Similar to iOS you can add a very special key to your Info.plist called NSRequiresAquaSystemAppearance and set it to true:

<key>NSRequiresAquaSystemAppearance</key>
<true/>

Android

Android is actually really easy as you have to opt-in for the dark mode to apply to your application. Traditionally, you have always set a light or dark mode for your app in your app's styles. Every Xamarin app defaults to a light theme:

<style name="MainTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">

You have to opt in by setting the theme to DayNight:

<style name="MainTheme.Base" parent="Theme.AppCompat.DayNight.DarkActionBar">

This also works if you are using MaterialComponents theming:

<style name="MainTheme.Base" parent="Theme.MaterialComponents.DayNight">

UWP

UWP is also similar to Android as you have to opt-in for the system wide defaults. Traditionally in your App.xaml file you would set a requested theme:

<Application
    RequestedTheme="Light">

</Application>

This means your app will always use light theme no matter what. To opt-in you will need to remove this property completely.

Checking Requested Theme

It is actually pretty simple to check theme of the app. It has been blogged abotu a lot, and I have even submitted a pull request to Xamarin.Essentials that hightlights it, so take a look. There are also great docs for iOS, Android, and UWP to help guide you.

]]>
Add ASP.NET Core's Dependency Injection into Xamarin Apps with HostBuilder https://staging.montemagno.com/add-asp-net-cores-dependency-injection-into-xamarin-apps-with-hostbuilder/ 5cc7df16b2f3d500bff4c9d0 Tue, 18 Jun 2019 21:53:00 GMT

Dependency injection(DI) and inversion of control (IoC), have been a recurring theme in questions that I have received over the last six years. Sometimes it is around using constructor injection(), simple service containers, and often around full frameworks such as Prism or MVVM Light. I will be honest with you, I have never been a fan of DI/IoC when building mobile apps. My main reason is that there has never been any official pattern or recommendations from Google or Apple (or Microsoft/Xamarin) when building Android and iOS apps. Also, I have always been worried around startup performance in my apps and didn't want to introduce another dependency. However, it is 2019 and I think that my concerns have finally been resolved after talking with my friend Glenn Condron, and here is why:

  1. There is a new official library from Microsoft that handles everything for you.
  2. Using this model gives you other powerful features like logging, HTTPClientFactory, and more.
  3. These devices are crazy powerful and the frameworks we use such as Xamarin.Forms, have really matured.

So, what is this magical library? It is the Microsoft.Extensions framework, but specifically the Generic Host framework that lives inside of Microsoft.Extensions.Hosting. It is a platform independent implementation of ASP.NET Core's extensibility framework.

Microsoft.Extenions

The cool part about this library is that if you already have been building ASP.NET Core applications, then you already know exactly how to use the library. Additionally, it means that just about every other library that extends it works with your Xamarin apps like app configuration, logging, HTTPClientFactory, Polly, and more! Let's start with the basics and first setup a HostBuild, which is the base for the Extensions library and then setup some dependency injection with constructor injection.

Installation and Setup

The first thing to do is to install the v3 pre-release Micosoft.Extensions.Hosting NuGet into all of our projects. This will give our projects access to the base foundation and also includes the ability to use the built-in logging framework and application configuration via a json file.

With the library installed we will create two files in our shared code (also if you are building a single iOS/Android app this will also work):

Startup.cs

Will host all of our HostBuilder configuration and setup all of the dependencies in our application. Here is the base foundation for the file with all of the using statements we will need:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Xamarin.Essentials;

namespace MyApp
{
    public static class Startup
    {
        public static IServiceProvider ServiceProvider { get; set; }
        public static void Init()
        {

        }

        static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
        {

        }
    }
}

appsettings.json

This file will need to be set as an EmbeddedResource and will host configuration properties and settings. Here is a generic default appsettings file:

{
  "Hello": "World",
  "Environment": "Development",
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

Host Builder setup

The extensions library has a few main concepts to understand:

  1. HostBuilder: Acts as a builder for adding services (logging, app config, etc.) onto the app.
  2. IServiceProvider: Handles all dependency injection & acts as a service locator and comes from the HostBuilder.

Our Init method must be called from either the main Xamarin.Forms or platform specific app startup. We will fill in the Init method by getting a stream to the app settings file so the HostBuilder can read it, configure services, and setup the built in logging service:

var a = Assembly.GetExecutingAssembly();
using var stream = a.GetManifestResourceStream("MyAssemblyName.appsettings.json");

var host = new HostBuilder()
            .ConfigureHostConfiguration(c =>
            {
                // Tell the host configuration where to file the file (this is required for Xamarin apps)
                c.AddCommandLine(new string[] { $"ContentRoot={FileSystem.AppDataDirectory}" });

                //read in the configuration file!
                c.AddJsonStream(stream);
            })
            .ConfigureServices((c, x) =>
            {
                // Configure our local services and access the host configuration
                ConfigureServices(c, x);
            })
            .ConfigureLogging(l => l.AddConsole(o =>
            {
                //setup a console logger and disable colors since they don't have any colors in VS
                o.DisableColors = true;
            }))
            .Build();

//Save our service provider so we can use it later.
ServiceProvider = host.Services;

Configure Dependencies

We have setup our HostBuilder and called ConfigureServices, but we haven't done anything yet. So let's do it!

Let's say we have the following code for a ViewModel and a service that it needs to use:

public interface IDataService
{
    void DoStuff();
}

public class MyDataService : IDataService
{
    // We need access to the ILogger from Microsoft.Extensions so pass it into the constructor
    ILogger<MyDataService> logger;
    public MyDataService(ILogger<MyDataService> logger)
    {
        this.logger = logger;
    }

    public void DoStuff()
    {
        logger.LogCritical("You just called DoStuff from MyDataService");
    }
}

public class MockDataService : IDataService
{
    // We need access to the ILogger from Microsoft.Extensions so pass it into the constructor
    ILogger<MyDataService> logger;
    public MockDataService(ILogger<MyDataService> logger)
    {
        this.logger = logger;
    }

    public void DoStuff()
    {
        logger.LogCritical("You just called DoStuff from MockDataService");
    }
}

public class MyViewModel
{
    IDataService dataService;
    public MyViewModel(IDataService dataService)
    {
        this.dataService = dataService;
    }

    public void DoIt()
    {
        dataService.DoStuff();
    }
}

Here we have a ViewModel that needs access to the IDataService and one of the implementations needs access to a ILogger. This is where the ServiceCollection comes in allowing us to configure all of our dependencies. Let's setup the ConfigureServices method in the Startup.cs again and configure these new classes.

static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
{
    // The HostingEnvironment comes from the appsetting.json and could be optionally used to configure the mock service
    if (ctx.HostingEnvironment.IsDevelopment())
    {
        // add as a singleton so only one ever will be created.
        services.AddSingleton<IDataService, MockDataService>();
    }
    else
    {
        services.AddSingleton<IDataService, MyDataService>();
    }

    // add the ViewModel, but as a Transient, which means it will create a new one each time.
    services.AddTransient<MyViewModel>();

    //Another thing we can do is access variables from that json file
     var world = ctx.Configuration["Hello"];
}

HostingEnvironment

Access Dependencies

With our HostBuilder and dependencies configured, we can now access them from anywhere. Since we only went up to the ViewModel level we will need to grab it from the ServiceProvider in the code behind from our page:

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        BindingContext = Startup.ServiceProvider.GetService<MyViewModel>();
    }

    protected override void OnAppearing()
    {
        base.OnAppearing();
        ((MyViewModel)BindingContect).DoIt();
    }
}

When we call GetService the Microsoft.Extensions framework will handle creating and injecting any dependencies including our IDataService and the ILogger that came for free just by using the library. When we call DoIt in the OnAppering our app ill print out a message to the console using the logger that will have info of what data service it was using.

HTTPClientFactory In Your Xamarin App!

That's right, if you are using Microsoft.Extensions you can use the awesome HTTPClientFactory class that is essentially HTTPClient done right! It handles caching, retries, logging, and all other sorts of awesome! It is really easy to put into your app with just a few lines of code.

First, we will need to install the v3 prerelease Microsoft.Extensions.Http NuGet to get access to HTTPClientFactory. Then in the ConfigureServices method add the following line of code:

services.AddHttpClient();

With this single line of code in place it is now time to get access to the HTTPClientFactory from one of our classes. This is handled like our interface that was passed in as a parameter to the constructor. Microsoft.Extensions takes care of everything for you:

public class MyDataService : IDataService
{
    ILogger<MyDataService> logger;
    HttpClient client;
    public MyDataService(ILogger<MyDataService> logger, IHttpClientFactory httpClientFactory)
    {
        this.logger = logger;
        client = httpClientFactory.CreateClient();
    }

    public void DoStuff()
    {
        logger.LogCritical("You just called DoStuff from MyDataService");
    }
}

If you don't need access to the IHttpClientFactory at all this code can be simplified down to just passing in the HttpClient itself:

ILogger<MyDataService> logger;
HttpClient client;
public MyDataService(ILogger<MyDataService> logger, HttpClient client)
{
    this.logger = logger;
    this.client = client;
}

What is cool now is that when you make any calls with this HttpClient you will automatically get console logging, which is super helpful:

Response

To top it off you can even extend the HttpClient beyond just logging and introduce Polly into the mix with the Microsoft.Extenions.Http.Polly library that will enable you to automatically setup retries and other really cool features. You can find an example of this in my "First App Example" with ASP.NET Core + Xamarin

Platform Specific Dependencies

At this point you are probably like WOW, this is amazing, but what about platform specific implementations that I may need to get access to. Do not fear because it is totally possible and pretty easy to setup. Instead of calling the Init from your shared code, you can call it from your iOS/Android project and pass in a configuration Action like this:

First, let's modify the Init:

public static App Init(Action<HostBuilderContext, IServiceCollection> nativeConfigureServices)
{
    // Resource code

    var host = new HostBuilder()
                    //ConfigureHostConfiguration
                    .ConfigureServices((c, x) =>
                    {
                        //Add this line to call back into your native code
                        nativeConfigureServices(c, x);
                        ConfigureServices(c, x);
                    })
                    // Logging
                    .Build();
}

Then from inside of the Android project's MainActivity and iOS project's AppDelegate you can call this Init method:

Android Example

public class MainActivity : FormsAppCompatActivity
{
    protected override void OnCreate(Bundle savedInstanceState)
    {
        TabLayoutResource = Resource.Layout.Tabbar;
        ToolbarResource = Resource.Layout.Toolbar;

        base.OnCreate(savedInstanceState);

        //setup code

        Startup.Init(ConfigureServices);
        LoadApplication(new App());
    }

    void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
    {
        services.AddSingleton<INativeCalls, NativeCalls>();
    }
}

public class NativeCalls : INativeCalls
{
    public void OpenToast(string text)
    {
        Toast.MakeText(Application.Context, text, ToastLength.Long).Show();
    }
}

iOS Example

[Register("AppDelegate")]
public partial class AppDelegate : FormsApplicationDelegate
{
    public override bool FinishedLaunching(UIApplication app, NSDictionary options)
    {
        //Init Code:

        Startup.Init(ConfigureServices)

        LoadApplication(new App();

        return base.FinishedLaunching(app, options);
    }

    void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
    {
        services.AddSingleton<INativeCalls, NativeCalls>();
    }
}

public class NativeCalls : INativeCalls
{
    public void OpenToast(string text)
    {
        var vc = UIApplication.SharedApplication.KeyWindow.RootViewController;
        var okAlert = UIAlertController.Create(string.Empty, text, UIAlertControllerStyle.Alert);
        okAlert.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null));
        vc.PresentViewController(okAlert, true, null);
    }
}

Inject The Entire App

As you can see this framework is extremely flexible and allows for optimal extensibility, but you can go even further. As I was trying out this API Glenn challenged me to see how far I could take this dependency/constructor injection madness... so I took it all the way up to the app level. That is right, I injected the entire app, pages, and view models.... because why not! Well maybe you shouldn't do this, but it is pretty cool.

Keeping the same dependencies and view models from above all we need to do is update our MainPage, App, and Init calls:

MainPage

public partial class MainPage : ContentPage
{
    //simply pass in the view model you desire!
    public MainPage(MyViewModel viewModel = null)
    {
        InitializeComponent();
        BindingContext = viewModel;
    }
}

App

There are a few ways to do this. You could pass in the page that you desire into the App constructor or you could have some logic and grab it from the ServiceProvider:

public App()
{
    InitializeComponent();
    MainPage = ServiceProvider.GetService<MainPage>();
}

Update Init

This is where it gets pretty cool, we will create Transient and Singletons of all of the services, view models, pages, and the app itself and have the method return the App intead of being a void method.

public static App Init(Action<HostBuilderContext, IServiceCollection> nativeConfigureServices)
{
    //all of the same code from before
    return ServiceProvider.GetService<App>();
}

static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
{
    services.AddHttpClient();
    services.AddSingleton<IDataService, MyDataService>();
    services.AddTransient<MyViewModel>();
    services.AddTransient<MainPage>();
    services.AddSingleton<App>();
}

Then, call Init from the LoadApplication method instead of calling Init randomly in the iOS/Android project:

LoadApplication(Startup.Init(ConfigureServices));

Just like that we dependency injected our entire application and that is crazy! If you want to try it yourself, you can grab my sample project from GitHub that shows off just about everything in this blog post.

Required Versions of Xamarin/VS

As of today, you should be able to develop and debug iOS/Android apps with all these goodies using any version of Visual Studio 2019 or Visual Studio for Mac 2019. However, if you want to use ship your app and turn on the Linker you will need to have a newer version of mono, which ships with Visual Studio 2019 16.2 (preview 2) and Visual Studio for Mac 2019 8.2 (preview). If you are on Windows you can easily install VS 2019 Preview side by side the normal release.

Watch The Build Session

I had the honor of presenting how ASP.NET and Xamarin can come closer in app model with Glenn in a 20 minute theater session. It is available on the Xamarin Developers YouTube channel:

Learn More

This stuff is super fun and I really hope that it takes off and we can encorporate it into user testing, and perhaps one day into the official templates! Until then it is easy to setup and can lead to some really clean and powerful code. Be sure to read through all of the great docs for Generic Host and through any of the extensions documentation. Enjoy!

]]>
Control Text Size on Android Bottom Navigation View https://staging.montemagno.com/control-text-size-on-android-bottom-navigation/ 5c7f7cab720b5500bfd5d63a Wed, 06 Mar 2019 16:16:00 GMT It seems as though my obsession with the Android Bottom Navigation View aka Bottom Tabs will never end! This time I got a question about controlling the size of the text when the user taps on a tab. The default behavior is to zoom in on the text:

Before

Inside of your Android project's Resources/values folder create a new file called dimens.xml if it doesn't exist. Then add the following XML to override the size of the text when it is active and not active:

<resources xmlns:tools="http://schemas.android.com/tools">
    <dimen name="design_bottom_navigation_text_size" tools:override="true">12sp</dimen>
    <dimen name="design_bottom_navigation_active_text_size" tools:override="true">12sp</dimen>
</resources>

After

Now for this to work you must be using Android Support libraries version 28 and higher. You also most likely want to turn off shifting. Enjoy and don't forget to read all about Bottom Tabs on the official documentation.

]]>
Xamarin.Forms: Fully Customize Bottom Tabs on Android & Turn Off Shifting https://staging.montemagno.com/xamarin-forms-fully-customize-bottom-tabs-on-android-turn-off-shifting/ 5c6de28e720b5500bfd5d622 Fri, 22 Feb 2019 00:15:00 GMT

It seems as if the BottomNavigationView aka Bottom Tabs on Android is one of the top things that I write about on my blogs. There was an introduction post, how to use reflection to remove shifting on native Xamarin.Android, official support in Xamarin.Forms, and most recently how to get a nice drop shadow. I am back one more time to talk specifically about how to finely tune those bottom tabs on android and stop the ridiculous default Android behavior of shifting the tab icons and text.

BottomNavFixed-1

Let's start with the problem. If you have more than 3 tabs in your bottom navigation view (aka bottom tabs) then the text of the icons vanishes and you are left with just the icons that "shift". As you can see from this example it is kind of annoying:

Shifting

What is very interesting is that not a single Google specific app that uses bottom tabs has the shift on at all. To adjust this behavior it used to be extremely difficult and required a bunch of Java reflection (which is no fun). However, in the most recent Android Support 28 libraries Google fixed it with the LabelVisibilityMode. So first things first, make sure you are set to compile against Android 9.0 and update those Support Libraries:

Compile: Compile

Libraries: Libs

Now, we just need to write a simple effect to apply to our Xamarin.Forms TabbedPage (no renderer required!).

Here is our base NoShiftEffect.cs inside of our Android project:

using Android.Support.Design.BottomNavigation;
using Android.Support.Design.Widget;
using Android.Views;
using App16.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly:ResolutionGroupName ("MyCompany")]
[assembly:ExportEffect (typeof(NoShiftEffect), "NoShiftEffect")]
namespace App16.Droid
{
    public class NoShiftEffect : PlatformEffect
    {
        protected override void OnAttached ()
        {
            if (!(Container.GetChildAt(0) is ViewGroup layout))
                return;

            if (!(layout.GetChildAt(1) is BottomNavigationView bottomNavigationView))
                return;

            // This is what we set to adjust if the shifting happens
            bottomNavigationView.LabelVisibilityMode = LabelVisibilityMode.LabelVisibilityLabeled;
        }

        protected override void OnDetached ()
        {
        }
    }
}

Now inside of your shared code create the Routing Effect:

using Xamarin.Forms;

namespace App16
{
    public class NoShiftEffect : RoutingEffect
    {
        public NoShiftEffect() : base("MyCompany.NoShiftEffect")
        {
        }
    }
}

And finally add the effect:

<TabbedPage.Effects>
    <local:NoShiftEffect />
</TabbedPage.Effects>

Using LabelVisibilityLabeled for the mode will turn off shifting and properly display the labels:

Tabs

Here is what the other modes do:

  • LabelVisibilityAuto: Label behaves as "labeled" when there are 3 items or less, or "selected" when there are 4 items or more.
  • LabelVisibilityLabeled: Label is shown on all navigation items.
  • LabelVisibilitySelected: Label is shown on the selected navigation item.
  • LabelVisibilityUnlabled: Label is not shown on any navigation items.

Here is what the last one looks like:

NoLabels

If you are using an older version of the support libraries then you can fall back on my old reflection trick and a custom renderer.

I think that is it! I think I can retire from bottom tabs for now! Enjoy and don't forget to read all about Bottom Tabs on the official documentation.

]]>
Xamarin.Forms: Drop Shadow/Elevation on Android Bottom Navigation TabbedPage https://staging.montemagno.com/xamarin-forms-drop-shadow-elevation-on-android-bottom-navigation-tabbedpage/ 5c6271e1720b5500bfd5d611 Thu, 14 Feb 2019 20:58:00 GMT

It is a tale as old of time fighting with Android elevation and drop shadows. First it was the Toolbar on a Navigation page and now it is the new fancy bottom tabs that we got in Xamarin.Forms 3.1. I know what you are thinking... another custom renderer... NOPE! You just have to know to set a few different properties correctly.

BeforeAfter

Here is what we are starting with:

<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
             android:TabbedPage.ToolbarPlacement="Bottom"
             x:Class="GeoContacts.View.HomePage">
<!-- Pages Here-->
</TabbedPage>

As we can see there is no fancy shadow or elevation :(

Screenshot_20190211-164745

That Background Color!

The BottomNavigationView is a tricky beast regardless if you are in Android XML or Xamarin.Forms. To get a drop shadow/elevation on it you MUST set the background color! It is true, I even mentioned it way back in August 2017 when I wrote my original blog on it. All we need to do is set the background color on our TabbedPage and we are set:

BarBackgroundColor="{OnPlatform Android=White}"

And here we go:

Screenshot_20190211-163121

That is literally it! You can of course set it to any color you would like and you should still see a nice shadow appear to separate out your content. Now if for some reason you have to support a really really old device that doesn't support elevation, then you will have to do some custom work here. Don't worry as you can custom render it :)

Custom Android Layouts & Renderer

To do this manually you will need to create 2 resource files:

  • Resources/drawable/shadow.xml
  • Resource/layout/view_shadow.axml

The shadow.xml is a very simple reverse gradient:

<?xml version="1.0" encoding="utf-8" ?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <gradient
        android:startColor="#1F000000"
        android:endColor="@android:color/transparent"
        android:angle="90" />
</shape>

For the view_shadow.axml this is just a dummy view with the background set to shadow.

<View
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="4dp"
    android:background="@drawable/shadow"/>

Now, it is time for our custom renderer inside of our Android project. This code is actually very simple and just injects the view_shadow onto the top of our navigation view:

using Android.Views;
using Xamarin.Forms.Platform.Android.AppCompat;
using Xamarin.Forms.Platform.Android;
using Android.Support.Design.Widget;
using Android.Content;
using Android.Widget;

[assembly: ExportRenderer(typeof(TabbedPage), typeof(MyTabsRenderer))]
namespace GeoContacts.Droid
{
    public class MyTabsRenderer : TabbedPageRenderer
    {
        public MyTabsRenderer()
        {

        }
        public MyTabsRenderer(Context context)
        {

        }

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.TabbedPage> e)
        {
            base.OnElementChanged(e);

            if (!(GetChildAt(0) is ViewGroup layout))
                return;

            if (!(layout.GetChildAt(1) is BottomNavigationView bottomNavigationView))
                return;

            var topShadow = LayoutInflater.From(Context).Inflate(Resource.Layout.view_shadow, null);

            var layoutParams =
                new Android.Widget.RelativeLayout.LayoutParams(LayoutParams.MatchParent, 15);
            layoutParams.AddRule(LayoutRules.Above, bottomNavigationView.Id);

            layout.AddView(topShadow, 2, layoutParams);

        }
    }
}

There you have it! Regardless of your implementation the shadow adds some nice flare and takes minimal effort.

]]>
Enhanced Mobile App Versioning in Azure DevOps (VSTS) with Mobile Tasks https://staging.montemagno.com/enhanced-mobile-app-versioning-in-azure-devops-vsts-with-mobile-tasks/ 5c39652e58056200bf4c25d0 Tue, 15 Jan 2019 22:30:00 GMT A little over a year ago, I introduced my Mobile App Tasks Extensions for iOS and Android into the Azure DevOps (VSTS) marketplace. For iOS and Android developers it made the tasks of versioning and adjusting package names a breeze. They were written completely in TypeScript with VS Code and of course are open source on GitHub. Since the initial release I have received overall positive feedback and a few feature requests to add new features. Over the last year I teamed up with my good friend Andrew Hoefling to add small features and optimizations, but finally this week we released version 1.0 with tons of great new features!

New

PList Transform

The first feature you will see is a brand new PList Transform Task. Andrew sent down this pull request and it is super slick. Basically just set any property name and value you want to set in your iOS plist file and you are good to go!

Unix Timestamp

When versioning apps I always had the assumption that developers wanted to manually configure version numbers. However, sometimes you want an auto incrementing number such as a timestamp. When versioning iOS and Android apps you now have the ability to select a custom version number or auto numbering with the Unix time in seconds. This matches App Center functionality, which is super cool :)

format

Misc Updates of Joy

Beyond that we fixed a few other items such as adding the iOS Bundle Identifier to the package name task, fixed issues when comments were in the Android Manifest, handled escaped paths, and updated to the latest packages.

Grab the Extension

You can grab the latest update of the Mobile App Tasks from the Visual Studio Marketplace right now!

Watch Me & Donovan

Last year my good friend Donovan Brown asked me to come on Visual Studio Toolbox to show off the awesome extension and talk Mobile DevOps. Check it out as I walk through the extensions:

How It Was Developed

If you want to see more on how it was developed be sure to check me out on Twitch where I live code. I recently did a full live stream where I added the unix timestamp feature to iOS and Android build tasks. You can watch the full video on YouTube right now!

]]>