User Tools

Site Tools


android-labs-s14:class-09

Class 09

In this class, we'll be learning about:

  • Camera
  • External Storage
  • Google Maps
  • Getting your location from GPS and Network Provider
  • Publishing your App on PlayStore

Demo - 01

A quick look into what we're going to build next.

Camera

FIRSTLY, 'Edit' your existing Phone emulator to set both Front Camera and Back Camera to emulated. Launch your Phone emulator.

You can use Camera in two ways:

  1. [First] If you want normal Camera functionality, it's advisable to use already existing Camera application on your device to capture images/videos.
  2. [Second] Otherwise, create custom Camera interface as part of your app. The general steps for creating a custom camera interface for your applications are as follows:
    1. Detect and Access Camera - Create code to check for the existence of cameras and request access.
    2. Create a Preview Class - Create a camera preview class that extends SurfaceView and implements the SurfaceHolder interface. This class previews the live images from the camera.
    3. Build a Preview Layout - Once you have the camera preview class, create a view layout that incorporates the preview and the user interface controls you want.
    4. Setup Listeners for Capture - Connect listeners for your interface controls to start image or video capture in response to user actions, such as pressing a button.
    5. Capture and Save Files - Setup the code for capturing pictures or videos and saving the output.
    6. Release the Camera - After using the camera, your application must properly release it for use by other applications.

For simplicity, we'll consider ([First]) using an existing Camera application, where we just have to -

1. Compose a Camera Intent - Create an Intent that requests an image or video, using one of these intent types:

MediaStore.ACTION_IMAGE_CAPTURE - Intent action type for requesting an image from an existing camera application.
MediaStore.ACTION_VIDEO_CAPTURE - Intent action type for requesting a video from an existing camera application.

2. Start the Camera Intent - Use the startActivity() method to execute the camera intent. After you start the intent, the Camera application user interface appears on the device screen and the user can take a picture or video.

App: CaptureImage

Step 01: Create a Main Screen

Create a layout with just one button as shown below:

Step 02: Manifest Declarations

Before you start using the Camera API, please make sure you've made appropriate declarations in AndroidManifest.xml file to allow use of camera hardware and its features. (Add these xml tags as child of <manifest> tag.)

  • Permissions: You must request permission to use a device camera, and using external storage to store your pictures.
AndroidManifest.xml
	<uses-permission android:name="android.permission.CAMERA" />
	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • Required Features: You must also declare use of camera features, so that your app only get installed in the devices sufficing your feature requirements:
AndroidManifest.xml
	<uses-feature android:name="android.hardware.camera" />
	<uses-feature android:name="android.hardware.camera.autofocus" />
	<uses-feature android:name="android.hardware.camera.flash" />

Step 03: Create a File in External Storage

To get standard directory names, we can use public static methods of ''Environment'' class. For instance, Environment.getExternalStorageDirectory() returns primary external storage directory, which is mostly “/mnt/sdcard”. Similarly to get public directories inside /mnt/sdcard/, we can call Environment.getExternalStoragePublicDirectory() method. Then, we can simply create a file inside these directories.

So, following code snippet is a simple implementation to create a new file in SDCard meant for storing an image captured by Camera application.

MainActivity.java
	/** Get a File for saving an image or video */
	private File getOutputImageFile()
	{
		String YOUR_FIRST_NAME = "YourFirstName"; // Example: "Prakhar"
 
		// To be safe, you should check that the SDCard is mounted.
		String state = Environment.getExternalStorageState();
		if (!Environment.MEDIA_MOUNTED.equals(state))
		{
			Toast.makeText(this, "SD Card not mounted!", Toast.LENGTH_LONG).show();
			return null;
		}
 
		// SD Card works best if you want the created images to be shared
		// between applications and persist after your app has been uninstalled.
		File imageStorageDir = new File(
				Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
				YOUR_FIRST_NAME);
 
		// Create the storage directory if it does not exist
		if (!imageStorageDir.exists() && !imageStorageDir.mkdirs())
		{
			Toast.makeText(this, "Failed to create directory in SD Card", Toast.LENGTH_LONG).show();
			return null;
		}
 
		// Create a media file name
		String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
		File imageFile = new File(imageStorageDir.getPath() + File.separator + YOUR_FIRST_NAME
				+ "_" + timeStamp + ".jpg");
 
		return imageFile;
	}

