Testing Your MAMP HTML5 Content on Android Emulators

Seeing your MAMP localhost site inside a web browser in an Android Emulator

On OS X your local IP is usually http://127.0.0.1:8080, which is what’s pulled up by http://localhost:8080.
Inside an Android Emulator instance or AVD, that same IP and localhost don’t work.
Android docs point us to this number instead:

http://10.0.2.2:8080 

Seeing MAMP Virtual Hosts from inside the Android Emulator

According to this post, you need to login into the Emulator’s shell:

$adb -e shell

Then edit the Emulator’s /etc/hosts/ file to point to your http://mySite.localhost

# echo '10.0.2.2      prototypes.localhost' >> /etc/hosts

First time I tried this, I got this error:

root@android:/ # echo '10.0.2.2 prototypes.localhost' >> /etc/hosts
/system/bin/sh: can't create /etc/hosts: Read-only file system

Then I found a great step by step tutorial on the subject from http://dillieodigital.wordpress.com. His post reminded me to check that my emulator exists, has been started up and is running properly via “./android list avd”, “./adb devices”, “./adb kill server”, “./adb start server” and “./adb -s emulator-5554 remount”.

I tried his “Option 1”, pulling the Emulator’s /etc/hosts file, editing it off my Desktop and pushing it back to the Emulator but it didn’t work:

# ./adb -s emulator-5554 pull /etc/hosts /Users/your.name/Desktop
# ### edit hosts file ### 
# ./adb -s emulator-5554 push /Users/your.name/Desktop/hosts /etc/

“Option 2” from the above quoted tutorial worked fine for me, like so:

# ./adb -s emulator-5554 shell
# echo '10.0.2.2   prototypes.localhost' >> /etc/hosts
# exit

Now, if I ever get that “can’t create /etc/hosts: Read-only file system” error from the Emulator’s shell, I have to “exit” of the Emulator’s shell and remount again:

"./adb -s emulator-5554 remount" 

Then, “Option 2” works fine for me again.

Advertisements

Unity3D for Android: Using a Native Java Plugin to Grab the Android Device ID

The java code returns an MEID (Android Device ID) on CDMA phones (Verizon, Sprint), an IMEI on GSM phones (AT&T, T Mobile) and an Android ID on WIFI-only devices (no cellular network).

This code is based on Android Java Plugin for Unity example project in the Unity manual.

Why bother with a plugin?

I tried using Unity 3.5’s built-in SystemInfo.deviceUniqueIdentifier getter for obtaining a unique device ID but it didn’t meet our ad network’s requirements.

The set up in Unity 3.5

  • Drag the AndroidJava.jar file inside /Assets/Plugins/Android/. /Assets/Plugins/Android/bin/ worked too. This .jar file is created via Eclipse or command line javac based on the Java source code
  • Drag the example libjni.so file inside /Assets/Plugins/Android/. Make sure it’s inside /Plugins/Android or you’ll get a “DllNotFoundException: jni” error when you test on device with adb.
  • Drag JNI.cs into /Assets/Plugins/
  • Drag JavaVM.cs into /Assets/Plugins/
  • Make sure your project has an AndroidManifest.xml file inside /Assets/Plugins/Android/ with android.permission.READ_PHONE_STATE permission set. android.permission.READ_PHONE_STATE is needed to access an Android phone/tablet’s Android ID, IMEI or MEID. If you’re making HTTP calls you’ll also need additional permissions, etc.

Code that worked:

...
	private static IntPtr	JavaClass; 
	private static int		GetDeviceId;
...

	///if you're calling the plugin from a static method, you still have to make sure an instance of this script is attached to 
	///an empty GameObject in Hierarchy so Start() method runs & the below code initializes:	
	void Start() {
		
		#if UNITY_ANDROID 		
		
			// attach our thread to the java vm; obviously the main thread is already attached but this is good practice..
			JavaVM.AttachCurrentThread();
			
			// first we try to find our main activity..
			IntPtr cls_Activity		= JNI.FindClass("com/unity3d/player/UnityPlayer");
			int fid_Activity		= JNI.GetStaticFieldID(cls_Activity, "currentActivity", "Landroid/app/Activity;");
			IntPtr obj_Activity		= JNI.GetStaticObjectField(cls_Activity, fid_Activity);
			Debug.Log("obj_Activity = " + obj_Activity);		
			
			// create a JavaClass object...
               //"com/yourCompany/yourProjectName" should be your Bundle ID in Unity's Player Settings & should be the package name in your Java class
			IntPtr cls_JavaClass	= JNI.FindClass("com/yourCompany/yourProjectName/JavaClass"); 	
			int mid_JavaClass		= JNI.GetMethodID(cls_JavaClass, "<init>", "(Landroid/app/Activity;)V");
			IntPtr obj_JavaClass	= JNI.NewObject(cls_JavaClass, mid_JavaClass, obj_Activity);
			Debug.Log("java Helper object = " + obj_JavaClass);		
			
			// create a global reference to the JavaClass object and fetch method id(s)..
			JavaClass					= JNI.NewGlobalRef(obj_JavaClass);
			GetDeviceId					= JNI.GetMethodID(cls_JavaClass, "GetDeviceId", "()Ljava/lang/String;");
			Debug.Log("JavaClass global ref = " + JavaClass);
			Debug.Log("JavaClass GetDeviceId method id = " + GetDeviceId);						
		
		#endif			
	}	
