blob: f7d442a50deb7679bbd75b8fc33d68c65c3b61c6 [file] [log] [blame]
/*
* Copyright (C) 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.googlecode.android_scripting.facade;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.StatFs;
import android.os.Vibrator;
import android.text.ClipboardManager;
import android.text.InputType;
import android.text.method.PasswordTransformationMethod;
import android.widget.EditText;
import android.widget.Toast;
import com.googlecode.android_scripting.BaseApplication;
import com.googlecode.android_scripting.FileUtils;
import com.googlecode.android_scripting.FutureActivityTaskExecutor;
import com.googlecode.android_scripting.Log;
import com.googlecode.android_scripting.NotificationIdFactory;
import com.googlecode.android_scripting.future.FutureActivityTask;
import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
import com.googlecode.android_scripting.rpc.Rpc;
import com.googlecode.android_scripting.rpc.RpcDefault;
import com.googlecode.android_scripting.rpc.RpcDeprecated;
import com.googlecode.android_scripting.rpc.RpcOptional;
import com.googlecode.android_scripting.rpc.RpcParameter;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Some general purpose Android routines.<br>
* <h2>Intents</h2> Intents are returned as a map, in the following form:<br>
* <ul>
* <li><b>action</b> - action.
* <li><b>data</b> - url
* <li><b>type</b> - mime type
* <li><b>packagename</b> - name of package. If used, requires classname to be useful (optional)
* <li><b>classname</b> - name of class. If used, requires packagename to be useful (optional)
* <li><b>categories</b> - list of categories
* <li><b>extras</b> - map of extras
* <li><b>flags</b> - integer flags.
* </ul>
* <br>
* An intent can be built using the {@see #makeIntent} call, but can also be constructed exterally.
*
*/
public class AndroidFacade extends RpcReceiver {
/**
* An instance of this interface is passed to the facade. From this object, the resource IDs can
* be obtained.
*/
public interface Resources {
int getLogo48();
}
private final Service mService;
private final Handler mHandler;
private final Intent mIntent;
private final FutureActivityTaskExecutor mTaskQueue;
private final Vibrator mVibrator;
private final NotificationManager mNotificationManager;
private final Resources mResources;
private ClipboardManager mClipboard = null;
@Override
public void shutdown() {
}
public AndroidFacade(FacadeManager manager) {
super(manager);
mService = manager.getService();
mIntent = manager.getIntent();
BaseApplication application = ((BaseApplication) mService.getApplication());
mTaskQueue = application.getTaskExecutor();
mHandler = new Handler(mService.getMainLooper());
mVibrator = (Vibrator) mService.getSystemService(Context.VIBRATOR_SERVICE);
mNotificationManager =
(NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
mResources = manager.getAndroidFacadeResources();
}
ClipboardManager getClipboardManager() {
Object clipboard = null;
if (mClipboard == null) {
try {
clipboard = mService.getSystemService(Context.CLIPBOARD_SERVICE);
} catch (Exception e) {
Looper.prepare(); // Clipboard manager won't work without this on higher SDK levels...
clipboard = mService.getSystemService(Context.CLIPBOARD_SERVICE);
}
mClipboard = (ClipboardManager) clipboard;
if (mClipboard == null) {
Log.w("Clipboard managed not accessible.");
}
}
return mClipboard;
}
/**
* Creates a new AndroidFacade that simplifies the interface to various Android APIs.
*
* @param service
* is the {@link Context} the APIs will run under
*/
@Rpc(description = "Put text in the clipboard.")
public void setClipboard(@RpcParameter(name = "text") String text) {
getClipboardManager().setText(text);
}
@Rpc(description = "Read text from the clipboard.", returns = "The text in the clipboard.")
public String getClipboard() {
CharSequence text = getClipboardManager().getText();
return text == null ? null : text.toString();
}
Intent startActivityForResult(final Intent intent) {
FutureActivityTask<Intent> task = new FutureActivityTask<Intent>() {
@Override
public void onCreate() {
super.onCreate();
try {
startActivityForResult(intent, 0);
} catch (Exception e) {
intent.putExtra("EXCEPTION", e.getMessage());
setResult(intent);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
setResult(data);
}
};
mTaskQueue.execute(task);
try {
return task.getResult();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
task.finish();
}
}
// TODO(damonkohler): Pull this out into proper argument deserialization and support
// complex/nested types being passed in.
public static void putExtrasFromJsonObject(JSONObject extras, Intent intent) throws JSONException {
JSONArray names = extras.names();
for (int i = 0; i < names.length(); i++) {
String name = names.getString(i);
Object data = extras.get(name);
if (data == null) {
continue;
}
if (data instanceof Integer) {
intent.putExtra(name, (Integer) data);
}
if (data instanceof Float) {
intent.putExtra(name, (Float) data);
}
if (data instanceof Double) {
intent.putExtra(name, (Double) data);
}
if (data instanceof Long) {
intent.putExtra(name, (Long) data);
}
if (data instanceof String) {
intent.putExtra(name, (String) data);
}
if (data instanceof Boolean) {
intent.putExtra(name, (Boolean) data);
}
// Nested JSONObject
if (data instanceof JSONObject) {
Bundle nestedBundle = new Bundle();
intent.putExtra(name, nestedBundle);
putNestedJSONObject((JSONObject) data, nestedBundle);
}
// Nested JSONArray. Doesn't support mixed types in single array
if (data instanceof JSONArray) {
// Empty array. No way to tell what type of data to pass on, so skipping
if (((JSONArray) data).length() == 0) {
Log.e("Empty array not supported in JSONObject, skipping");
continue;
}
// Integer
if (((JSONArray) data).get(0) instanceof Integer) {
Integer[] integerArrayData = new Integer[((JSONArray) data).length()];
for (int j = 0; j < ((JSONArray) data).length(); ++j) {
integerArrayData[j] = ((JSONArray) data).getInt(j);
}
intent.putExtra(name, integerArrayData);
}
// Double
if (((JSONArray) data).get(0) instanceof Double) {
Double[] doubleArrayData = new Double[((JSONArray) data).length()];
for (int j = 0; j < ((JSONArray) data).length(); ++j) {
doubleArrayData[j] = ((JSONArray) data).getDouble(j);
}
intent.putExtra(name, doubleArrayData);
}
// Long
if (((JSONArray) data).get(0) instanceof Long) {
Long[] longArrayData = new Long[((JSONArray) data).length()];
for (int j = 0; j < ((JSONArray) data).length(); ++j) {
longArrayData[j] = ((JSONArray) data).getLong(j);
}
intent.putExtra(name, longArrayData);
}
// String
if (((JSONArray) data).get(0) instanceof String) {
String[] stringArrayData = new String[((JSONArray) data).length()];
for (int j = 0; j < ((JSONArray) data).length(); ++j) {
stringArrayData[j] = ((JSONArray) data).getString(j);
}
intent.putExtra(name, stringArrayData);
}
// Boolean
if (((JSONArray) data).get(0) instanceof Boolean) {
Boolean[] booleanArrayData = new Boolean[((JSONArray) data).length()];
for (int j = 0; j < ((JSONArray) data).length(); ++j) {
booleanArrayData[j] = ((JSONArray) data).getBoolean(j);
}
intent.putExtra(name, booleanArrayData);
}
}
}
}
// Contributed by Emmanuel T
// Nested Array handling contributed by Sergey Zelenev
private static void putNestedJSONObject(JSONObject jsonObject, Bundle bundle)
throws JSONException {
JSONArray names = jsonObject.names();
for (int i = 0; i < names.length(); i++) {
String name = names.getString(i);
Object data = jsonObject.get(name);
if (data == null) {
continue;
}
if (data instanceof Integer) {
bundle.putInt(name, ((Integer) data).intValue());
}
if (data instanceof Float) {
bundle.putFloat(name, ((Float) data).floatValue());
}
if (data instanceof Double) {
bundle.putDouble(name, ((Double) data).doubleValue());
}
if (data instanceof Long) {
bundle.putLong(name, ((Long) data).longValue());
}
if (data instanceof String) {
bundle.putString(name, (String) data);
}
if (data instanceof Boolean) {
bundle.putBoolean(name, ((Boolean) data).booleanValue());
}
// Nested JSONObject
if (data instanceof JSONObject) {
Bundle nestedBundle = new Bundle();
bundle.putBundle(name, nestedBundle);
putNestedJSONObject((JSONObject) data, nestedBundle);
}
// Nested JSONArray. Doesn't support mixed types in single array
if (data instanceof JSONArray) {
// Empty array. No way to tell what type of data to pass on, so skipping
if (((JSONArray) data).length() == 0) {
Log.e("Empty array not supported in nested JSONObject, skipping");
continue;
}
// Integer
if (((JSONArray) data).get(0) instanceof Integer) {
int[] integerArrayData = new int[((JSONArray) data).length()];
for (int j = 0; j < ((JSONArray) data).length(); ++j) {
integerArrayData[j] = ((JSONArray) data).getInt(j);
}
bundle.putIntArray(name, integerArrayData);
}
// Double
if (((JSONArray) data).get(0) instanceof Double) {
double[] doubleArrayData = new double[((JSONArray) data).length()];
for (int j = 0; j < ((JSONArray) data).length(); ++j) {
doubleArrayData[j] = ((JSONArray) data).getDouble(j);
}
bundle.putDoubleArray(name, doubleArrayData);
}
// Long
if (((JSONArray) data).get(0) instanceof Long) {
long[] longArrayData = new long[((JSONArray) data).length()];
for (int j = 0; j < ((JSONArray) data).length(); ++j) {
longArrayData[j] = ((JSONArray) data).getLong(j);
}
bundle.putLongArray(name, longArrayData);
}
// String
if (((JSONArray) data).get(0) instanceof String) {
String[] stringArrayData = new String[((JSONArray) data).length()];
for (int j = 0; j < ((JSONArray) data).length(); ++j) {
stringArrayData[j] = ((JSONArray) data).getString(j);
}
bundle.putStringArray(name, stringArrayData);
}
// Boolean
if (((JSONArray) data).get(0) instanceof Boolean) {
boolean[] booleanArrayData = new boolean[((JSONArray) data).length()];
for (int j = 0; j < ((JSONArray) data).length(); ++j) {
booleanArrayData[j] = ((JSONArray) data).getBoolean(j);
}
bundle.putBooleanArray(name, booleanArrayData);
}
}
}
}
void startActivity(final Intent intent) {
try {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mService.startActivity(intent);
} catch (Exception e) {
Log.e("Failed to launch intent.", e);
}
}
private Intent buildIntent(String action, String uri, String type, JSONObject extras,
String packagename, String classname, JSONArray categories) throws JSONException {
Intent intent = new Intent(action);
intent.setDataAndType(uri != null ? Uri.parse(uri) : null, type);
if (packagename != null && classname != null) {
intent.setComponent(new ComponentName(packagename, classname));
}
if (extras != null) {
putExtrasFromJsonObject(extras, intent);
}
if (categories != null) {
for (int i = 0; i < categories.length(); i++) {
intent.addCategory(categories.getString(i));
}
}
return intent;
}
// TODO(damonkohler): It's unnecessary to add the complication of choosing between startActivity
// and startActivityForResult. It's probably better to just always use the ForResult version.
// However, this makes the call always blocking. We'd need to add an extra boolean parameter to
// indicate if we should wait for a result.
@Rpc(description = "Starts an activity and returns the result.", returns = "A Map representation of the result Intent.")
public Intent startActivityForResult(
@RpcParameter(name = "action") String action,
@RpcParameter(name = "uri") @RpcOptional String uri,
@RpcParameter(name = "type", description = "MIME type/subtype of the URI") @RpcOptional String type,
@RpcParameter(name = "extras", description = "a Map of extras to add to the Intent") @RpcOptional JSONObject extras,
@RpcParameter(name = "packagename", description = "name of package. If used, requires classname to be useful") @RpcOptional String packagename,
@RpcParameter(name = "classname", description = "name of class. If used, requires packagename to be useful") @RpcOptional String classname)
throws JSONException {
final Intent intent = buildIntent(action, uri, type, extras, packagename, classname, null);
return startActivityForResult(intent);
}
@Rpc(description = "Starts an activity and returns the result.", returns = "A Map representation of the result Intent.")
public Intent startActivityForResultIntent(
@RpcParameter(name = "intent", description = "Intent in the format as returned from makeIntent") Intent intent) {
return startActivityForResult(intent);
}
private void doStartActivity(final Intent intent, Boolean wait) throws Exception {
if (wait == null || wait == false) {
startActivity(intent);
} else {
FutureActivityTask<Intent> task = new FutureActivityTask<Intent>() {
private boolean mSecondResume = false;
@Override
public void onCreate() {
super.onCreate();
startActivity(intent);
}
@Override
public void onResume() {
if (mSecondResume) {
finish();
}
mSecondResume = true;
}
@Override
public void onDestroy() {
setResult(null);
}
};
mTaskQueue.execute(task);
try {
task.getResult();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
/**
* packagename and classname, if provided, are used in a 'setComponent' call.
*/
@Rpc(description = "Starts an activity.")
public void startActivity(
@RpcParameter(name = "action") String action,
@RpcParameter(name = "uri") @RpcOptional String uri,
@RpcParameter(name = "type", description = "MIME type/subtype of the URI") @RpcOptional String type,
@RpcParameter(name = "extras", description = "a Map of extras to add to the Intent") @RpcOptional JSONObject extras,
@RpcParameter(name = "wait", description = "block until the user exits the started activity") @RpcOptional Boolean wait,
@RpcParameter(name = "packagename", description = "name of package. If used, requires classname to be useful") @RpcOptional String packagename,
@RpcParameter(name = "classname", description = "name of class. If used, requires packagename to be useful") @RpcOptional String classname)
throws Exception {
final Intent intent = buildIntent(action, uri, type, extras, packagename, classname, null);
doStartActivity(intent, wait);
}
@Rpc(description = "Send a broadcast.")
public void sendBroadcast(
@RpcParameter(name = "action") String action,
@RpcParameter(name = "uri") @RpcOptional String uri,
@RpcParameter(name = "type", description = "MIME type/subtype of the URI") @RpcOptional String type,
@RpcParameter(name = "extras", description = "a Map of extras to add to the Intent") @RpcOptional JSONObject extras,
@RpcParameter(name = "packagename", description = "name of package. If used, requires classname to be useful") @RpcOptional String packagename,
@RpcParameter(name = "classname", description = "name of class. If used, requires packagename to be useful") @RpcOptional String classname)
throws JSONException {
final Intent intent = buildIntent(action, uri, type, extras, packagename, classname, null);
try {
mService.sendBroadcast(intent);
} catch (Exception e) {
Log.e("Failed to broadcast intent.", e);
}
}
@Rpc(description = "Create an Intent.", returns = "An object representing an Intent")
public Intent makeIntent(
@RpcParameter(name = "action") String action,
@RpcParameter(name = "uri") @RpcOptional String uri,
@RpcParameter(name = "type", description = "MIME type/subtype of the URI") @RpcOptional String type,
@RpcParameter(name = "extras", description = "a Map of extras to add to the Intent") @RpcOptional JSONObject extras,
@RpcParameter(name = "categories", description = "a List of categories to add to the Intent") @RpcOptional JSONArray categories,
@RpcParameter(name = "packagename", description = "name of package. If used, requires classname to be useful") @RpcOptional String packagename,
@RpcParameter(name = "classname", description = "name of class. If used, requires packagename to be useful") @RpcOptional String classname,
@RpcParameter(name = "flags", description = "Intent flags") @RpcOptional Integer flags)
throws JSONException {
Intent intent = buildIntent(action, uri, type, extras, packagename, classname, categories);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (flags != null) {
intent.setFlags(flags);
}
return intent;
}
@Rpc(description = "Start Activity using Intent")
public void startActivityIntent(
@RpcParameter(name = "intent", description = "Intent in the format as returned from makeIntent") Intent intent,
@RpcParameter(name = "wait", description = "block until the user exits the started activity") @RpcOptional Boolean wait)
throws Exception {
doStartActivity(intent, wait);
}
@Rpc(description = "Send Broadcast Intent")
public void sendBroadcastIntent(
@RpcParameter(name = "intent", description = "Intent in the format as returned from makeIntent") Intent intent)
throws Exception {
mService.sendBroadcast(intent);
}
@Rpc(description = "Vibrates the phone or a specified duration in milliseconds.")
public void vibrate(
@RpcParameter(name = "duration", description = "duration in milliseconds") @RpcDefault("300") Integer duration) {
mVibrator.vibrate(duration);
}
@Rpc(description = "Displays a short-duration Toast notification.")
public void makeToast(@RpcParameter(name = "message") final String message) {
mHandler.post(new Runnable() {
public void run() {
Toast.makeText(mService, message, Toast.LENGTH_SHORT).show();
}
});
}
private String getInputFromAlertDialog(final String title, final String message,
final boolean password) {
final FutureActivityTask<String> task = new FutureActivityTask<String>() {
@Override
public void onCreate() {
super.onCreate();
final EditText input = new EditText(getActivity());
if (password) {
input.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
input.setTransformationMethod(new PasswordTransformationMethod());
}
AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());
alert.setTitle(title);
alert.setMessage(message);
alert.setView(input);
alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
setResult(input.getText().toString());
finish();
}
});
alert.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
dialog.dismiss();
setResult(null);
finish();
}
});
alert.show();
}
};
mTaskQueue.execute(task);
try {
return task.getResult();
} catch (Exception e) {
Log.e("Failed to display dialog.", e);
throw new RuntimeException(e);
}
}
@Rpc(description = "Queries the user for a text input.")
@RpcDeprecated(value = "dialogGetInput", release = "r3")
public String getInput(
@RpcParameter(name = "title", description = "title of the input box") @RpcDefault("SL4A Input") final String title,
@RpcParameter(name = "message", description = "message to display above the input box") @RpcDefault("Please enter value:") final String message) {
return getInputFromAlertDialog(title, message, false);
}
@Rpc(description = "Queries the user for a password.")
@RpcDeprecated(value = "dialogGetPassword", release = "r3")
public String getPassword(
@RpcParameter(name = "title", description = "title of the input box") @RpcDefault("SL4A Password Input") final String title,
@RpcParameter(name = "message", description = "message to display above the input box") @RpcDefault("Please enter password:") final String message) {
return getInputFromAlertDialog(title, message, true);
}
@Rpc(description = "Displays a notification that will be canceled when the user clicks on it.")
public void notify(@RpcParameter(name = "title", description = "title") String title,
@RpcParameter(name = "message") String message) {
Notification notification =
new Notification(mResources.getLogo48(), message, System.currentTimeMillis());
// This contentIntent is a noop.
PendingIntent contentIntent = PendingIntent.getService(mService, 0, new Intent(), 0);
notification.setLatestEventInfo(mService, title, message, contentIntent);
notification.flags = Notification.FLAG_AUTO_CANCEL;
// Get a unique notification id from the application.
final int notificationId = NotificationIdFactory.create();
mNotificationManager.notify(notificationId, notification);
}
@Rpc(description = "Returns the intent that launched the script.")
public Object getIntent() {
return mIntent;
}
@Rpc(description = "Launches an activity that sends an e-mail message to a given recipient.")
public void sendEmail(
@RpcParameter(name = "to", description = "A comma separated list of recipients.") final String to,
@RpcParameter(name = "subject") final String subject,
@RpcParameter(name = "body") final String body,
@RpcParameter(name = "attachmentUri") @RpcOptional final String attachmentUri) {
final Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.setType("plain/text");
intent.putExtra(android.content.Intent.EXTRA_EMAIL, to.split(","));
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
intent.putExtra(android.content.Intent.EXTRA_TEXT, body);
if (attachmentUri != null) {
intent.putExtra(android.content.Intent.EXTRA_STREAM, Uri.parse(attachmentUri));
}
startActivity(intent);
}
@Rpc(description = "Returns package version code.")
public int getPackageVersionCode(@RpcParameter(name = "packageName") final String packageName) {
int result = -1;
PackageInfo pInfo = null;
try {
pInfo =
mService.getPackageManager().getPackageInfo(packageName, PackageManager.GET_META_DATA);
} catch (NameNotFoundException e) {
pInfo = null;
}
if (pInfo != null) {
result = pInfo.versionCode;
}
return result;
}
@Rpc(description = "Returns package version name.")
public String getPackageVersion(@RpcParameter(name = "packageName") final String packageName) {
PackageInfo packageInfo = null;
try {
packageInfo =
mService.getPackageManager().getPackageInfo(packageName, PackageManager.GET_META_DATA);
} catch (NameNotFoundException e) {
return null;
}
if (packageInfo != null) {
return packageInfo.versionName;
}
return null;
}
@Rpc(description = "Checks if version of SL4A is greater than or equal to the specified version.")
public boolean requiredVersion(@RpcParameter(name = "requiredVersion") final Integer version) {
boolean result = false;
int packageVersion = getPackageVersionCode("com.googlecode.android_scripting");
if (version > -1) {
result = (packageVersion >= version);
}
return result;
}
@Rpc(description = "Writes message to logcat.")
public void log(@RpcParameter(name = "message") String message) {
android.util.Log.v("SCRIPT", message);
}
/**
*
* Map returned:
*
* <pre>
* TZ = Timezone
* id = Timezone ID
* display = Timezone display name
* offset = Offset from UTC (in ms)
* SDK = SDK Version
* download = default download path
* appcache = Location of application cache
* sdcard = Space on sdcard
* availblocks = Available blocks
* blockcount = Total Blocks
* blocksize = size of block.
* </pre>
*/
@Rpc(description = "A map of various useful environment details")
public Map<String, Object> environment() {
Map<String, Object> result = new HashMap<String, Object>();
Map<String, Object> zone = new HashMap<String, Object>();
Map<String, Object> space = new HashMap<String, Object>();
TimeZone tz = TimeZone.getDefault();
zone.put("id", tz.getID());
zone.put("display", tz.getDisplayName());
zone.put("offset", tz.getOffset((new Date()).getTime()));
result.put("TZ", zone);
result.put("SDK", android.os.Build.VERSION.SDK);
result.put("download", FileUtils.getExternalDownload().getAbsolutePath());
result.put("appcache", mService.getCacheDir().getAbsolutePath());
try {
StatFs fs = new StatFs("/sdcard");
space.put("availblocks", fs.getAvailableBlocks());
space.put("blocksize", fs.getBlockSize());
space.put("blockcount", fs.getBlockCount());
} catch (Exception e) {
space.put("exception", e.toString());
}
result.put("sdcard", space);
return result;
}
@Rpc(description = "Get list of constants (static final fields) for a class")
public Bundle getConstants(
@RpcParameter(name = "classname", description = "Class to get constants from") String classname)
throws Exception {
Bundle result = new Bundle();
int flags = Modifier.FINAL | Modifier.PUBLIC | Modifier.STATIC;
Class<?> clazz = Class.forName(classname);
for (Field field : clazz.getFields()) {
if ((field.getModifiers() & flags) == flags) {
Class<?> type = field.getType();
String name = field.getName();
if (type == int.class) {
result.putInt(name, field.getInt(null));
} else if (type == long.class) {
result.putLong(name, field.getLong(null));
} else if (type == double.class) {
result.putDouble(name, field.getDouble(null));
} else if (type == char.class) {
result.putChar(name, field.getChar(null));
} else if (type instanceof Object) {
result.putString(name, field.get(null).toString());
}
}
}
return result;
}
}