Step 04: Using Intent to launch Camera application

Since, we're just interested in capturing an image, we'll just use an intent of type MediaStore.ACTION_IMAGE_CAPTURE. Through this intent, we can also specify the location of the output image file (after converting it to an URI) as an Extra parameter.

MainActivity::onButtonClick()
	// Create Intent to take a picture and return control to the calling application
	Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
 
	// Create a file to save the image
	File imageFile = getOutputImageFile();
	Uri imageUri = Uri.fromFile(imageFile);
 
	// Pass the image file name to the intent
	intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
 
	// Start the image capture Intent
	startActivity(intent);

Now after you click a picture from the Camera application, and come back to your application, you can then login into your device via adb shell command, and look for your captured images inside sdcard/Pictures/<YourFirstName> directory.


Demo - 02

A quick look into what we're going to build next.

App: GoogleMaps

Here are some useful links that may be used during entire process:

Step 01: Install required packages

  • To use GoogleMaps in your application, you first need to install following packages:
    • Android 4.4.2 (API Level 9)

  • Google Play Services (inside 'Extras')

  • Now, let's create a project with minimum API Level=11, as we'll be embedding map in a fragment.
  • Also, let's create another Phone Emulator with Target=“Google APIs (Google Inc.) - API Level 19”, as depicted in figure below:

Step 02 : Initial Setup

Using GoogleMaps is pretty straightforward, but it's the initial set up which takes time. Now, our next steps are:

  1. Add Google Play services as an Android library project.
  2. Reference the Google Play services in your app's project.
  3. Add the Google Play services version to your app's manifest.
  4. Get an Android certificate and the Google Maps API key.
  5. Add the API key to your application.
  6. Specify app settings (permissions + features) in the application manifest.

Let's use Google documentation to get started. Click here.

Step 03: Add a map

Let's create a layout (in activity_main.xml) with the following fragment snippet, holding three additional buttons (Hint: use RelativeLayout).

<fragment
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.google.android.gms.maps.MapFragment" />

If you launch your app at this point, you should see a GoogleMap showing up on your app's Main screen with panning and zoom control capability.

Step 04: Add marker (default & custom)

To add a marker, we'd first need to create a GoogleMap object from the map fragment we added earlier in activity_main.xml. Let's make a global object and initialize it in onCreate() method, as we could then reuse it in our button click callback methods later.

MainActivity.java
	private GoogleMap mGoogleMap;
 
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
 
		mGoogleMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
 
		/* 
		// You can use UiSettings to play around with the UI controls on a Google Map.
		UiSettings uiSettings = mGoogleMap.getUiSettings();
		uiSettings.setZoomControlsEnabled(false);
		uiSettings.setCompassEnabled(true);
		uiSettings.setMyLocationButtonEnabled(true);
		*/
	}

As, you'd have noticed, I added some commented code, which can let you enable/disable certain UI controls on the GoogleMaps.

Now, we can implement button click callback for Go to SF and Go to Madison buttons by adding the implementation described below.

Default Marker

For instance, say we know the latitude and longitude of San Francisco (CA), and wish to have a normal marker for the city, something as specified below:

So, here's how I can add a marker to that location:

	LatLng SAN_FRANCISCO = new LatLng(37.777499, -122.430662);
	mGoogleMap.addMarker(new MarkerOptions().position(SAN_FRANCISCO)
						.title("Bay Area"));

Custom Marker

Now, let's say, we know the latitude and longitude of Madison, and we wish to have a custom marker for the town, something as specified below:

In that case, we can create a marker with the implementation mentioned below:

	LatLng MADISON = new LatLng(43.074907, -89.419639);
	mGoogleMap.addMarker(new MarkerOptions().position(MADISON)
		.title("MadCity")
		.snippet("This is cool, right?")
		.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher)));