...

	/// <summary>
	/// Gets the unique device for Android devices (IMEI, MEID or Android ID).
	/// </summary>
	/// <returns>
	/// Returns the id as a string.
	/// </returns>
	private string GetTheDeviceID()
	{		
		//didn't work... supposed to return IMEI but didn't 
		//return SystemInfo.deviceUniqueIdentifier;
		
		String dID = "";
		
		#if UNITY_ANDROID 
	
			// again, make sure the thread is attached..
			JavaVM.AttachCurrentThread();
						
			// get the Java String object from the Helper java object
			IntPtr str_cacheDir 	= JNI.CallObjectMethod(JavaClass, GetDeviceId);
			Debug.Log("str_cacheDir = " + str_cacheDir);		
			
			// convert the Java String into a Mono string
			IntPtr stringPtr = JNI.GetStringUTFChars(str_cacheDir, 0);
			Debug.Log("stringPtr = " +stringPtr);
			dID = Marshal.PtrToStringAnsi(stringPtr);
			JNI.ReleaseStringUTFChars(str_cacheDir, stringPtr); 		
					
		#endif

		Debug.Log(" Device ID value is = " + dID);
		
		return devID;		
	}		

/// call GetTheDeviceID() when you need to use it
...

The Java Code

The below code is based on requirements by ad networks that need to use a specific device ID for metrics like conversion tracking. It’s the main source file for generating the AndroidJava.jar you need to import into Unity’s Plugins folder.

The Device ID & hashing methods implementations come directly from Millennial Media‘s public wiki.

package com.yourCompany.yourProjectName;

import android.app.Activity;
import android.content.Context;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.util.Log;
import java.io.File;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class JavaClass
{
	private Activity mActivity;
	
	public JavaClass(Activity currentActivity)
	{
		Log.i("JavaClass", "Constructor called with currentActivity = " + currentActivity);
		mActivity = currentActivity;
	}
	
	public String GetDeviceId()
	{		
		// Get the device ID
        String auid = android.provider.Settings.Secure.ANDROID_ID + "android_id";
        
        Context context = mActivity.getApplicationContext();
        
        TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
        if(tm != null)
        {
             try{
             auid = tm.getDeviceId();
             }
             catch (SecurityException e){
                  e.printStackTrace();
             }
             tm = null;
        }
        if(((auid == null) || (auid.length() == 0)) && (context != null))
             auid = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
        if((auid == null) || (auid.length() == 0))
             auid = null;
        
        return auid;	
	}
	
	public String GetHashedDeviceIdSHA1()
	{
		String deviceId = GetDeviceId();
		
		String hashedDeviceId = hashInputSHA1(deviceId);
		
		return hashedDeviceId;
	}
	
	public String GetHashedDeviceIdMD5()
	{
		String deviceId = GetDeviceId();
		
		String hashedDeviceId = hashInputMD5(deviceId);
		
		return hashedDeviceId;
	}

	
	/**
     * <p>
     * Hashes the given plain text using the MD5 algorithm.
     * </p>
     * 
     * <p>
     * Example:
     * </p>
     * 
     * <p>
     * 098f6bcd4621d373cade4e832627b4f6
     * </p>
     * 
     * @param input
     *            The raw input which must be hashed.
     * @return A String representing the MD5 hashed output.
     */
    public static String hashInputMD5(String input)
    {
        String rv = null;
          
        if (input != null)
        {
            try
            {
                // Hash the user ID
                byte[] hashBytes = null;
                MessageDigest md = MessageDigest.getInstance("MD5");
                  
                synchronized (md)
                {
                    hashBytes = md.digest(input.getBytes());
                }
                 
                // Convert the hashed bytes into a properly formatted String
                if (hashBytes != null)
                {
                    StringBuilder sb = new StringBuilder();
                    
                    for (byte b : hashBytes)
                    {
                        String hexString = Integer.toHexString(0x00FF & b);
                        sb.append((hexString.length() == 1) ? "0" + hexString
                                                           : hexString);
                    }
                         
                    rv = sb.toString();
                }
            }
            catch (NoSuchAlgorithmException exception)
            {
                // This exception should never occur
            }
        }
         
        return rv;
    }
    
    /**
     * <p>
     * Hashes the given plain text using the SHA-1 algorithm.
     * </p>
     *
     * <p>
     * Example:
     * </p>
     *
     * <p>
     * a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
     * </p>
     *
     * @param userId
     *            The raw input which must be hashed.
     * @return A String representing the SHA-1 hashed input.
     */
    public static String hashInputSHA1(String input)
    {
        String rv = null;
          
        if (input != null)
        {
            try
            {
                // Hash the user ID
                byte[] hashBytes = null;
                MessageDigest md = MessageDigest.getInstance("SHA1");
             
                synchronized (md)
                {
                    hashBytes = md.digest(input.getBytes());
                }
              
                // Convert the hashed bytes into a properly formatted String
                if (hashBytes != null)
                {
                    StringBuilder sb = new StringBuilder();
              
                    for (byte b : hashBytes)
                    {
                        String hexString = Integer.toHexString(0x00FF & b);
                        sb.append((hexString.length() == 1) ? "0" + hexString
                                                           : hexString);
                    }
                 
                    rv = sb.toString();
                }
            }
            catch (NoSuchAlgorithmException exception)
            {
                // This exception should never occur
            }
        }
             
        return rv;
    }
}

