Order of Final Presentations

Hi Everyone,

So I just ran the application which determines order for the final presentations. Here is the output:

0: Rediet D. and Sara H. 4 4.8509746
1: Nahla I. 2 2.3797078
2: Oleg P. and James H. 3 3.1255593
3: Kharisa R. 0 0.31645668
4: Kun L. and Wei-Chung L. 1 1.3669138
5: Supatra L. 5 5.229145
Done

For reference, here is the app:

String[] student = {
     "Kharisa R.",
     "Kun L. and Wei-Chung L.",
     "Nahla I.",
     "Oleg P. and James H.",
     "Rediet D. and Sara H.",
     "Supatra L."
};

int[] choosen = new int[student.length];

void setup() {
  
  for (int c = 0; c < choosen.length; c++) {
    choosen[c] = -1; 
  }
  
  for (int i = 0; i < student.length; i++) {
    float choicef = random(0,student.length);
    int choice = floor(choicef);
    while (true) {
      boolean good = true;
      for (int c = 0; c < choosen.length && good; c++) {
        if (choosen[c] == choice) {
          good = false;
        }    
      }
      if (good) {
        break;
      } else {
        choicef = random(0,student.length);
        choice = floor(choicef);
      }
    }

    choosen[i] = choice;
    println("" + i + ": " + student[choice] + " " + choice + " " + choicef);
  }
  println("Done");
}

Extra Office Hours

Hi Everyone,

I’ll have the following office hours in the run up to our final presentations:

Friday, May 4, 2PM to 4PM
Sunday, May 6, 3PM to 5PM (Normal)
Monday, May 7, 10AM to 1PM

During each of these times, I’ll be in my office (Sama 1315).

See you all soon!

Mapping

Android, being intimately linked with Google, offers very robust integration with Google Maps.

In order to use Google Maps in Android, we have to use an external library. Although the Mapping capabilities are included in Android, to use them as a developer requires a few extra steps.
These steps are outlined here: http://code.google.com/android/add-ons/google-apis/

In short, we need to procure a Maps API Key for development (the process is a bit different when we are ready to distribute the application).

Using the debug/development key provided by the SDK we can generate a request for an API key.

First we hash the debug key (which on mac and linux is located here: ~/.android/debug.keystore)

keytool -list -alias androiddebugkey \
-keystore ~/.android/debug.keystore \
-storepass android -keypass android

which outputs (for me):

Certificate fingerprint (MD5): C8:9E:82:75:AE:AB:D2:72:A2:EE:7D:D2:33:BE:89:02

(Note: This might be a bit tricky on a PC. You may have to find the keytool.exe application which will be installed somewhere along with the JDK. When you run it, you’ll probably have to add the -v flag as well. Ignore everything but the MD5 fingerprint.

keytool.exe -v -list -alias androiddebugkey -keystore ~/.android/debug.keystore -storepass android -keypass android

)

We paste that fingerprint into the form on this page:

http://code.google.com/android/maps-api-signup.html

which spits back a key that can be used in the app when using the MapView view.

<com.google.android.maps.MapView
	android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:apiKey="YOUR API KEY"
/>

or

MapView mMapView = new MapView(this, "YOUR API KEY");

The application will also have to specify that it is using the maps 3rd party library in the application tag in the manifest:

   <uses-library android:name="com.google.android.maps" />

Here is an example: (Don’t forget to use an Emulator that includes the 3rd party APIs)

package com.mobvcasting.maptest;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;

import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;

public class MapTest extends MapActivity implements LocationListener {

	LocationManager lm; 
	MapView mv;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
		lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 6000l, 5.0f, this);
        
		mv = (MapView) this.findViewById(R.id.mapview);
    }

    public void onPause() {
		super.onPause(); 
		lm.removeUpdates(this);
	}

	public void onLocationChanged(Location location) {
		MapController mc = mv.getController();
		
		GeoPoint p = new GeoPoint((int) (location.getLatitude() * 1000000), (int) (location.getLongitude() * 1000000));
		mc.animateTo(p);
		//http://code.google.com/android/add-ons/google-apis/reference/com/google/android/maps/MapController.html#setZoom(int)
		mc.setZoom(14);
	}

	public void onProviderDisabled(String provider) {
		
	}

	public void onProviderEnabled(String provider) {
		
	}

	public void onStatusChanged(String provider, int status, Bundle extras) {
		
	}

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}
}

res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello"
    />