At this point, if you launch your app, you'll notice the markers at San Francisco and Madison, But, to go to these cities, you have to pan the map. In our next step, we'll see how we can move the camera while animating the movement using GoogleMap APIs.

Step 05: Moving the map to the desired location

To move a camera to a desired location, we can use moveCamera() method, while passing in LatLng object along with the zoom level. FYI, Zoom level can vary from 2.0 to 21.0. To animate the movement, you can use animateCamera() method of GoogleMap class.

In my implementation, I've added a utility method named 'moveToLocation()', which can be then reused in our click callback methods.

MainActivity.java
	private void moveToLocation(LatLng latlng, int zoomLevel /* Between 2 an 21 */, int animationDuration)
	{
		// Move the camera to the given location with a specific zoomlevel.
		mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latlng, zoomLevel));
 
		// Zoom in, animating the camera.
		mGoogleMap.animateCamera(CameraUpdateFactory.zoomTo(zoomLevel), animationDuration, null);
	}

Now, as just mentioned, you can call the above method from your earlier implemented click callbacks. For example,

	// Move the camera to Madison with a zoom of 12.
	moveToLocation(MADISON, 12, 2000);

Step 06: Get my location (Using GPS + Network Provider)

Now, as a click callback for Go to My Location button, we would first like to get our current location using GPS and Network Provider (WiFi or your mobile network). If we are successful in getting that information, we can then use GoogleMap to move to that location and add a marker at that place.

We can use GPS or Network Provider services through LocationManager class. If we're not able to get current information from either GPS or NetworkProvider (using requestLocationUpdated() method and LocationListener interface), we can try our chance to get last known information using getLastKnowLocation() method of LocationManager class.

Just a heads up: The implementation metioned below is highly unlikely to work on your emulator.

MainActivity.java
	private LocationManager mLocManager;
 
	public boolean getLocation()
	{
		mLocManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
 
		// Exceptions will be thrown if provider is not permitted.
		boolean gpsEnabled = false, networkEnabled = false;
		try
		{
			gpsEnabled = mLocManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
			networkEnabled = mLocManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
		} 
		catch (Exception ex)
		{
			Toast.makeText(this, "Some error occurred", Toast.LENGTH_SHORT).show();
			return false;
		}
 
		if (gpsEnabled)
			mLocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mLocListener);
		if (networkEnabled)
			mLocManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, mLocListener);
		else
			// Otherwise check last known location
			getLastKnownLocation();
 
		return true;
	}
 
	public void getLastKnownLocation()
	{
		mLocManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
 
		Location location = null;
		boolean networkEnabled = mLocManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
		if (networkEnabled)
			location = mLocManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
 
		if (location != null)
		{
			LatLng myLocation = new LatLng(location.getLatitude(), location.getLongitude());
 
			mGoogleMap.addMarker(new MarkerOptions().position(myLocation).title("Here I am!"));
 
			moveToLocation(myLocation, 10, 2000);
 
		} else
			Toast.makeText(this, "Sorry, cannot determine your current location.",
					Toast.LENGTH_LONG).show();
	}
 
	LocationListener mLocListener = new LocationListener()
	{
		public void onLocationChanged(Location location)
		{
			LatLng myLocation = new LatLng(location.getLatitude(), location.getLongitude());
 
			mGoogleMap.addMarker(new MarkerOptions().position(myLocation).title("Here I am!"));
 
			moveToLocation(myLocation, 10, 2000);
 
			// Preventing repetitive calls to onLocationChanged.
			mLocManager.removeUpdates(this);
			mLocManager.removeUpdates(mLocListener);
		}
 
		@Override
		public void onProviderDisabled(String provider) { }
 
		@Override
		public void onProviderEnabled(String provider) { }
 
		@Override
		public void onStatusChanged(String provider, int status, Bundle extras) { }
	};

And, finally you can use getLocation() method in your click callback for 'Go to My Location' button.


Publishing on Play Store

android-labs-s14/class-09.txt · Last modified: 2014/02/28 11:59 by prakhar