AndroidManifest.xml

If you don’t specify the correct permissions for your app and try to do things like make an HTTP request or grab the Device ID, you’ll get an error, like this:

10-02 15:06:49.223  7776  7784 W System.err: java.lang.SecurityException: Requires READ_PHONE_STATE: Neither user 10109 nor current process has android.permission.READ_PHONE_STATE.
10-02 15:06:49.223  7776  7784 W System.err: 	at android.os.Parcel.readException(Parcel.java:1322)
10-02 15:06:49.223  7776  7784 W System.err: 	at android.os.Parcel.readException(Parcel.java:1276)
10-02 15:06:49.223  7776  7784 W System.err: 	at com.android.internal.telephony.IPhoneSubInfo$Stub$Proxy.getDeviceId(IPhoneSubInfo.java:150)
10-02 15:06:49.231  7776  7784 W System.err: 	at android.telephony.TelephonyManager.getDeviceId(TelephonyManager.java:216)
10-02 15:06:49.231  7776  7784 W System.err: 	at com.yourCompany.yourProjectName.JavaClass.GetDeviceId(JavaClass.java:44)
10-02 15:06:49.231  7776  7784 W System.err: 	at com.unity3d.player.UnityPlayer.nativeRender(Native Method)
10-02 15:06:49.231  7776  7784 W System.err: 	at com.unity3d.player.UnityPlayer.onDrawFrame(Unknown Source)
10-02 15:06:49.231  7776  7784 W System.err: 	at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1363)
10-02 15:06:49.231  7776  7784 W System.err: 	at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1118)

I grabbed the default one from inside Unity.app on my Mac:

/Applications/Unity/Unity.app/Contents/PlaybackEngines/AndroidDevelopmentPlayer/AndroidManifest.xml

Kept all as is, just added my permissions and it worked:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.unity3d.player"
	android:installLocation="preferExternal"
    android:versionCode="1"
    android:versionName="1.0">
    <supports-screens
        android:smallScreens="true"
        android:normalScreens="true"
        android:largeScreens="true"
        android:xlargeScreens="true"
        android:anyDensity="true"/>

    <application
		android:icon="@drawable/app_icon"
        android:label="@string/app_name"
        android:debuggable="true">

        //...

    </application>

	
	<!-- PERMISSIONS -->
		<uses-permission android:name="android.permission.INTERNET"/>
		<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
		<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
	
</manifest>

Some Other Possible Errors

Here’s a fun one, based on a dumb mistake I made. I put the libjni.so into the wrong folder, /Assets/Plugins/, instead of /Assets/Plugins/Android/:

10-02 14:43:17.147  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.147  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.155  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.163  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.171  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.171  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.179  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.186  7587  7596 E Unity   : Unable to find jni
10-02 14:43:17.897  1410  1410 I wpa_supplicant: WPS-AP-AVAILABLE 
10-02 14:43:17.905  1320  1411 V WifiMonitor: Event [WPS-AP-AVAILABLE ]
10-02 14:43:17.905  1320  1411 D WifiMonitor: WPS Event: WPS-AP-AVAILABLE 
10-02 14:43:18.264  1391  1391 D RadioSignalLevel: evdo dbmLevel: 4, snrLevel: 3
10-02 14:43:18.522  7587  7596 I Unity   : DllNotFoundException: jni
10-02 14:43:18.522  7587  7596 I Unity   :   at (wrapper managed-to-native) JavaVM:AttachCurrentThread ()
10-02 14:43:18.522  7587  7596 I Unity   :   at MyUnityDeviceIdScript.Start () [0x00000] in <filename unknown>:0 
10-02 14:43:18.522  7587  7596 I Unity   :  
10-02 14:43:18.522  7587  7596 I Unity   : (Filename:  Line: -1)
10-02 14:43:18.522  7587  7596 I Unity   : 
10-02 14:43:21.561  7587  7596 I Unity   : checkDeviceID() called
10-02 14:43:21.561  7587  7596 I Unity   :  
10-02 14:43:21.561  7587  7596 I Unity   : (Filename: ./Runtime/ExportGenerated/AndroidManaged/UnityEngineDebug.cpp Line: 43)
10-02 14:43:21.561  7587  7596 I Unity   : 
10-02 14:43:21.671  7587  7596 E Unity   : Unable to find jni
10-02 14:43:21.679  7587  7596 E Unity   : Unable to find jni
10-02 14:43:21.679  7587  7596 E Unity   : Unable to find jni
10-02 14:43:21.679  7587  7596 E Unity   : Unable to find jni
10-02 14:43:21.694  7587  7596 I Unity   : DllNotFoundException: jni
10-02 14:43:21.694  7587  7596 I Unity   :   at (wrapper managed-to-native) JavaVM:AttachCurrentThread ()
10-02 14:43:21.694  7587  7596 I Unity   :   at MyUnityDeviceIdScript.GetUniqueDeviceID () [0x00000] in <filename unknown>:0 
10-02 14:43:21.694  7587  7596 I Unity   :   at MyUnityDeviceIdScript.callTheUrlWithDeviceIDParam () [0x00000] in <filename unknown>:0 
10-02 14:43:21.694  7587  7596 I Unity   :   at GameManager.checkDeviceID () [0x00000] in <filename unknown>:0 
10-02 14:43:21.694  7587  7596 I Unity   :  

As far as I’ve seen, this code only works on a device. If you try to test inside Unity, you’ll get a similar error:

DllNotFoundException: jni
MyUnityDeviceIdScript.Start () (at Assets/Scripts/MyUnityDeviceIdScript.cs:21)

Once you test on a device, if all else if good, this error should go away.

NOTE: looks like this code requires some time to load up the JNI .so file. Not absolutely sure yet, but it appears if I have a fairly complex project and I try to call a JNI related method very soon after app loading, the app crashes with weird mystery output in adb logcat. Need to look into this more to be sure about the details.

Unity3D for Android: “JNI ERROR (app bug): attempt to use stale global reference” with Incorrect Use of Prime31’s Social Networking Plugin

I was porting a Unity3D-built iOS game to Android the other day, while using the awesome Social Networking plugin from Prime31, and got this fun Java error:

E/dalvikvm( 5539): JNI ERROR (app bug): attempt to use stale global reference 0x32
E/dalvikvm( 5539): VM aborting
F/libc    ( 5539): Fatal signal 11 (SIGSEGV) at 0xdeadd00d (code=1), thread 5583 (UnityMain)
... 

The above error crashed my app consistently.

I was lucky in this case because I was fairly sure where it’s coming from, suspecting an unnecessary call to FacebookAndroid.logout() inside a check for !FacebookAndroid.isSessionValid() check. Not my proudest coding moment, for sure.

Once I put a Debug.Log() right before the FacebookAndroid.logout() call and right after. The error changed to… or perhaps a new error started showing up around the same place in the debug log:

W/dalvikvm( 2910): threadid=9: thread exiting with uncaught exception (group=0x4001d5a0)
E/AndroidRuntime( 2910): FATAL EXCEPTION: GLThread 10
E/AndroidRuntime( 2910): java.lang.NullPointerException
E/AndroidRuntime( 2910): 	at com.prime31.FacebookPlugin.logout(FacebookPlugin.java:187)
E/AndroidRuntime( 2910): 	at com.unity3d.player.UnityPlayer.nativeRender(Native Method)
E/AndroidRuntime( 2910): 	at com.unity3d.player.UnityPlayer.onDrawFrame(Unknown Source)
E/AndroidRuntime( 2910): 	at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1363)
E/AndroidRuntime( 2910): 	at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1118)
W/ActivityManager( 1323):   Force finishing activity com.companyName.MyBundleIDHere/com.unity3d.player.UnityPlayerNativeActivity