<com.google.android.maps.MapView
	android:layout_width="fill_parent"
	android:layout_height="fill_parent"
	android:apiKey="0Pr8kowSuIxjgvMYb7_ZWv0wWHjxVUZoyRGUGBA"
	android:id="@+id/mapview" />    
</LinearLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.mobvcasting.maptest"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MapTest"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
	    <uses-library android:name="com.google.android.maps" />
    </application>
    <uses-sdk android:minSdkVersion="8" />
	<uses-permission android:name="android.permission.INTERNET"></uses-permission>
	<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
</manifest> 

Good tutorial: http://www.androidcompetencycenter.com/2009/01/android-location-api/

Taking things a step further, we can update the code so that it includes a location marker.

Here is a full example: SimpleMap

Improved Simple HTTP Request

The last version of the Simple HTTP Request example had the issue of running the request on the same thread as the UI of our applications. This is a no-no in Android and has actually become harder to do with a recent version of Android (it gives an error).

The version below shows a similar HTTP Request being called when a button is pressed but instead of just the HTTP Request code being run in the onClick method, it is run in an AsyncTask which is in effect another thread.

ImprovedSimpleHTTPRequest.zip

PHP 101

PHP is a very useful language for doing many types of development. Primarily it is used for web development (hence the name, Hypertext Preprocessor) but it is certainly not limited to that. For our purposes we will be using PHP as both a web development tool and as a means to glue together various webservices that we might access through our mobile applications.

Basic HTML

<html> <!-- Required for all HTML pages -->
	<head> 
		<title>The Top Bar Title</title>
	</head>
	<body>
		Some information
		<!-- A Comment Tag -->
	</body>
</html> <!-- Closing tag, required -->

Basic HTML with PHP
PHP = Hypertext Preprocessor

<html> <!-- Required for all HTML pages -->
        <head>
                <title>The Top Bar Title</title>
        </head>
        <body>
                Some information
		<br> <!-- A line break -->
                <!-- A Comment Tag -->
		<?  // Denotes the start of PHP processing
        		echo "The Date and Time is: ";  // print a string, end with a semicolon
        		$mydata = "July 29, 2004"; // Assign a variable 
        		echo $mydata; // print the string variable
		?> 

        </body>
</html> <!-- Closing tag, required -->

Take note of the different comment styles.
The “//” style within PHP and “<--" outside of PHP (normal HTML)
Variables begin with "$".
"=" are assignment.
"echo" is the print function.
";" ends line of code.
PHP files are saved with ".php" not ".html"

Creating a PHP file for use on the command line

PHP can be developed in any text editor and doesn’t require any compilation.

More Information:
PHP: Basic Syntax – Manual

Variables

Variables in PHP are not strictly typed, meaning that you do not have to differentiate between strings, integers and so on. They start with a “$”.

<?PHP
	$myString = "hello world\n";
	echo($myString);
	$myInteger = 1003;
	echo($myInteger . "\n");
	$somethingelse = false;
	echo($somethingelse . "\n");
?>

More Information:
PHP: Types – Manual
PHP: Variables – Manual
PHP: Constants – Manual

Mathematical Operators

<?PHP
	$aValue = 0;
	$aValue++;
	echo("aValue now = " . $aValue . "\n");
	$aValue = $aValue + $aValue;
	echo("aValue now = " . $aValue . "\n");
	// % + - * / ++ -- and so on, same as in Processing/Java
?>

More Information:
PHP: Expressions – Manual
PHP: Operators – Manual

Control Structures, Logical Operators and Loops

<?PHP
		
	// If Statement
	$aValue = 0;
	if ($aValue == 0)
	{
		echo("aValue is 0");
	}
	else if ($aValue == 1)
	{
		echo("aValue is 1");
	}
	else if ($aValue >= 2)
	{
		echo("aValue is greater than or equal to 2");
	}
	else
	{
		echo("aValue something else");
	}
	echo("\n");
	// Other Logical Operators ==, >, <, >=, <=, ||, &&

		
	// For loop
	for ($i = 0; $i < 10; $i++)
	{
		echo("\$i = $i\n");
	}
			
	// Also While
?>

More Information:
PHP: Control Structures - Manual

Arrays and Loops

<?PHP
	// Pretty normal array
	$anArray = array();
	$anArray[0] = "Something";
	$anArray[1] = "Something Else";
	for ($i = 0; $i < sizeof($anArray); $i++)
	{
		echo($anArray[$i] . "\n");
	}
			
	// Key Value Map
	$anotherA = array("somekey" => "somevalue", "someotherkey" => "someothervalue");
	$keys = array_keys($anotherA);
	$values = array_values($anotherA);
	for ($i = 0; $i < sizeof($keys); $i++)
	{
		echo($keys[$i] . " = " . $values[$i] . "\n");
	}
