Saturday, September 15, 2018

Simpsons Trivia Android Port

In the previous post, we introduced Simpsons Trivia: a simple quiz game for the Sega Master System. Here we outlined tasks to port game to Windows using MonoGame. Now we would like to port game to Android.
Let's check it out!

Pre-Requisites
This post extends the Candy Kid Android port which assumes MonoGame, JDK + Android SDK are installed.

Setup
Download and install Visual Studio 2017. Install "Mobile development with .NET" for Xamarin development.

Launch Visual Studio 2017. Configure JDK and Android SDK. Tools | Options | Xamarin | Android Settings:
 Type  Location
 Java Development Kit Location  C:\Program Files\Java\jdk1.8.0_172
 Android SDK Location  C:\Program Files (x86)\Android\android-sdk
 Android NDK Location  C:\ProgramData\Microsoft\AndroidNDK\android-ndk-r11c

Development
AND Game Client
Visual Studio 2017: Create new project. New Project | Visual C# | MonoGame | MonoGame Android Project.
 Key  Value
 Name  SimpsonsTrivia.AND
 Location  C:\SVN\SimpsonsTrivia
 Solution name  SimpsonsTrivia.AND
Download code sample here.

Checklist
Here is a checklist of tasks to complete in order to port game from Windows to Android using MonoGame:
  • Configure project properties
  • Build Android Manifest file
  • Update project references
  • Update nuget packages
  • Configure SVN Externals
  • Import Assets content
  • Import Resources
  • Update client files

Configure project properties
As of August 1, 2018 Google Play requires new apps to target at least Android 8.0 (API level 26) to meet the minimum target API level requirement. Right click SimpsonsTrivia.AND project | Properties. Application tab. Ensure "Compile using Android version: (Target Framework)" is set to, at a minimum, Android 8.0 (Oreo).
If Android 8.0 (Oreo) not installed then launch Android Studio. Tools | SDK Manager. Download API level 26. Note the Android SDK location and copy "platforms" folder to C:\Program Files (x86)\Android\android-sdk.

Build Android Manifest file
Right click SimpsonsTrivia.AND project | Properties. Android Manifest tab. Click "No AndroidManifest.xml"
 Key  Value  Element  Attribute
 Application name  Simpsons Trivia  application  android:label
 Package name  com.steveproxna.simpsons  manifest  package
 Application icon  @drawable/Icon  application  android:icon
 Version number  1  manifest  android:versionCode
 Version name  1.0.0  manifest  android:versionName
 Minimum Android version  Android 8.0 (API Level 26 - Oreo)  uses-sdk  android:minSdkVersion
 Target Android version  Android 8.0 (API Level 26 - Oreo)  uses-sdk  android:targetSdkVersion
IMPORTANT
If there is an integer in the bundle identifier then Google will prepend an "x" e.g. 3dcity becomes "x3dcity"

Update project references
Changes to Android version (Target Framework) on the Application tab [above] will update Mono.Android.dll reference. Note, it may be necessary to change OpenTK-1.0 reference to OpenTK for compatibility reasons.

Update nuget packages
It may also be necessary to downgrade MonoGame.Framework to 3.4.0.459 in order to load Content from the Assets parent folder and play sound effects. Right click References node and Manage NuGet Packages. Change MonoGame version. Install Portable Ninject package too as Ninject is required as an IoC Container.

Configure SVN Externals
Follow instructions to setup SVN Externals and use shared Data content plus Common + Master source code:
DATA
 AND  Folder :  C:\SVN\SimpsonsTrivia\SimpsonsTrivia.AND\SimpsonsTrivia.AND\Assets
 Action :  Right click "Content" folder
 URL  http://build/svn/SimpsonsTrivia/SimpsonsTrivia.XNA/SimpsonsTrivia.XNA/SimpsonsTrivia.XNAContent/Data

CODE
 AND  Folder :  C:\SVN\SimpsonsTrivia\SimpsonsTrivia.AND
 Action :  Right click "SimpsonsTrivia.AND" folder
 URL  http://build/svn/SimpsonsTrivia/SimpsonsTrivia.XNA/SimpsonsTrivia.XNA.Library/Common
 URL  http://build/svn/SimpsonsTrivia/SimpsonsTrivia.XNA/SimpsonsTrivia.XNA.Library/Master

Import Assets content
Manually import game content and set file properties. Refer to Candy Kid Android port for more information:
 Folder  Type  XNB  Build Action  Copy to Output Directory
 Data  All  No  AndroidAsset  Do not copy
 Fonts  All  Yes  AndroidAsset  Do not copy
 Sound  All  No  AndroidAsset  Do not copy
 Textures  All  No  AndroidAsset  Do not copy

Import Resources
Replace default Icon and Splash images located in Resources / Drawable folder with custom images:
 Icon.png 72x72
 Splash.png 800x480

Navigate to makeappicon. Upload generic icon image that represents game. Best results use 1536 x 1536 image. After processing, create the following sub folders beneath the Resources parent folder and import:
 Folder  Dimension    Folder  Dimension
 mipmap-hdpi  72 x 72    mipmap-xhdpi  96 x 96
 mipmap-ldpi  36 x 36    mipmap-xxhdpi  144 x 144
 mipmap-mdpi  48 x 48    mipmap-xxxhdpi  192 x 192

Delete default Strings.xml file as not needed here. Update the default Styles.xml file for fullscreen splash:
<?xml version="1.0" encoding="utf-8"?>
<resources>
 <style name="Theme.Splash" parent="@android:style/Theme.NoTitleBar.Fullscreen">
  <item name="android:windowBackground">@drawable/splash</item>
  <item name="android:windowFullscreen">true</item>
  <item name="android:windowNoTitle">true</item>
  <item name="android:windowIsFloating">false</item>
 </style>