The app stopped crashing but the java.lang.NullPointerException didn’t go away either. Still looking into it.

Unity3D for Android: Porting a Unity3D iOS Project to Android

This is a Notes-To-Self work in progress. Why share? Several reasons, among them, my desire to see Unity become a more popular platform for mobile app development in the near future. If any part of this post saves someone dev time or helps them pick Unity as the dev platform for their mobile project, that means more work opportunities for developers that took the time to learn Unity.

Basic checklist

  • For on-device debugging, add Android’s adb tool to your environment variable. For me, on OS X, it was fairly easy.
  • Set up logcat w/ adb to get on-device debugging on Android. Xcode’s Console lets you do this pretty much by default when you “Run” on a device that’s attached to your machine via USB.
  • Enable your Android phone for development & USB debugging.
  • Figure out an approach for dealing with the numerous Android pixel densities & screen sizes. Daniel Brauer’s DisplayMetricsAndroid class looks pretty good. Haven’t tested enough yet. As a commenter states, it’s implemented like a “plugin” via the Plugins folder in your project’s Assets folder. Then there’s Screen.dpi, in, at least, Unity 3.5.
  • If you’re using Prime31 plugins like Etcetera and Social Networking, you’ll need to get the Android versions. Their Google Analytics plugin works on both.
  • If you’re using the open source UIToolkit for menus, screens, HUD, etc, look into how to get 1.5x graphics to work. Perhaps, 1.5x sprite sheet and tweak(s) to or subclasses of the UI or UIToolkit classes. Not sure yet.
  • The ProgressDialog is Android’s version of the Activity indicator, UIActivityIndicatorView on iOS.

Potentially useful formulas & numbers

A review aspect ratio formula might come in useful. For example, if you have a screen that’s “1280 x 720”, you can assume width/height = 1280/720 = 1.78 = 16/9, aka it’s a 16:9 aspect ratio. This calculator can be useful at times.

  • So, for HTC Legend’s 480×320, you can do 480/320 = 1.5 = 3/2 to get an aspect ratio of 3:2.
  • For Motorola Droid’s 854 x 480 – 854/480 = 1.78 (1.779) = 16/9 (roughly) yields a 16:9 ratio.
  • For Nexus One’s 800 x 480, do 800/480 = 1.6 (1.666) = 16/10 (roughly) to get 16:10 aspect ratio.
  • Samsung Galaxy S3 is at 1280×720, with a 16:9 ratio.
  • Most Galaxy S2 phones are at 800×480, 16:10 ratio. At least one is at 1280×720, 16:9 ratio.
  • iPad 3 has the horizontal dimensions of 2048 × 1536. 2048/1536 = 1.33, which is roughly 3/2. 3:2 aspect ratio

So if I’m using sprite sheets w/ UIToolkit, does this mean I should create android versions of the sprite sheets at 1.5x, 1.78x and 1.6x versions of the basic size of the smallest iOS images (sheets)? Mmmmm… more research needed.

Try Using Density Independent Pixels via Unity’s Screen.dpi

Screen.dpi is one of the improvements in Unity 3.5 (or from what I hear, even 3.4). If you’re using UIToolkit, you can try using public methods like pixelsFromTopLeft( float x, float y) coupled with the Density-independent pixel technique recommended by the Android developer guide:

Density-independent pixel (dp)
A virtual pixel unit that you should use when defining UI layout, to express layout dimensions or position in a density-independent way.
The density-independent pixel is equivalent to one physical pixel on a 160 dpi screen, which is the baseline density assumed by the system for a “medium” density screen. At runtime, the system transparently handles any scaling of the dp units, as necessary, based on the actual density of the screen in use. The conversion of dp units to screen pixels is simple: px = dp * (dpi / 160). For example, on a 240 dpi screen, 1 dp equals 1.5 physical pixels. You should always use dp units when defining your application’s UI, to ensure proper display of your UI on screens with different densities.

So to get the x, y values for the pixelsFromTopLeft( float x, float y) method, use the formula px = dp * (Screen.dpi / 160), assuming 160 dpi is your minimally supported screen dpi. This also means that dp = px / (Screen.dpi / 160).

Note, on some Android devices the system fails to generate a dpi and returns a ZERO. In those cases, the above formula breaks, as noted here.
Here’s a free DP to PS calculator.

SD / HD screen sizes for Android, UIToolkit Sprite Sheets & PSDs