?>

More Information:
PHP: Arrays - Manual

Functions

<?PHP
			
	function myFunction($somearg)
	{
		// You would do something here
		return "You passed in $somearg";
	}
			
	$passing_in = "Hello World";
	$return_value = myFunction($passing_in);
	echo($return_value);
			
	echo("\n");
?>

More Information:
PHP: Functions - Manual

Classes and Objects

<?PHP
	class MyClass
	{
		var $myClassVar;

		function set_var($new_var)
		{
			$this->myClassVar = $new_var;
		}

		function get_var()
		{
			return $this->myClassVar;
		}
	}
	
	$aClass = new MyClass;
	$aClass->set_var("something");
	echo("Var: " . $aClass->get_var() . "\n");		
?>

Classes and Objects in PHP are very similar to Java/Processing. Syntactically though they don't use the "." operator, instead using "->". Also, in the class definition you need to use the "this" keyword.

More Information:
PHP: Classes and Objects - Manual

Some Interesting Functions

isset()

<?PHP
	$somevar = NULL;
	if (isset($somevar))
	{
		echo("somevar is set");	
	}
	else
	{
		echo("somevar is NOT set");
	}
?>

mail

	$mailmsg = "A Mail Message";
	$mailsubject = "A Mail Subject";
	$mailfrom = "vanevery@walking-productions.com";
	$mailto =  "vanevery@walking-productions.com;
	$mailheaders = 'From: ' . $mailfrom . "\n"; 
	$mailheaders .= 'Reply-To: ' . $mailfrom . "\n"; 
	$mailheaders .= 'Return-Path: ' . $mailfrom . "\n";
	$mailheaders .= "Message-ID: <".time().">\n"; 
	$mailheaders .= "X-Mailer: Shawns PHP Mailer Function\n";          
	ini_set('sendmail_from',$mailfrom);
	$return = mail($mailto, $mailsubject, $mailmsg, $mailheaders);
	ini_restore('sendmail_from'); 
	echo "Mail Sent?: ";
	var_dump($return);		

More information:
PHP: Mail - Manual

Tons More Here: PHP: Function Reference - Manual

Custom Camera – Preview Callback

Warning, this is messy proof of concept code. Thanks to this: http://stackoverflow.com/questions/8350230/android-how-to-display-camera-preview-with-callback

package com.apress.proandroidmedia.ch2.snapshot;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;

import android.app.Activity;
import android.content.ContentValues;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.hardware.Camera;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore.Images.Media;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.Toast;