</resources>

Update client files
Finally, delete default Game1.cs code file. Rename Activity1.cs to SplashActivity.cs + add the following code:
[Android.App.Activity(Label = "Simpsons Trivia"
  , MainLauncher = true, NoHistory = true, Icon = "@drawable/icon"
  , Theme = "@style/Theme.Splash", AlwaysRetainTaskState = true
  , ScreenOrientation = Android.Content.PM.ScreenOrientation.SensorLandscape)]
public class SplashActivity : Android.App.Activity
{
  protected override void OnCreate(Android.OS.Bundle bundle)
  {
    base.OnCreate(bundle);
    StartActivity(typeof(GameActivity));
  }
}

Next, add GameActivity.cs. Add the following code to take advantage of the new fullscreen Immersive mode:
using Android.Content.PM;
using Android.Views;
[Android.App.Activity(Label = "Simpsons Trivia"
  , NoHistory = true, Icon = "@drawable/icon"
  , Theme = "@style/Theme.Splash", AlwaysRetainTaskState = true
  , LaunchMode = Android.Content.PM.LaunchMode.SingleInstance
  , ScreenOrientation = ScreenOrientation.SensorLandscape
  , ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | ConfigChanges.ScreenSize)]
public class GameActivity : Microsoft.Xna.Framework.AndroidGameActivity
{
  protected override void OnCreate(Android.OS.Bundle bundle)
  {
    base.OnCreate(bundle);
    var g = new WindowsGame.Common.AnGame();
    Android.Views.View view = (Android.Views.View)g.Services.GetService(typeof(Android.Views.View));
    if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Kitkat)
    {
      view.SystemUiVisibility = (Android.Views.StatusBarVisibility)(
        SystemUiFlags.LayoutStable | SystemUiFlags.LayoutHideNavigation |
        SystemUiFlags.LayoutFullscreen | SystemUiFlags.HideNavigation |
        SystemUiFlags.Fullscreen | SystemUiFlags.ImmersiveSticky);
    }
    SetContentView(view);
    g.Run();
  }
}

Also, to prevent annoying landscape flipping during game play set display orientation to landscape left only:
//AnGame.cs
graphics = new GraphicsDeviceManager(this) {SupportedOrientations = DisplayOrientation.LandscapeLeft};

Update AndroidManifest.xml. Add "android.max_aspect" at 2:1 value to ensure immersive mode accordingly:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
 package="com.steveproxna.simpsons" 
 android:versionCode="1" 
 android:versionName="1.0.0" 
 android:installLocation="auto">
 <uses-sdk android:minSdkVersion="26" android:targetSdkVersion="26" />
 <application android:label="Simpsons Trivia" android:icon="@drawable/Icon">
  <meta-data android:name="android.max_aspect" android:value="2.1" />
 </application>
</manifest>

IMPORTANT
If 2x app icons appear on device then ensure there is one instance of MainLauncher=true only in an Activity!

Deployment
Despite the fact that you are able to complete all game development on Windows PC using Visual Studio 2017, it may actually be easier to complete Android deployment on the Mac using Visual Studio for Mac.

Launch the Terminal, checkout the source code and open SimpsonsTrivia.AND.sln in Visual Studio for Mac. Right click project | Options | Build | Android Build | Advanced. Ensure 3x Supported ABIs are checked:
Connect Android device. Launch Terminal and enter adb devices to ensure Android device is detected in the list of devices attached. In Visual Studio ensure device selected. Right click project | Archive for Publishing.
Click Sign and Distribute... There are 2x Android Distribution channels: Ad Hoc and Google Play. Choose Ad Hoc to save to disk and test locally while Google Play will upload to the Google Play store available for sale.

During Google Play deployment you may be prompted to create a new project in the "API access" tab linked to your Google Play Developer account in which you must create an OAuth client and enter the ID + Secret:
Sign in to your Google Developer account. Navigate to play.google.com/apps/publish | Settings | API access. Create OAuth client. View in Google Developers Console. Download JSON and extract the Client ID + Secret.

Publication
Complete the Google Play deployment to sign APK binary ready for upload. In Google Play Developer console Create Application. Enter all relevant details. Create promotional images and upload to Store listing section:
 Type  Dimensions  Description
 Hi-res icon  512 x 512  32-bit PNG (with alpha)
 Feature Graphic  1024 x 500  JPG or 24-bit PNG (no alpha)
 Promo Graphic  180 x 120  JPG or 24-bit PNG (no alpha)
 TV Banner  1280 x 720  JPG or 24-bit PNG (no alpha)
 Daydream 360 degree stereoscopic image  4096 x 4096  JPG or 24-bit PNG (no alpha)
NOTE: resizeimage.net is a great online tool to help resize the same generic image for different resolutions.

VIDEO
Produce a YouTube video to showcase the game: Launch Camtasia Studio. Disconnect webcam if connected. Choose Record screen. In popup | click Audio button. You should see the following message about No audio: No audio input sources were found, pls verify that your audio devices are installed and plugged in correctly.

Plug black cable from microphone into headphone jack. Shouldn't need to unplug speaker cable from audio jack. Click Audio drop down | Click Microphone Real Tech [visible] to enable. Now record screen as before.

COPPA
Ensure the game is COPPA compliant by completing the "Primarily Child-Detected" section under the Store Presence | Pricing & Distribution section otherwise the game could be unpublished from Google Play store.
Finally, upload signed APK binary from deployment completion to Google Play and rollout Production release!

Summary
That concludes the Simpsons Trivia port to Android. The next post will focus on Simpsons Trivia port to iOS.