In 2012, 800×480 seems like the most common Android resolution for phones. I’ve heard some rumors on forums w/ no links to sources. Any sources I find, I’ll post here:

For UIToolkit, 800×480 can be our SD resolution and the size we should create the first sprite sheet for. So the .PSD files should be created at 16:10 aspect ratio for 800×480, or the SD UIToolkit sprite sheet in Unity.

I personally don’t care about the low density older Android phones – since I’m making 3D games using Unity, those phone’s processors & memory probably won’t be able to handle my app anyway, so why waste time on creating yet another sprite sheet for it?

Depending on whether or not you care about all of Android’s aspect ratios, supporting slightly older Android tablets or only the latest ones, or just the latest high density phones, you can set your UIToolkit HD sprite sheet(s) to, for example 1280×720 for the latest 2012 Samsung phones like the Galaxy SIII. Using 1280×720 or slightly less like 1160×720 to catch devices like the Galaxy Nexus phone, seems to also work on a few Android tables (like Motorola’s).

Remember, and prep your Designers for this ahead of time, it won’t look pixel perfect on every device. Android device screen size fragmentation is probably worse to deal with than cross-browser CSS problems (yes, even with IE6,7.8 vs the standards browsers on Mac and PC). Gently, remind Designers that an Android layout is not a PSD, PDF, an InDesign file or a printed page.

Some common Android phones with the medium 800×480, or the very close 854×480, screen size:

  • HTC Inspire 4G
  • Motorola DroidX
  • HTC Evo 4G

Sprite sheet problem

UIToolkit worked mostly well for iOS with 3 large, full-screen-sized background images even fitting inside the regular (SD) and 2x (HD) sprite sheet at 2048×2048 in TexurePacker Pro.

I just tried using a similar approach for Android and ran into a wall. The 2x sprite sheet is a problem – if for 2x, I’m sizing my graphics for HD Android phones like the Galaxy SIII at 1280×720… those 3 full-screen-sized background images don’t fit anymore inside 2048×2048. From what I’ve heard on the Unity forums Android has problems with images (sprite sheets) larger than 2048×2048. It’s not ideal, but I guess I can always proportionally scale those down for the sprite sheet and then use Screen.width and Screen.height to scale them. Given the client approved design, it’s nearly impossible, not to have them. The other approach would be to add a second sprite sheet, may be one for buttons & smaller UI stuff and one for unavoidable large background images. This will increase production time since extra sprite sheets will need to be created for SD and HD resolutions. & you’ll need to be careful which sprite sheet you’re targeting when from code.

Some Android-related posts on the main UIToolkit forum thread

Testing in Unity’s Game panel

  • To fake the 1280×720 screen size of the Samsung Galaxy SIII and similar new phones, switch the Game panel’s aspect ratio to Android Wide (16:10) and then stretch the panel till it approximates those dimensions. You can test them in the Console via Screen.width & Screen.height or use something like Screen Ruler or a PSD that’s at that size to approximate. Note: 1280×720 is actually 16:9, the 16:10 Android devices at that size are 1280×800, so keep that in mind.

On-device testing

  • I had trouble with “adb devices” not recognizing an HTC Inspire 4G and then a Motorola DroidX. I googled the crap out of it, read through every post on http://forum.xda-developers.com/ and still nothing. Then I borrowed a USB cable from a coworker who had an Android phone & boom! everything magically worked: adb devices listed both phones, Unity3D was able to publish directly to the device via “Build & Run”. Fun times.