public class SnapShot extends Activity implements OnClickListener,
		SurfaceHolder.Callback, Camera.PictureCallback, Camera.PreviewCallback {

	SurfaceView cameraView;
	SurfaceHolder surfaceHolder;
	Camera camera;
	ImageView imgView;
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.main);

		cameraView = (SurfaceView) this.findViewById(R.id.CameraView);
		surfaceHolder = cameraView.getHolder();
		surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		surfaceHolder.addCallback(this);

		cameraView.setFocusable(true);
		cameraView.setFocusableInTouchMode(true);
		cameraView.setClickable(true);

		cameraView.setOnClickListener(this);
		
		imgView = (ImageView) this.findViewById(R.id.ImageView);
	}

	public void onClick(View v) {
		//camera.takePicture(null, null, this);
		camera.setOneShotPreviewCallback(this);
	}

	public void onPictureTaken(byte[] data, Camera camera) {
		Uri imageFileUri = getContentResolver().insert(
				Media.EXTERNAL_CONTENT_URI, new ContentValues());
		try {
			OutputStream imageFileOS = getContentResolver().openOutputStream(
					imageFileUri);
			imageFileOS.write(data);
			imageFileOS.flush();
			imageFileOS.close();
		} catch (FileNotFoundException e) {
			Toast t = Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT);
			t.show();
		} catch (IOException e) {
			Toast t = Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT);
			t.show();
		}
		camera.startPreview();
	}

	public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
		camera.startPreview();
	}

	public void surfaceCreated(SurfaceHolder holder) {
		camera = Camera.open();
		try {
			camera.setPreviewDisplay(holder);
			Camera.Parameters parameters = camera.getParameters();
			if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
				parameters.set("orientation", "portrait");

				// For Android Version 2.2 and above
				camera.setDisplayOrientation(90);

				// For Android Version 2.0 and above
				parameters.setRotation(90);
			}

			parameters.setExposureCompensation(parameters.getMinExposureCompensation());
			
			// Effects are for Android Version 2.0 and higher
			List colorEffects = parameters.getSupportedColorEffects();
			Iterator cei = colorEffects.iterator();
			while (cei.hasNext()) {
				String currentEffect = cei.next();
				if (currentEffect.equals(Camera.Parameters.EFFECT_SOLARIZE)) {
					parameters
							.setColorEffect(Camera.Parameters.EFFECT_SOLARIZE);
					break;
				}
			}
			// End Effects for Android Version 2.0 and higher

			camera.setParameters(parameters);
		} catch (IOException exception) {
			camera.release();
		}
	}

	public void surfaceDestroyed(SurfaceHolder holder) {
		camera.stopPreview();
		camera.release();
	}
	
	public void onPreviewFrame(byte[] data, Camera camera) {
		int height = camera.getParameters().getPreviewSize().height;
		int width = camera.getParameters().getPreviewSize().width;
		
	    int frameSize = width*height;
	    int[] rgba = new int[frameSize+1];

	    // Convert YUV to RGB
	    for (int i = 0; i < height; i++)
	        for (int j = 0; j < width; j++) {
	            int y = (0xff & ((int) data[i * width + j]));
	            int u = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 0]));
	            int v = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 1]));
	            y = y < 16 ? 16 : y;

	            int r = Math.round(1.164f * (y - 16) + 1.596f * (v - 128));
	            int g = Math.round(1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
	            int b = Math.round(1.164f * (y - 16) + 2.018f * (u - 128));

	            r = r < 0 ? 0 : (r > 255 ? 255 : r);
	            g = g < 0 ? 0 : (g > 255 ? 255 : g);
	            b = b < 0 ? 0 : (b > 255 ? 255 : b);

	            rgba[i * width + j] = 0xff000000 + (b << 16) + (g << 8) + r;
	        }

	    Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
	    bmp.setPixels(rgba, 0/* offset */, width /* stride */, 0, 0, width, height);
	    imgView.setImageBitmap(bmp);
	}

} // End the Activity

Custom Camera

Using straight Android we can build a custom camera Activity.

Here are the basics:

package com.mobvcasting.snapshot;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;

import android.app.Activity;
import android.content.ContentValues;
import android.content.res.Configuration;
import android.hardware.Camera;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore.Images.Media;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;

public class SnapShot extends Activity implements OnClickListener,
		SurfaceHolder.Callback, Camera.PictureCallback {

	SurfaceView cameraView;
	SurfaceHolder surfaceHolder;
	Camera camera;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.main);

		cameraView = (SurfaceView) this.findViewById(R.id.CameraView);
		surfaceHolder = cameraView.getHolder();
		surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		surfaceHolder.addCallback(this);

		cameraView.setFocusable(true);
		cameraView.setFocusableInTouchMode(true);
		cameraView.setClickable(true);

		cameraView.setOnClickListener(this);
	}

	public void onClick(View v) {
		camera.takePicture(null, null, this);
	}

	public void onPictureTaken(byte[] data, Camera camera) {
// data is a byte array already in JPEG form
		Uri imageFileUri = getContentResolver().insert(
				Media.EXTERNAL_CONTENT_URI, new ContentValues());
		try {
			OutputStream imageFileOS = getContentResolver().openOutputStream(
					imageFileUri);
			imageFileOS.write(data);
			imageFileOS.flush();
			imageFileOS.close();
		} catch (FileNotFoundException e) {
			Toast t = Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT);
			t.show();
		} catch (IOException e) {
			Toast t = Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT);
			t.show();
		}
		camera.startPreview();
	}

	public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
		camera.startPreview();
	}

	public void surfaceCreated(SurfaceHolder holder) {
		camera = Camera.open();
		try {
			camera.setPreviewDisplay(holder);
			Camera.Parameters parameters = camera.getParameters();
			if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
				parameters.set("orientation", "portrait");

				// For Android Version 2.2 and above
				camera.setDisplayOrientation(90);

				// For Android Version 2.0 and above
				parameters.setRotation(90);
			}

			

			camera.setParameters(parameters);
		} catch (IOException exception) {
			camera.release();
		}
	}

	public void surfaceDestroyed(SurfaceHolder holder) {
		camera.stopPreview();
		camera.release();
	}
} // End the Activity

Here is the corresponding main.xml file from res/layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<SurfaceView android:id="@+id/CameraView" android:layout_width="fill_parent" android:layout_height="fill_parent">

Don't forget to add CAMERA permissions into your AndroidManifest.xml file.