package org.sana.android.service;
import java.io.File;
import org.sana.android.Constants;
import android.content.ContentResolver;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
/**
* Handles preparing Intents used for launching data capture Activities through
* the ProcedureRunner class as well as post processing the returned data into
* a standardized Intent.
*
* @author Sana Development Team
*/
public class PluginService{
public static final String TAG = PluginService.class.getSimpleName();
/** Handles extracting the actual data, if available, when an intent is
* returned from startActivityForResult with code RESULT_OK. The data will
* be returned in an Intent with
*
* 1. data - Uri encoded as either:
* inline-data: data:[mime-type]#[data] Actual data can be retrieved by
* calling Uri.getFragment();
* content uri: content:[authority][path] Actual data can be retrieved
* using Android content resolution API
* 2. type - as determined by ContentResolver.getType() or text/plain
*
* Activity accepting the rendered Intent should call Intent.getData() and
* Intent.getType() to extract the information.
*
* Handles these situations:
* 1. Data is in result.getData()
* 2. Intent action is set to "inline-data" and data is present in extras
* through the "data" key-default for many Camera apps.
* 3. Data is returned as Intent.EXTRA_STREAM
* 4. Data is returned as Intent.EXTRA_TEXT
*
* @param cr //Deprecated as soon as we change this over to a Service class.
* @param result The result that came back to the original launching
* Activity
* @param obs The observation this was collected for
* @param hack String flag indicating this may require a hack.
* @return
*/
public static Intent renderPluginActivityResult(ContentResolver cr,
Intent result, Uri obs, String hack)
{
Log.d(TAG, "Rendering activity result.");
String type = "";
String text = "";
Intent rResult = new Intent();
Uri stream = null;
if (result != null){
logIntent(TAG, result);
Bundle extras = result.getExtras();
Uri data = result.getData();
String action = result.getAction();
type = result.getType();
// handle various returned data scenarios
// Standard
if(data != null){
// hack for applications which don't return type
if(TextUtils.isEmpty(type)){
type = cr.getType(data);
type = (TextUtils.isEmpty(type))? "application/octet-stream": type;
}
stream = data;
Log.d(TAG, "....getData() : " + stream.toString());
}
// Many camera apps return data as inline-data
else if(action != null && action.equals("inline-data")){
Bitmap b = result.getParcelableExtra("data");
Log.d(TAG, "inline-data : " + b);
// TODO fix this
File extDir = Environment.getExternalStorageDirectory();
File path = new File(extDir, Constants.PATH_OBSERVATION);
path.mkdirs();
File f = new File(path, obs.hashCode() + ".jpg");
stream = Uri.fromFile(f);
// TODO Write the bitmap to the File
type = (TextUtils.isEmpty(type))? "image/jpg": type;
Log.d(TAG, "....inline-data : " + stream);
}
// Scanner hack
else if(action != null && action.equals("com.google.zxing.client.android.SCAN")){
text = result.getStringExtra("SCAN_RESULT");
type = "text/plain";
stream = Uri.fromParts("data", type, text);
Log.d(TAG, "....com.google.zxing.client.android.SCAN : " + stream);
} else if(extras != null){
// What we really want for content
if(extras.containsKey(Intent.EXTRA_STREAM)){
stream = extras.getParcelable(Intent.EXTRA_STREAM);
type = cr.getType(stream);
type = (TextUtils.isEmpty(type))?
"application/octet-stream": type;
Log.d(TAG, "....EXTRA_STREAM : " + stream);
}else if(extras.containsKey(Intent.EXTRA_TEXT)){
text = extras.getString(Intent.EXTRA_TEXT);
type = "text/plain";
stream = Uri.fromParts("data", type, text);
Log.d(TAG, "....EXTRA_TEXT : " + text);
}
}
} else if(hasHack(hack)){
Log.d(TAG, "Attemptig hack");
type = renderHackType(hack);
stream = renderHackStream(hack, obs);
} else {
// To get here the Activity would have had to return a status
// RESULT_OK and null data Intent
throw new NullPointerException("NULL data returned by intent");
}
// set return properly
rResult.setDataAndType(stream, type);
Log.d(TAG, "Rendered intent: ");
logIntent(TAG, rResult);
return rResult;
}
// Infamous image hack for the Camera app. Put more here if we need it.
private static final boolean hasHack(String hack){
return hack.contains("image/");
}
// More hack
private static final String renderHackType(String hack){
return "image/jpg";
}
// Makes sure the obs file exists
private static final Uri renderHackStream(String hack, Uri obs){
File extDir = Environment.getExternalStorageDirectory();
File path = new File(extDir, Constants.PATH_OBSERVATION);
File f = new File(path, obs.hashCode() + ".jpg");
if(f.exists())
return Uri.fromFile(f);
else
throw new NullPointerException("NULL data. File Not Found");
}
/**
* Renders plug in intents for launching data capture activities. This
* method handles any of the odd behavior or parameters that need to
* be set such as with the stock Android Camera app. For most Activities
* this does nothing.
*
* @param intent The raw launch intent.
* @param obs The observation.
* @return
*/
public static Intent renderPluginLaunchIntent(Intent intent, Uri obs){
Intent rIntent = new Intent();
Log.d(TAG, "Rendering launch Intent....");
logIntent(TAG, intent);
String action = intent.getAction();
String mime = intent.getType();
if(!TextUtils.isEmpty(action)){
rIntent.setAction(action);
if(action.equals(Intent.ACTION_GET_CONTENT))
rIntent.setType(mime);
} else {
rIntent.setAction(Intent.ACTION_GET_CONTENT);
rIntent.setType(mime);
}
Log.d(TAG, "..obs: " + obs);
if(rIntent.getAction().equals(MediaStore.ACTION_IMAGE_CAPTURE)){
Log.d(TAG, "Got an ACTION_IMAGE_CAPTURE" );
rIntent = new Intent(intent.getAction());
File extDir = Environment.getExternalStorageDirectory();
File path = new File(extDir, Constants.PATH_OBSERVATION);
path.mkdirs();
File f = new File(path, obs.hashCode() + ".jpg");
Uri fUri = Uri.fromFile(f);
Log.d(TAG, "..output: " + fUri);
rIntent.putExtra(MediaStore.EXTRA_OUTPUT, fUri);
}
Log.d(TAG, "Rendered launch Intent....");
logIntent(TAG, rIntent);
return rIntent;
}
/**
* Log helper for pulling information out of Intents.
*
* @param tag The tag that should be associated with the log entry.
* @param intent The intent to analyze.
*/
public static void logIntent(String tag, Intent intent){
Log.d(tag, "Intent: " + intent.toUri(Intent.URI_INTENT_SCHEME));
Log.d(tag, "..action : " + intent.getAction());
Log.d(tag, "..data : " + intent.getData());
Log.d(tag, "..type : " + intent.getType());
Log.d(tag, "..scheme : " + intent.getScheme());
Bundle extras = intent.getExtras();
// Purely for debugging
if(extras != null){
Log.d(tag, "..extras : ");
for(String key:extras.keySet())
Log.d(tag, "...." + key + " : " + extras.get(key));
} else
Log.d(TAG, "..extras : null");
}
}