PlayerPrefs.txt & Releasing Updates to iOS & Android Apps

  • Android: Looks like if you release an update to your app via Google Play, your PlayerPrefs.txt file will not be overwritten. I was able to confirm this from my own experience with a Google Play app.
  • Same deal for iOS, looks like an App Store update doesn’t overwrite PlayerPrefs.txt. I was able to confirm this from my own experience with a Unity3D-built app in Apple’s App Store.
  • Some Stuff About Google Play Sucks

    • If you’ve uploaded a new version of your app (a new APK), realize that this version has a bug, and decide to revert to the previous version, you can’t. Google Play doesn’t allow it as of October, 2012. If you’re the developer, you can compile your older source code with a newer Version Code (versionCode) and upload it as a NEWER APK and overwrite the problematic version. What a waste of my time!

    Grabbing Image Paths For All Images in Your App’s Local “/Documents” Directory Using Titanium SDK

    This is a quick example of how to grab local images that were earlier stored as PNG’s in your app’s /Documents folder and store their paths inside an array that can later be used to populate a View like CoverFlow, etc. Note: for CoverFlow, you’ll want to first resize your images down to a smaller widthxheight or CoverFlow will crash your app. I’m using the CommonJS modules approach to app structure. When testing in the Simulator, you can find your App’s /Documents folder here: /Users/your_User_Name/Library/Application Support/iPhone Simulator/5.0/Applications/FF203495C-R93475-466B-9103-EEEF0E9687B9/Documents/. A quick way to add some sample images to your Simulator’s /Documents folder, open Safari on the Simulator, find an image, Tap & Hold until the dialog comes up asking you to “Save Image”.

    function SecondView() {
    	
    	var self = Ti.UI.createView();	
    	var imgs = [];
    
    	/* 
    	 * 
    	 * The FILE system API is not fully documented on Appcelerator's website. 
    	 * For a more complete list of available methods for Ti.Filesystem check the Kitchen Sink's filesystem.js 
    	 * 
    	 * */
        				
    	function loadData() {	
    						
    		var dir = Ti.Filesystem.getFile(Titanium.Filesystem.applicationDataDirectory);
    		var fdir = dir.getDirectoryListing();
    		var numPhotos = fdir.length || 1;
    		 //Documents folder is empty on 1st load post install, 
    		 //so unless there's a default value, you get a blank screen... 	
    		
    		for(var a=0; a < numPhotos; a++ ) {
    		
    			var f = Titanium.Filesystem.getFile(Titanium.Filesystem.applicationDataDirectory,fdir[a]);
    			
    			if(f.exists())
    			{
    				var fr = f.read();		
    			
    				if(fr.mimeType === 'image/png'){
    					Ti.API.info('photo.nativePath: ' + f.nativePath);
    					imgs.push(f.nativePath); //imgs[a] = f.nativePath;				
    				}			
    			}						
    		}
    	}
    
    	loadData();
    	
    	return self;
    }
    
    module.exports = SecondView;
    
    

    Work in Progress: Choices for Mobile Development

    Example “every day” app request:

    Build me a “recipes” app that lets me browse resipes and take a photo of a dish and upload to Facebook App, Flickr account, Twitter feed, or Generic Server so it can later be displayed in a web view or another in-app view that gets refreshed after you get a “success” message back from the server that you uploaded to.

    Right this second, I’d personally code it in Objective-C/Xcode first, then do a Java version for Android.

    QUESTION: Given a bit more time and a chance to step back for a minute, which language, IDE, SDK should it be written in?

    Near Term Technology Choices

    • Corona SDK
    • Objective-C in Xcode for iOS / Java in Eclipse for Android
    • AIR 3.1 for mobile, CameraUI
    • PhoneGap
    • Titanium

    Corona SDK, media.Camera

    Cons

    • As of 02.20.2012, lack of support for Web Services (REST, SOAP, etc) processing. So far just HTTP POST & GET and sockets.
    • 2.5D is not easy in Corona SDK. Trying to “fake 3D” or do 2.5D by scaling a physics body while animating it’s position doesn’t work out of the box, for example. Various hacks are required to fake it. While you’re resizing the visual graphic for every frame, collision detection is likely to get screwed up because the physics body’s collision bounding box doesn’t resize “on enter frame” after it’s been set once. This is a Box2D limitation, from what I’ve heard. In general, Corona is best for 2D content only, at least for the moment.

    Pros

    • Holy shit! It’s so easy! If you have Actionscript experience (or Javascript application dev experience), Corona is a piece of cake. Feels easier than web Javascript (no DOM differences or old browsers to worry about). Here’s a Flash to Corona porting guide, their pitch to Flash Devs.
    • Has support for device hardware access to Camera (native Camera app can be call up) via media.show().
    • Has access to SQLlite.
    • No iAds support but has custom Ads framework with support for two ad networks.
    • Has built in Physics, Animation, Facebook, Analytics, JSON, In-App Purchases libraries.
    • Looks super approachable for the average AS3 developer. A bit more to learn for Front End devs who’ve never worked with sprite sheets, movieclips, easing equations, etc. Faster for Front End and Flash Developers to learn than learning about reference counting in Objective-C.

    Objective-C / Java native, UIImagePickerController or AV Foundation framework / android.hardware.Camera

    Cons

    • Building for iOS first (most likely). Will need to write separate Java version for Android. Two code bases to maintain.
    • Possible code base fragmentation between iPhone, iPad 2 and iPad 3, depending on how the app file size download limits issue plays out and whether. People are expecting x4 the pixels for iPad 3 high res display.
    • More dev time required

    Pros

    • Closer to the metal, more hands on control of everything in the iOS / Android SDK. Fewer problems, re: lack of full native support in 3rd party mobile tools? (a.k.a. no need to find or write “native extensions”).
    • Cleaner code – just try opening up a published Titanium project in Xcode and take a look at the crap load of native code that converts JS to Obj-C included that would not be there if you built your app using native code in the first place.
    • A lot of clients, for the moment, seem very into iPads and iOS, more so than into Android (not a long term bet, considering Android’s market share, even with the hardware fragmentation).

    AIR 3.1 for mobile, CameraUI and CameraRoll

    Cons

    • Adobe’s sloppy management of the Flash platform was a highly frustrating and uninspiring series of events. Between the buggy releases of Flash IDE, Flash Builder and the Flash browser plugin, especially on Mac OS X, and Adobe’s bumbling PR fiasco re:HTML5, it’s belated focus on the Gaming market and it’s late introduction of Stage3D for mobile devices Adobe is not a company that inspires me with confidence when it comes to IDE’s and SDK’s for any kind of code.
    • Lack of full native support in 3rd party mobile tools? (need to find or write “native extensions”).
    • Need to super optimize AS3.0 code. Possibly at the expense of code clarity.
    • To get access to the Gyroscope and Notifications, you need to use a Native Extensions
    • Potentially unable to use some commonly used libraries, for example, if they’re not compatible with Starling framework (Stage 3D) etc. or if they’re likely to affect performance on slower mobile processors

    Pros

    • There’s a legion of Devs with years of AS3.0 experience out there.
    • Tons of free resources all over the web.
    • I’m getting up to speed w/ Objective-C, have Java experience, how complex will it really be to write my own native extensions?
    • Possibility of a Single Code base for iOS and Android…

    PhoneGap

    Cons

    • Adobe…
    • Lack of full native support in 3rd party mobile tools? (need to find or write “native extensions” ?).
    • Will still require lots of conditional code / State design pattern to account for platform specific features as well as iPad v. iPhone, standard display v. high retina display, etc.

    Pros

    • PhoneGap has some elementary access to the iOS camera (at least).
    • More developers know Javascript, HTML, CSS or at least, those are easier to pick up than manual “reference counting” in Objective-C
    • Support for Web Services via Javascript, examples in their wiki.
    • Possibility of a Single Code base for iOS and Android…

    Titanium

    Cons

    • Lack of on device debugging in the FREE version of Titanium IDE itself. Looks like there’s a 3rd party tool that might work.
    • Incomplete documentation in Titanium’s API docs. For example, as of 03.19.2012, the Filesystem API doesn’t document methods like mimeType(), which is nontheless included in the Kitchen Sink’s filesystem.js example.
    • Lack of full native API support. Need to find or write modules for certain native features.
    • Will take months for Titanium to be updated for features in every new native OS release (like it did with the iOS 5 release).
    • Will sometimes still require lots of conditional code to account for platform specific features as well as iPad v. iPhone, standard display v. high retina display, iOS v. Android, etc. Perhaps, this is not a surprise, since for Universal apps in Xcode one sometimes has to create separate Views or .xib files for Universal apps, one for iPhone, one for iPad.
    • Titanium IDE was super buggy on my OS X 10.6.8 machines at work and home. SDK 1.8.2 seems to have fixed these issues.

    Pros

    • A reasonable level of Memory Profiling is possible for Titanium apps using Xcode’s Instruments.
    • Titanium offers you access to your device’s built-in basic camera app (UIImagePickerController on iOS), but it looks like a lot of the Titanium.Media camera related methods are iOS-only.
    • Enterprise-level support available, which Tech / IT Directors in charge of larger projects will find appealing.
    • Titanium allows developers to write their own modules, if Titanium itself is missing native features. There’s an iOS Module Developers Guide. For example, Titanium doesn’t support the iOS AV Foundation framework, the one that allows you to write your own custom Camera or Video application (as opposed to using UIImagePickerController, etc). Here’s a Titanium AV Foundation module written by a dev on GitHub (free, open source, under Apache License 2.0).
    • Has a Web Services (SOAP) example in it’s demo Kitchen Sink app. More developers already know Javascript.
    • Has iAds support. Possible to use other networks, like AdMob, through plugins/extensions (write yourself or get from their Open Mobile Marketplace)
    • Possibility of a Single Code base for iOS and Android…

    Eclipse links for Android w/ Java

    Add Missing Import Statements: Cmd-Shift-O
    “An easy way to add import packages to your project is to press Ctrl-Shift-O (Cmd-Shift-O, on Mac). This is an Eclipse shortcut that identifies missing packages based on your code and adds them for you.”

    More shortcuts:

    Eclipse themes w/dark background & light text