/*
* AndroWish.java --
*
* Main application class and some supplemental classes
* of the "AndroWish" Tcl/Tk runtime environment.
*
* Copyright (c) 2013-2024 Christian Werner <chw@ch-werner.de>
*
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
package tk.tcl.wish;
import org.libsdl.app.SDLActivity;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.Map.*;
import java.io.*;
import android.os.*;
import android.os.PowerManager.*;
import android.net.*;
import android.app.*;
import android.content.*;
import android.content.pm.*;
import android.content.res.*;
import android.database.*;
import android.media.*;
import android.graphics.*;
import android.view.*;
import android.location.*;
import android.speech.*;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.*;
import android.net.*;
import android.util.*;
import android.bluetooth.*;
import android.widget.*;
import androidx.core.app.NotificationCompat;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.hardware.*;
import android.hardware.usb.*;
import android.telephony.*;
import android.nfc.*;
import android.nfc.tech.*;
import android.view.inputmethod.InputMethodManager;
public class AndroWish extends SDLActivity
implements LocationListener,
ActivityCompat.OnRequestPermissionsResultCallback {
static final String TAG = "AndroWish";
protected static AndroWish mSingleton;
protected static PackageManager mPackageManager;
protected static Vibrator mVibrator;
protected static TextToSpeech mTTS;
protected static CountDownLatch mTTSLock;
protected static TTS_UPL mTTSUPL;
protected static boolean mTTSAvailable;
protected static int mTTSCount;
protected static Map<String, Location> mLocations;
protected static LocationManager mLocationMgr;
protected static ConnectivityManager mConnMgr;
protected static Object mNetLock;
protected static NetworkInfo mNetworkInfo;
protected static String mNTl[], mNTa[], mNTe[];
protected static BroadcastReceiver mNWRecvr;
protected static BroadcastReceiver mNTRecvr;
protected static NotificationManager mNotificationMgr;
protected static BluetoothAdapter mBluetoothAdap;
protected static BroadcastReceiver mBTRecvr, mBDRecvr;
protected static ProgressDialog mSpinner;
protected static ContentResolver mContentResolver;
protected static AlarmManager mAlarmManager;
protected static Ringtone mCurrentTone;
protected static UsbManager mUsbManager;
protected static Sensors mSensors;
protected static SpeechRec mSpeechRec;
protected static Object mConfigLock;
protected static Configuration mConfig;
protected static GpsInfo mGpsInfo;
protected static NmeaInfo mNmeaInfo;
protected static TelephonyManager mPhoneManager;
protected static PSListener mPSListener;
protected static Map<String, BCRecvr> mBCRecvrs;
protected static ImageCapture mCamera;
protected static BroadcastReceiver mUsbRecvr;
protected static Map<String, UsbDeviceConnection> mUsbHold;
protected static BCRecvr mNfcBCRecvr;
protected static NfcAdapter mNfcAdapter;
protected static Tag mNfcTag;
protected static CountDownLatch mPermLock;
private int mSavedOrient;
/*
* Callback on application startup
*/
@Override
public void onCreate(Bundle savedInstanceData) {
super.onCreate(savedInstanceData);
if (mSingleton != null) {
/* Should not happen, but ... */
finish();
return;
}
mSingleton = this;
int glesVer = getGLESVersion(this);
String lang = Locale.getDefault().toString();
Log.v(TAG, "onCreate GLES=" + glesVer + " LANG=" + lang);
nativeInit(glesVer, lang);
mPackageManager = getPackageManager();
mNotificationMgr = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
mContentResolver = getContentResolver();
if (hasPerm(android.Manifest.permission.VIBRATE)) {
mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
}
if (hasPerm(android.Manifest.permission.ACCESS_FINE_LOCATION) ||
hasPerm(android.Manifest.permission.ACCESS_COARSE_LOCATION)) {
mLocationMgr = (LocationManager)
getSystemService(Context.LOCATION_SERVICE);
if (mLocationMgr != null) {
mGpsInfo = new GpsInfo(this);
mLocationMgr.addGpsStatusListener(mGpsInfo);
mNmeaInfo = new NmeaInfo(this);
mLocationMgr.addNmeaListener(mNmeaInfo);
}
}
mLocations = new HashMap<String, Location>();
mNetLock = new Object();
mNTl = null;
mNTa = null;
mNTe = null;
if (hasPerm(android.Manifest.permission.ACCESS_NETWORK_STATE)) {
mConnMgr = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mNWRecvr = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mNetLock) {
mNetworkInfo = mConnMgr.getActiveNetworkInfo();
}
mSingleton.nativeTriggerNetworkInfo();
}
};
registerReceiver(mNWRecvr, filter);
filter = new IntentFilter();
filter.addAction("android.net.conn.TETHER_STATE_CHANGED");
mNTRecvr = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.net.conn.TETHER_STATE_CHANGED")) {
ArrayList<String> a =
intent.getStringArrayListExtra("activeArray");
ArrayList<String> l =
intent.getStringArrayListExtra("availableArray");
ArrayList<String> e =
intent.getStringArrayListExtra("erroredArray");
synchronized (mNetLock) {
if (a != null) {
mNTa = new String[a.size()];
a.toArray(mNTa);
} else {
mNTa = null;
}
if (l != null) {
mNTl = new String[l.size()];
l.toArray(mNTl);
} else {
mNTl = null;
}
if (e != null) {
mNTe = new String[e.size()];
e.toArray(mNTe);
} else {
mNTe = null;
}
}
mSingleton.nativeTriggerTetherInfo();
}
}
};
registerReceiver(mNTRecvr, filter);
}
if (hasPerm(android.Manifest.permission.BLUETOOTH) &&
hasPerm(android.Manifest.permission.BLUETOOTH_ADMIN)) {
mBluetoothAdap = BluetoothAdapter.getDefaultAdapter();
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
mBTRecvr = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mSingleton.nativeTriggerBluetooth();
}
};
registerReceiver(mBTRecvr, filter);
filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
mBDRecvr = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent i) {
String action = i.getAction();
String uristr = i.getDataString();
String type = i.getType();
String[] cats = null;
String args[] = null;
try {
BluetoothDevice dev =
i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if ((dev.getAddress() != null) &&
(dev.getName() != null)) {
args = new String[2];
args[0] = dev.getAddress();
args[1] = dev.getName();
}
} catch (Exception e) {
}
Log.v(TAG, "nativeTriggerIntent: " + action + "," +
uristr + "," + type + "," + cats + "," + args);
mSingleton.nativeTriggerIntent(action, uristr, type,
cats, args);
}
};
registerReceiver(mBDRecvr, filter);
}
mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
if (hasPerm(android.Manifest.permission.WAKE_LOCK) &&
getIntent().getBooleanExtra("tk.tcl.wish.wakeup", false)) {
PowerManager pm =
(PowerManager) getSystemService(Context.POWER_SERVICE);
WakeLock wl =
pm.newWakeLock(PowerManager.FULL_WAKE_LOCK |
PowerManager.ACQUIRE_CAUSES_WAKEUP,
TAG);
wl.acquire();
wl.release();
}
mCurrentTone = null;
if (android.os.Build.VERSION.SDK_INT >= 11) {
mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
}
mSensors = new Sensors(this);
if (hasPerm(android.Manifest.permission.RECORD_AUDIO)) {
mSpeechRec = new SpeechRec(this);
}
mConfigLock = new Object();
synchronized (mConfigLock) {
mConfig = new Configuration(getResources().getConfiguration());
}
mPSListener = null;
mPhoneManager =
(TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (mPhoneManager != null) {
if (hasPerm(android.Manifest.permission.READ_PHONE_STATE)) {
mPSListener = new PSListener(this);
mPhoneManager.listen(mPSListener,
PhoneStateListener.LISTEN_DATA_CONNECTION_STATE |
PhoneStateListener.LISTEN_SERVICE_STATE |
PhoneStateListener.LISTEN_CALL_STATE |
PhoneStateListener.LISTEN_DATA_ACTIVITY |
PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
}
}
mBCRecvrs = new HashMap<String, BCRecvr>();
if (hasPerm(android.Manifest.permission.CAMERA)) {
ImageCapture.init(getContext());
mCamera = ImageCapture.get();
}
if ((android.os.Build.VERSION.SDK_INT >= 11) && (mUsbManager != null)) {
mUsbHold = new HashMap<String, UsbDeviceConnection>();
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
mUsbRecvr = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String a = intent.getAction();
if (a.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
/*
* Seems to be reported on Android >= 4.4.
* Older version must poll for USB devices.
*/
mSingleton.nativeTriggerUsb(1);
} else if (a.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
mSingleton.nativeTriggerUsb(0);
UsbDevice d = (UsbDevice)
intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (d != null) {
String n = d.getDeviceName();
UsbDeviceConnection c = mUsbHold.get(n);
if (c != null) {
c.close();
mUsbHold.remove(n);
}
}
}
}
};
registerReceiver(mUsbRecvr, filter);
}
if (hasPerm(android.Manifest.permission.NFC)) {
NfcManager manager =
(NfcManager) getSystemService(Context.NFC_SERVICE);
mNfcAdapter = manager.getDefaultAdapter();
} else {
mNfcAdapter = null;
}
mNfcBCRecvr = null;
mNfcTag = null;
mSavedOrient = getRequestedOrientation();
}
/*
* Tear down everything
*/
@Override
protected void onDestroy() {
Log.v(TAG, "onDestroy()");
super.onDestroy();
if (mPSListener != null) {
mPhoneManager.listen(mPSListener, PhoneStateListener.LISTEN_NONE);
mPSListener = null;
}
if (mSpeechRec != null) {
mSpeechRec.destroy();
mSpeechRec = null;
}
if (mNWRecvr != null) {
unregisterReceiver(mNWRecvr);
mNWRecvr = null;
}
if (mNTRecvr != null) {
unregisterReceiver(mNTRecvr);
mNTRecvr = null;
}
if (mBTRecvr != null) {
unregisterReceiver(mBTRecvr);
mBTRecvr = null;
}
if (mBDRecvr != null) {
unregisterReceiver(mBDRecvr);
mBDRecvr = null;
}
if (mGpsInfo != null) {
mLocationMgr.removeGpsStatusListener(mGpsInfo);
mGpsInfo = null;
}
if (mNmeaInfo != null) {
mLocationMgr.removeNmeaListener(mNmeaInfo);
mNmeaInfo = null;
}
if (mLocationMgr != null) {
mLocationMgr.removeUpdates(this);
mLocationMgr = null;
}
nfcEnable(false);
synchronized (mBCRecvrs) {
for (Entry<String, BCRecvr> entry : mBCRecvrs.entrySet()) {
BCRecvr bcr = entry.getValue();
unregisterReceiver(bcr);
}
mBCRecvrs.clear();
}
mNfcBCRecvr = null;
if (mCamera != null) {
mCamera.onDestroy();
mCamera = null;
}
if (mUsbRecvr != null) {
unregisterReceiver(mUsbRecvr);
mUsbRecvr = null;
}
if (mUsbHold != null) {
for (final UsbDeviceConnection c : mUsbHold.values()) {
c.close();
}
mUsbHold.clear();
mUsbHold = null;
}
}
/*
* Find out if OpenGL ES >= 2.x is available.
*/
private static int getGLESVersion(Context context) {
final ActivityManager am = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
final ConfigurationInfo ci = am.getDeviceConfigurationInfo();
return (ci.reqGlEsVersion & 0xFFFF0000) >> 16;
}
/*
* Check for a permission
*/
static boolean hasPerm(String which) {
int result =
mSingleton.getContext().checkCallingOrSelfPermission(which);
return result == PackageManager.PERMISSION_GRANTED;
}
/*
* Pause/resume handling
*/
@Override
public void onPause() {
super.onPause();
mSensors.pause();
if (mPSListener != null) {
mPhoneManager.listen(mPSListener, PhoneStateListener.LISTEN_NONE);
}
if (mCamera != null) {
mCamera.onPause();
}
nfcEnable(false);
mSavedOrient = getRequestedOrientation();
int orient;
if (mConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
orient = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
} else {
orient = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
}
if (android.os.Build.VERSION.SDK_INT >= 27) {
setRequestedOrientation(orient);
}
}
@Override
public void onResume() {
super.onResume();
mSensors.resume();
if (mPSListener != null) {
mPhoneManager.listen(mPSListener,
PhoneStateListener.LISTEN_DATA_CONNECTION_STATE |
PhoneStateListener.LISTEN_SERVICE_STATE |
PhoneStateListener.LISTEN_CALL_STATE |
PhoneStateListener.LISTEN_DATA_ACTIVITY |
PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
}
Configuration config = getResources().getConfiguration();
boolean trigger = false;
synchronized (mConfigLock) {
if ((config.keyboard != mConfig.keyboard) ||
(config.keyboardHidden != mConfig.keyboardHidden) ||
(config.hardKeyboardHidden != mConfig.hardKeyboardHidden)) {
trigger = true;
}
mConfig = new Configuration(config);
}
if (trigger) {
mSingleton.nativeTriggerKeyboard();
}
if (mCamera != null) {
mCamera.onResume();
}
nfcEnable(true);
if (android.os.Build.VERSION.SDK_INT >= 27) {
setRequestedOrientation(mSavedOrient);
}
}
/*
* Configuration (e.g. keyboard) has changed
*/
@Override
public void onConfigurationChanged(Configuration config) {
Log.v(TAG, "onConfigurationChanged: " + config);
super.onConfigurationChanged(config);
boolean trigger = false;
boolean hideIM = false;
synchronized (mConfigLock) {
if ((config.keyboard != mConfig.keyboard) ||
(config.keyboardHidden != mConfig.keyboardHidden) ||
(config.hardKeyboardHidden != mConfig.hardKeyboardHidden)) {
hideIM = (mConfig.hardKeyboardHidden ==
Configuration.HARDKEYBOARDHIDDEN_NO) &&
(config.hardKeyboardHidden ==
Configuration.HARDKEYBOARDHIDDEN_YES);
trigger = true;
}
mConfig = new Configuration(config);
}
if (hideIM) {
super.hideTextInput();
}
if (trigger) {
mSingleton.nativeTriggerKeyboard();
}
}
/*
* Turn NFC foreground dispatch on or off.
*/
int nfcEnable(boolean on) {
if (mNfcAdapter == null) {
return -1;
}
if ((mNfcBCRecvr != null) && on) {
PendingIntent pi =
PendingIntent.getBroadcast(getContext(), 0,
new Intent("tk.tcl.wish.nfc"), 0);
/*
* This should be sufficient but doesn't work, unfortunately:
*
* mNfcAdapter.enableForegroundDispatch(this, pi, null, null);
*
* thus use filters for both intent and tech.
*/
IntentFilter ndef =
new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
IntentFilter[] filt = new IntentFilter[] { ndef };
String[][] techs = new String[][] {
new String[] { Ndef.class.getName() },
new String[] { NdefFormatable.class.getName() }
};
try {
mNfcAdapter.enableForegroundDispatch(this, pi, filt, techs);
} catch (Exception e) {
return 0;
}
mNfcTag = null;
} else {
try {
mNfcAdapter.disableForegroundDispatch(this);
} catch (Exception e) {
return 0;
}
mNfcTag = null;
}
return 1;
}
/*
* Encode Bundle to String[] for "array set"
*/
String[] formatBundle(Bundle b, String[] add) {
Set<String> keys = b.keySet();
String[] ret = new String[keys.size() * 2 +
((add != null) ? add.length : 0)];
int k = 0;
Iterator<String> it = keys.iterator();
while (it.hasNext()) {
String key = it.next();
Object obj = b.get(key);
ret[k++] = key;
if (obj == null) {
ret[k] = new String("");
} else if (obj instanceof String) {
ret[k] = (String) obj;
} else if (obj instanceof String[]) {
ret[k] = encodeStringList((String[]) obj);
} else if (obj instanceof byte[]) {
ret[k] =
android.util.Base64.encodeToString((byte[]) obj,
android.util.Base64.DEFAULT);
} else if (obj instanceof Bitmap) {
ret[k] = encodeBitmap((Bitmap) obj);
} else if (obj instanceof ArrayList) {
ret[k] = encodeToList((ArrayList) obj);
} else if (obj instanceof double[]) {
ret[k] = encodeDoubleList((double[]) obj);
} else if (obj instanceof float[]) {
ret[k] = encodeFloatList((float[]) obj);
} else if (obj instanceof short[]) {
ret[k] = encodeShortList((short[]) obj);
} else if (obj instanceof int[]) {
ret[k] = encodeIntList((int[]) obj);
} else if (obj instanceof long[]) {
ret[k] = encodeLongList((long []) obj);
} else if (obj instanceof boolean[]) {
ret[k] = encodeBoolList((boolean []) obj);
} else if (obj instanceof char[]) {
ret[k] = encodeCharList((char []) obj);
} else if (obj instanceof Boolean) {
ret[k] = ((Boolean) obj).booleanValue() ? "1" : "0";
} else if ((obj instanceof Parcelable[]) &&
key.equals(NfcAdapter.EXTRA_NDEF_MESSAGES)) {
ArrayList<byte[]> al = new ArrayList<byte[]>();
Parcelable[] pa = (Parcelable[]) obj;
for (int n = 0; n < pa.length; n++) {
NdefMessage msg = (NdefMessage) pa[n];
byte[] ba;
if (msg != null) {
ba = msg.toByteArray();
} else {
ba = new byte[0];
}
al.add(ba);
}
ret[k] = encodeToList(al);
} else {
ret[k] = obj.toString();
}
k++;
}
if (add != null) {
for (int i = 0; i < add.length; i++) {
ret[k++] = add[i];
}
}
return ret;
}
/*
* Encode Set<String> to String[] for "array set"
*/
String[] formatCategories(Set<String> cset) {
String ret[] = new String[cset.size()];
cset.toArray(ret);
return ret;
}
/*
* Callback on re-activation
*/
@Override
public void onNewIntent(Intent i) {
setIntent(i);
if (hasPerm(android.Manifest.permission.WAKE_LOCK) &&
i.getBooleanExtra("tk.tcl.wish.wakeup", false)) {
PowerManager pm =
(PowerManager) getSystemService(Context.POWER_SERVICE);
WakeLock wl =
pm.newWakeLock(PowerManager.FULL_WAKE_LOCK |
PowerManager.ACQUIRE_CAUSES_WAKEUP,
TAG);
wl.acquire();
wl.release();
}
String action = i.getAction();
String uristr = i.getDataString();
String type = i.getType();
String[] cats = null;
String[] args = null;
if (i.getCategories() != null) {
cats = formatCategories(i.getCategories());
}
if (i.getExtras() != null) {
args = formatBundle(i.getExtras(), null);
}
Log.v(TAG, "nativeTriggerIntent: " +
action + "," + uristr + "," + type + "," + cats + "," + args);
mSingleton.nativeTriggerIntent(action, uristr, type, cats, args);
}
public static void syncIntent() {
Runnable synci = new Runnable() {
public void run() {
mSingleton.onNewIntent(mSingleton.getIntent());
}
};
mSingleton.runOnUiThread(synci);
}
public static Context getContext() {
return mSingleton.getApplicationContext();
}
public static UsbManager getUsbManager() {
return mUsbManager;
}
public static void setOrientation(final int orient) {
Runnable do_orient = new Runnable() {
public void run() {
mSingleton.setRequestedOrientation(orient);
}
};
mSingleton.runOnUiThread(do_orient);
}
public static int getOrientation() {
return mSingleton.getRequestedOrientation();
}
/*
* Find resource identifier by name
*/
public static int getResourceId(String name, String res) {
try {
return mSingleton.getResources().getIdentifier(name, res, mSingleton.getPackageName());
} catch (Exception e) {
return -1;
}
}
/*
* Callback to deal with activity results
*/
@Override
public void onActivityResult(int id, int ret, Intent i) {
super.onActivityResult(id, ret, i);
String action = null;
String uristr = null;
String type = null;
String[] cats = null;
String[] args = null;
if (i != null) {
action = i.getAction();
uristr = i.getDataString();
type = i.getType();
if (i.getCategories() != null) {
cats = formatCategories(i.getCategories());
}
if (i.getExtras() != null) {
args = formatBundle(i.getExtras(), null);
}
}
Log.v(TAG, "nativeIntentCallback: " + id + "," + ret + "," +
action + "," + uristr + "," + type + "," + cats + "," + args);
nativeIntentCallback(id, ret, action, uristr, type, cats, args);
}
String encodeBitmap(Bitmap bm) {
ByteArrayOutputStream bs = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 80, bs);
byte[] bytes = bs.toByteArray();
bs = null;
return android.util.Base64.encodeToString(bytes,
android.util.Base64.DEFAULT);
}
String toElement(String in) {
int level = 0;
int flags = 0;
int i, len;
if (in == null) {
return "{}";
}
len = in.length();
if (len == 0) {
return "{}";
}
for (i = 0; i < len; i++) {
if ((in.charAt(i) == '{') || (in.charAt(i) == '}')) {
flags |= 1;
}
}
for (i = 0; i < len; i++) {
switch (in.charAt(i)) {
case '{':
level++;
break;
case '}':
level--;
if (level < 0) {
flags |= 2;
}
break;
case '[':
case '$':
case ';':
case ' ':
case '\f':
case '\n':
case '\r':
case '\t':
case 0x0B:
flags |= 1;
break;
case '\\':
if ((i + 1 == len) || (in.charAt(i + 1) == '\n')) {
flags = 2;
} else {
flags |= 1;
}
break;
}
}
if (level != 0) {
flags = 2;
}
StringBuilder r = new StringBuilder(in.length() + 64);
if ((flags & 1) == 1) {
r.append("{");
r.append(in);
r.append("}");
} else {
i = 0;
if (in.charAt(i) == '{') {
flags |= 2;
r.append("\\");
r.append("{");
i++;
}
for (; i < len; i++) {
switch (in.charAt(i)) {
case ']':
case '[':
case '$':
case ';':
case ' ':
case '\\':
case '"':
r.append("\\");
r.append(in.charAt(i));
break;
case '{':
case '}':
if ((flags & 2) != 0) {
r.append("\\");
}
r.append(in.charAt(i));
break;
case '\f':
r.append("\\f");
break;
case '\n':
r.append("\\n");
break;
case '\r':
r.append("\\r");
break;
case '\t':
r.append("\\t");
break;
case 0x0B:
r.append("\\v");
break;
default:
r.append(in.charAt(i));
break;
}
}
}
return r.toString();
}
String encodeToList(ArrayList list) {
StringBuilder r = new StringBuilder();
int i;
for (i = 0; i < list.size(); i++) {
Object obj = list.get(i);
if (i != 0) {
r.append(" ");
}
if (obj == null) {
r.append("{}");
} else if (obj instanceof String) {
r.append(toElement((String) obj));
} else if (obj instanceof byte[]) {
r.append(android.util.Base64.encodeToString((byte[]) obj,
android.util.Base64.DEFAULT));
} else {
r.append(toElement(obj.toString()));
}
}
return r.toString();
}
String encodeDoubleList(double[] list) {
StringBuilder r = new StringBuilder(list.length * 16);
int i;
for (i = 0; i < list.length; i++) {
if (i != 0) {
r.append(" ");
}
r.append("" + list[i]);
}
return r.toString();
}
String encodeFloatList(float[] list) {
StringBuilder r = new StringBuilder(list.length * 16);
int i;
for (i = 0; i < list.length; i++) {
if (i != 0) {
r.append(" ");
}
r.append("" + list[i]);
}
return r.toString();
}
String encodeShortList(short[] list) {
StringBuilder r = new StringBuilder(list.length * 8);
int i;
for (i = 0; i < list.length; i++) {
if (i != 0) {
r.append(" ");
}
r.append("" + list[i]);
}
return r.toString();
}
String encodeIntList(int[] list) {
StringBuilder r = new StringBuilder(list.length * 8);
int i;
for (i = 0; i < list.length; i++) {
if (i != 0) {
r.append(" ");
}
r.append("" + list[i]);
}
return r.toString();
}
String encodeLongList(long[] list) {
StringBuilder r = new StringBuilder(list.length * 8);
int i;
for (i = 0; i < list.length; i++) {
if (i != 0) {
r.append(" ");
}
r.append("" + list[i]);
}
return r.toString();
}
String encodeBoolList(boolean[] list) {
StringBuilder r = new StringBuilder(list.length * 3);
int i;
for (i = 0; i < list.length; i++) {
if (i != 0) {
r.append(" ");
}
r.append("" + (list[i] ? "1" : "0"));
}
return r.toString();
}
String encodeCharList(char[] list) {
StringBuilder r = new StringBuilder(list.length * 2);
int i;
for (i = 0; i < list.length; i++) {
r.append(list[i]);
}
return r.toString();
}
String encodeStringList(String[] list) {
StringBuilder r = new StringBuilder();
int i;
for (i = 0; i < list.length; i++) {
String s = list[i];
if (i != 0) {
r.append(" ");
}
if ((s == null) || s.length() == 0) {
r.append("{}");
} else {
r.append(toElement(s));
}
}
return r.toString();
}
static Intent buildIntent(Intent i, String uristr, String type,
String[] cats, String[] comp) {
Uri uri = null;
if ((uristr != null) && (uristr.length() > 0)) {
uri = Uri.parse(uristr);
}
if ((type != null) && (type.length() == 0)) {
type = null;
}
if ((uri != null) && (type != null)) {
i.setDataAndType(uri, type);
} else if (uri != null) {
i.setData(uri);
} else if (type != null) {
i.setType(type);
}
if (cats != null) {
int k;
for (k = 0; k < cats.length; k++) {
i.addCategory(cats[k]);
}
}
if (comp != null && comp.length > 1) {
i.setComponent(new ComponentName(comp[0], comp[1]));
}
return i;
}
static void addArgsToIntent(Intent i, String types[], String[] argv) {
if (argv != null) {
int k;
for (k = 0; k < argv.length; k += 2) {
if ((types != null) && (types[k] != null)) {
if (types[k].equals("int")) {
i.putExtra(argv[k], Integer.parseInt(argv[k + 1]));
} else if (types[k].equals("long")) {
i.putExtra(argv[k], Long.parseLong(argv[k + 1]));
} else if (types[k].equals("short")) {
i.putExtra(argv[k], Short.parseShort(argv[k + 1]));
} else if (types[k].equals("char")) {
i.putExtra(argv[k], argv[k + 1].charAt(0));
} else if (types[k].equals("byte")) {
i.putExtra(argv[k], Byte.parseByte(argv[k + 1]));
} else if (types[k].equals("boolean")) {
i.putExtra(argv[k], Boolean.parseBoolean(argv[k + 1]));
} else if (types[k].equals("float")) {
i.putExtra(argv[k], Float.parseFloat(argv[k + 1]));
} else if (types[k].equals("double")) {
i.putExtra(argv[k], Double.parseDouble(argv[k + 1]));
} else if (types[k].equals("Uri")) {
i.putExtra(argv[k], Uri.parse(argv[k + 1]));
} else if (types[k].equals("String")) {
i.putExtra(argv[k], argv[k + 1]);
} else {
i.putExtra(argv[k], argv[k + 1]);
}
} else {
i.putExtra(argv[k], argv[k + 1]);
}
}
}
}
/*
* Run activity
*/
public static int runActivity(String action, String uristr, String type,
String[] cats, String types[], String[] argv,
int id) {
int err;
err = (id < 0) ? (id - 1) : -100;
try {
Intent i = new Intent(action);
i = buildIntent(i, uristr, type, cats, null);
addArgsToIntent(i, types, argv);
Runner r = new Runner(mSingleton, i, id);
mSingleton.runOnUiThread(r);
} catch (Exception e) {
Log.e(TAG, "runActivity failed: " + e.toString());
return err;
}
return id;
}
public static int runActivityEx(String action, String uristr,
String type, String cats[],
String[] comp, String types[],
String[] argv, int id) {
int err;
err = (id < 0) ? (id - 1) : -100;
try {
Intent i = new Intent(action);
i = buildIntent(i, uristr, type, cats, comp);
addArgsToIntent(i, types, argv);
Runner r = new Runner(mSingleton, i, id);
mSingleton.runOnUiThread(r);
} catch (Exception e) {
Log.e(TAG, "runActivity failed: " + e.toString());
return err;
}
return id;
}
/*
* Callback to deal with broadcast intent
*/
public void broadcastReceived(Intent i, int retcode) {
String action = i.getAction();
String uristr = i.getDataString();
String type = i.getType();
String[] cats = null;
String[] args = null;
if (i.getCategories() != null) {
cats = formatCategories(i.getCategories());
}
Bundle extras = i.getExtras();
if (extras != null) {
String addargs[] = null;
if (action.equals("android.provider.Telephony.SMS_RECEIVED")) {
Object[] pdus = (Object[]) extras.get("pdus");
if (pdus.length > 0) {
SmsMessage[] msgs = new SmsMessage[pdus.length];
StringBuilder sb = new StringBuilder();
for (int k = 0; k < pdus.length; k++) {
msgs[k] =
SmsMessage.createFromPdu((byte[]) pdus[k]);
sb.append(msgs[k].getMessageBody());
}
addargs = new String[4];
addargs[0] = "phone_number";
addargs[1] = msgs[0].getOriginatingAddress();
addargs[2] = "sms_text";
addargs[3] = sb.toString();
}
} else if (action.equals("tk.tcl.wish.nfc")) {
if (extras.get(NfcAdapter.EXTRA_TAG) != null) {
mNfcTag = (Tag) extras.get(NfcAdapter.EXTRA_TAG);
}
}
args = formatBundle(extras, addargs);
}
Log.v(TAG, "nativeBroadcastCallback: " + retcode + "," + action +
"," + uristr + "," + type + "," + cats + "," + args);
nativeBroadcastCallback(retcode, action, uristr, type, cats, args);
}
/*
* Deal with (un)registration of broadcast listeners from Tcl
*/
public static int handleBroadcastListener(int op, String action) {
int ret = -1;
if (action == null) {
return ret;
}
final boolean isNfc = action.equals("tk.tcl.wish.nfc");
synchronized (mBCRecvrs) {
BCRecvr bcr = mBCRecvrs.get(action);
if (op == 0) {
/* unregister */
ret = 0;
if (bcr != null) {
mBCRecvrs.remove(action);
final BCRecvr unreg_bcr = bcr;
if (isNfc) {
mNfcBCRecvr = null;
}
Runnable unreg = new Runnable() {
public void run() {
mSingleton.unregisterReceiver(unreg_bcr);
if (isNfc) {
mSingleton.nfcEnable(false);
}
}
};
mSingleton.runOnUiThread(unreg);
ret = 1;
}
} else if (op == 1) {
/* register */
ret = 0;
if (bcr == null) {
final BCRecvr reg_bcr = new BCRecvr(mSingleton);
final IntentFilter filter = new IntentFilter();
filter.addAction(action);
mBCRecvrs.put(action, reg_bcr);
if (isNfc) {
mNfcBCRecvr = reg_bcr;
}
Runnable reg = new Runnable() {
public void run() {
mSingleton.registerReceiver(reg_bcr, filter);
if (isNfc) {
mSingleton.nfcEnable(true);
}
}
};
mSingleton.runOnUiThread(reg);
ret = 1;
}
}
}
return ret;
}
/*
* Send SMS, text only for now
*/
public static int sendSMS(String phone, String msg, String actionSent,
String actionDelivered, String smsc) {
PendingIntent piSent = null, piDelivered = null;
if ((phone == null) || (msg == null)) {
return 0;
}
if (!hasPerm(android.Manifest.permission.SEND_SMS)) {
Log.e(TAG, "sendSMS failed: no permission");
return 0;
}
SmsManager sms = SmsManager.getDefault();
if (sms == null) {
Log.e(TAG, "sendSMS failed: no SmsManager");
return 0;
}
if (actionSent != null) {
piSent = PendingIntent.getBroadcast(mSingleton, 0,
new Intent(actionSent), 0);
}
if (actionDelivered != null) {
piDelivered =
PendingIntent.getBroadcast(mSingleton, 0,
new Intent(actionDelivered), 0);
}
sms.sendTextMessage(phone, smsc, msg, piSent, piDelivered);
return 1;
}
/*
* Speech recognition
*/
public static int speechRecognition(String types[], String[] argv, int id) {
if (mSpeechRec == null) {
return -1;
}
if (id < 0) {
Intent i = null;
if (id == -3) {
try {
i = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
i.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,
mSingleton.getClass().getPackage().getName());
addArgsToIntent(i, types, argv);
} catch (Exception e) {
Log.e(TAG, "speechRecognition failed: " +
e.toString());
}
}
SpRunner sr = new SpRunner(mSpeechRec, i, id);
mSingleton.runOnUiThread(sr);
return 0;
}
try {
Intent i = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
i.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,
mSingleton.getClass().getPackage().getName());
addArgsToIntent(i, types, argv);
Runner r = new Runner(mSingleton, i, id);
mSingleton.runOnUiThread(r);
} catch (Exception e) {
Log.v(TAG, "speechRecognition failed: " + e.toString());
return -1;
}
return id;
}
/*
* Query intents/activities
*/
public static String[] queryIntents(int which, String action,
String uristr, String type,
String cats[], String[] comp) {
List<String> apps = new ArrayList<String>();
Intent i;
List<ResolveInfo> acts;
Uri uri = null;
if ((uristr != null) && (uristr.length() > 0)) {
uri = Uri.parse(uristr);
}
if ((type != null) && (type.length() == 0)) {
type = null;
}
i = new Intent(action);
if ((uri != null) && (type != null)) {
i.setDataAndType(uri, type);
} else if (uri != null) {
i.setData(uri);
} else if (type != null) {
i.setType(type);
}
if (cats != null) {
int k;
for (k = 0; k < cats.length; k++) {
i.addCategory(cats[k]);
}
}
try {
if (comp != null && comp.length > 1) {
i.setComponent(new ComponentName(comp[0], comp[1]));
}
} catch (Exception e) {
return null;
}
switch (which) {
default:
acts = mPackageManager.queryIntentActivities(i, 0);
break;
case 1:
acts = mPackageManager.queryIntentServices(i, 0);
break;
case 2:
acts = mPackageManager.queryBroadcastReceivers(i, 0);
break;
}
int k = 0;
for (ResolveInfo res : acts) {
ActivityInfo actinfo = res.activityInfo;
if (actinfo != null) {
apps.add(actinfo.name);
k++;
}
}
String[] ret = new String[k];
apps.toArray(ret);
return ret;
}
/*
* Query features
*/
public static String[] queryFeatures() {
FeatureInfo fi[] = mPackageManager.getSystemAvailableFeatures();
if ((fi == null) || (fi.length == 0)) {
return null;
}
String ret[] = new String[fi.length];
int i;
for (i = 0; i < fi.length; i++) {
ret[i] = fi[i].name;
}
return ret;
}
/*
* Package info
*/
public static String[] packageInfo(String name) {
if (name == null) {
List<PackageInfo> pl = mPackageManager.getInstalledPackages(0);
List<String> ps = new ArrayList<String>();
int k = 0;
for (PackageInfo pi : pl) {
ps.add(pi.packageName);
k++;
}
String[] ret = new String[k];
ps.toArray(ret);
return ret;
}
try {
PackageInfo pi =
mPackageManager.getPackageInfo(name, PackageManager.GET_ACTIVITIES);
String [] ret = new String[10];
ret[0] = "classname";
ret[1] = pi.applicationInfo.className;
ret[2] = "datadir";
ret[3] = pi.applicationInfo.dataDir;
ret[4] = "sourcedir";
ret[5] = pi.applicationInfo.sourceDir;
ret[6] = "sharedlibrarydir";
ret[7] = pi.applicationInfo.nativeLibraryDir;
ret[8] = "publicsourcedir";
ret[9] = pi.applicationInfo.publicSourceDir;
return ret;
} catch (PackageManager.NameNotFoundException ne) {
return null;
}
}
/*
* Provider info
*/
public static String[] providerInfo() {
List<PackageInfo> pl =
mPackageManager.getInstalledPackages(PackageManager.GET_PROVIDERS);
List<String> ps = new ArrayList<String>();
int k = 0;
for (PackageInfo pi : pl) {
if (pi.providers != null) {
for (int i = 0; i < pi.providers.length; i++) {
String auth[] = pi.providers[i].authority.split(";");
for (int j = 0; j < auth.length; j++) {
ps.add(auth[j]);
k++;
}
}
}
}
String[] ret = new String[k];
ps.toArray(ret);
return ret;
}
/*
* Content query and manipulation
*/
public static WrappedCursor contentQuery(String uristr, String[] columns,
String where,
String[] whereArgs,
String orderby) {
WrappedCursor wc = null;
Uri uri = Uri.parse(uristr);
try {
Cursor c = mContentResolver.query(uri, columns, where,
whereArgs, orderby);
if (c != null) {
wc = new WrappedCursor(c);
}
} catch (Exception e) {
Log.v(TAG, "contentQuery:" + e.toString());
}
return wc;
}
public static String contentInsert(String uristr, String[] args) {
Uri uri = Uri.parse(uristr);
ContentValues vals = new ContentValues();
int i;
for (i = 0; i < args.length / 2; i++) {
vals.put(args[i * 2], args[i * 2 + 1]);
}
try {
uri = mContentResolver.insert(uri, vals);
if (uri != null) {
return uri.toString();
}
} catch (Exception e) {
Log.v(TAG, "contentInsert:" + e.toString());
}
return null;
}
public static int contentUpdate(String uristr, String[] uvals,
String sel, String[] args) {
Uri uri = Uri.parse(uristr);
ContentValues vals = new ContentValues();
int i;
for (i = 0; i < uvals.length / 2; i++) {
vals.put(uvals[i * 2], uvals[i * 2 + 1]);
}
i = 0;
try {
i = mContentResolver.update(uri, vals, sel, args);
} catch (Exception e) {
Log.v(TAG, "contentUpdate:" + e.toString());
}
return i;
}
public static int contentDelete(String uristr, String sel,
String[] args) {
Uri uri = Uri.parse(uristr);
int i;
i = 0;
try {
i = mContentResolver.delete(uri, sel, args);
} catch (Exception e) {
Log.v(TAG, "contentDelete:" + e.toString());
}
return i;
}
/*
* Notification by vibration
*/
public static void vibrate(int duration) {
if (duration <= 0) {
duration = 500;
}
if (mVibrator != null) {
mVibrator.vibrate(duration);
}
}
/*
* Notification by ringtone
*/
public static void beep() {
Runnable beeper = new Runnable() {
public void run() {
Uri ringuri =
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Ringtone tone =
RingtoneManager.getRingtone(mSingleton.getBaseContext(),
ringuri);
if ((tone != null) && (!tone.isPlaying())) {
tone.play();
}
}
};
mSingleton.runOnUiThread(beeper);
}
public static void beepEx(final String uristr) {
Runnable beeper = new Runnable() {
public void run() {
if (mCurrentTone != null) {
mCurrentTone.stop();
mCurrentTone = null;
}
Uri ringuri = null;
if (uristr != null) {
if (uristr.length() == 0) {
return;
}
ringuri = Uri.parse(uristr);
}
if (ringuri == null) {
ringuri =
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
}
Ringtone tone =
RingtoneManager.getRingtone(mSingleton.getBaseContext(),
ringuri);
if ((tone != null) && (!tone.isPlaying())) {
tone.play();
if (uristr != null) {
mCurrentTone = tone;
}
}
}
};
mSingleton.runOnUiThread(beeper);
}
/*
* Notification by toast
*/
public static void toast(final String text, final int duration) {
Runnable toaster = new Runnable() {
public void run() {
Toast toast =
Toast.makeText(mSingleton.getBaseContext(),
text, (duration > 0) ?
Toast.LENGTH_LONG : Toast.LENGTH_SHORT);
if (toast != null) {
toast.show();
}
}
};
mSingleton.runOnUiThread(toaster);
}
public static void toastHtml(final String text, final int duration) {
Runnable toaster = new Runnable() {
public void run() {
Toast toast =
Toast.makeText(mSingleton.getBaseContext(),
TextFromHtml.text(text),
(duration > 0) ?
Toast.LENGTH_LONG : Toast.LENGTH_SHORT);
if (toast != null) {
toast.show();
}
}
};
mSingleton.runOnUiThread(toaster);
}
/*
* Text-to-speech
*/
public static int speak(final int op, final String text, String lang,
float pitch, float rate) {
int ret = 0;
if (op < 0) {
/* shutdown */
if (mTTS != null) {
mTTS.stop();
mTTS.shutdown();
if (android.os.Build.VERSION.SDK_INT >
android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mTTSUPL.onShutdown();
mTTSUPL = null;
} else {
mTTSLock = null;
}
mTTS = null;
}
return ret;
}
if (mTTS == null) {
if (android.os.Build.VERSION.SDK_INT >
android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mTTSUPL = new TTS_UPL(mSingleton);
} else {
mTTSLock = new CountDownLatch(1);
}
mTTSCount = 999;
Runnable speaker = new Runnable() {
public void run() {
if (android.os.Build.VERSION.SDK_INT >
android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mTTS = new TextToSpeech(mSingleton.getContext(), mTTSUPL);
} else {
mTTS = new TextToSpeech(mSingleton.getContext(), new OnInitListener() {
@Override
public void onInit(int status) {
mTTSLock.countDown();
}
});
}
}
};
mSingleton.runOnUiThread(speaker);
}
try {
if (android.os.Build.VERSION.SDK_INT >
android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
if (!mTTSAvailable) {
return -1;
}
} else {
mTTSLock.await();
}
switch (op) {
case 0:
mTTS.stop();
break;
case 1:
if (pitch >= 0) {
ret = mTTS.setPitch(pitch);
}
if ((ret == 0) && (rate > 0)) {
ret = mTTS.setSpeechRate(rate);
}
if ((ret == 0) && (lang != null)) {
Locale loc = new Locale(lang);
ret = mTTS.isLanguageAvailable(loc);
if ((ret >= 0) && (text != null) && (text.length() > 0)) {
ret = mTTS.setLanguage(loc);
}
}
if ((ret >= 0) && (text != null) && (text.length() > 0)) {
HashMap<String, String> uplid = null;
int id = 0;
if (android.os.Build.VERSION.SDK_INT >
android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
id = ++mTTSCount;
if (id > 1999) {
mTTSCount = 999;
id = ++mTTSCount;
}
uplid = new HashMap<String, String>();
uplid.put("utteranceId", "" + id);
}
ret = mTTS.speak(text, TextToSpeech.QUEUE_ADD, uplid);
if (ret == 0) {
ret = id;
}
}
break;
case 2:
if (mTTS.isSpeaking()) {
ret = 1;
}
break;
}
} catch (InterruptedException e) {
/* shutdown */
if (mTTS != null) {
mTTS.shutdown();
if (android.os.Build.VERSION.SDK_INT >
android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mTTSUPL.onShutdown();
mTTSUPL = null;
} else {
mTTSLock = null;
}
mTTS = null;
}
}
return ret;
}
/*
* Some reflection stuff
*/
public static String[] queryFields(String clsname, int reqFlags) {
List<String> list = new ArrayList<String>();
int atLeastFlags = Modifier.PUBLIC | Modifier.PROTECTED;
int k = 0;
try {
Class<?> cls = Class.forName(clsname);
for (Field field : cls.getFields()) {
if (((field.getModifiers() & reqFlags) == reqFlags) &&
((field.getModifiers() & atLeastFlags) != 0)) {
Class<?> type = field.getType();
list.add(field.getName());
Object obj = field.get(null);
if (obj == null) {
list.add("");
} else if (obj instanceof String) {
list.add((String) obj);
} else if (obj instanceof String[]) {
list.add(mSingleton.encodeStringList((String[]) obj));
} else if (obj instanceof double[]) {
list.add(mSingleton.encodeDoubleList((double[]) obj));
} else if (obj instanceof float[]) {
list.add(mSingleton.encodeFloatList((float[]) obj));
} else if (obj instanceof short[]) {
list.add(mSingleton.encodeShortList((short[]) obj));
} else if (obj instanceof int[]) {
list.add(mSingleton.encodeIntList((int[]) obj));
} else if (obj instanceof long[]) {
list.add(mSingleton.encodeLongList((long[]) obj));
} else {
list.add(obj.toString());
}
k += 2;
}
}
} catch (Exception e) {
Log.e(TAG, "queryConsts failed: " + e);
}
String[] ret = new String[k];
list.toArray(ret);
return ret;
}
public static String[] queryConsts(String clsname) {
return queryFields(clsname, Modifier.FINAL | Modifier.STATIC);
}
public static String[] queryConsts(String clsname, int fields) {
int reqFlags = Modifier.STATIC;
if (fields == 0) {
reqFlags |= Modifier.FINAL;
}
return queryFields(clsname, reqFlags);
}
/*
* Display metrics
*/
public static String getDisplayMetrics() {
DisplayMetrics metrics = new DisplayMetrics();
mSingleton.getWindowManager().getDefaultDisplay().getMetrics(metrics);
int rot =
mSingleton.getWindowManager().getDefaultDisplay().getRotation();
StringBuilder sb = new StringBuilder(128);
sb.append("density ").append(metrics.density);
sb.append(" densitydpi ").append(metrics.densityDpi);
sb.append(" width ").append(metrics.widthPixels);
sb.append(" height ").append(metrics.heightPixels);
sb.append(" xdpi ").append(metrics.xdpi);
sb.append(" ydpi ").append(metrics.ydpi);
sb.append(" scaleddensity ").append(metrics.scaledDensity);
switch (rot) {
case Surface.ROTATION_90:
rot = 90;
break;
case Surface.ROTATION_180:
rot = 180;
break;
case Surface.ROTATION_270:
rot = 270;
break;
default:
rot = 0;
break;
}
sb.append(" rotation ").append(rot);
return sb.toString();
}
/*
* OS build information
*/
public static String[] getOSBuildInfo() {
String[] ret = new String[48];
ret[0] = "version.codename";
ret[1] = android.os.Build.VERSION.CODENAME;
ret[2] = "version.incremental";
ret[3] = android.os.Build.VERSION.INCREMENTAL;
ret[4] = "version.release";
ret[5] = android.os.Build.VERSION.RELEASE;
ret[6] = "version.sdk";
ret[7] = "" + android.os.Build.VERSION.SDK_INT;
ret[8] = "board";
ret[9] = android.os.Build.BOARD;
ret[10] = "bootloader";
ret[11] = android.os.Build.BOOTLOADER;
ret[12] = "brand";
ret[13] = android.os.Build.BRAND;
ret[14] = "cpu_abi";
ret[15] = android.os.Build.CPU_ABI;
ret[16] = "cpu_abi2";
ret[17] = android.os.Build.CPU_ABI2;
ret[18] = "device";
ret[19] = android.os.Build.DEVICE;
ret[20] = "display";
ret[21] = android.os.Build.DISPLAY;
ret[22] = "fingerprint";
ret[23] = android.os.Build.FINGERPRINT;
ret[24] = "hardware";
ret[25] = android.os.Build.HARDWARE;
ret[26] = "host";
ret[27] = android.os.Build.HOST;
ret[28] = "id";
ret[29] = android.os.Build.ID;
ret[30] = "manufacturer";
ret[31] = android.os.Build.MANUFACTURER;
ret[32] = "model";
ret[33] = android.os.Build.MODEL;
ret[34] = "product";
ret[35] = android.os.Build.PRODUCT;
ret[36] = "serial";
ret[37] = android.os.Build.SERIAL;
ret[38] = "tags";
ret[39] = android.os.Build.TAGS;
ret[40] = "time";
ret[41] = "" + android.os.Build.TIME;
ret[42] = "type";
ret[43] = android.os.Build.TYPE;
ret[44] = "user";
ret[45] = android.os.Build.USER;
ret[46] = "radio";
if (android.os.Build.VERSION.SDK_INT <
android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ret[47] = android.os.Build.RADIO;
} else {
ret[47] = RadioVersion_ICS.getRadioVersion();
}
return ret;
}
/*
* Withdraw APP
*/
public static void withdraw() {
Runnable dowithdraw = new Runnable() {
public void run() {
mSingleton.moveTaskToBack(true);
}
};
mSingleton.runOnUiThread(dowithdraw);
}
/*
* Keyboard information
*/
public static String getKeyboardInfo() {
int keyboard, keyboardHidden, hardKeyboardHidden;
synchronized (mSingleton.mConfigLock) {
keyboard = mSingleton.mConfig.keyboard;
keyboardHidden = mSingleton.mConfig.keyboardHidden;
hardKeyboardHidden = mSingleton.mConfig.hardKeyboardHidden;
}
StringBuilder sb = new StringBuilder(64);
sb.append("keyboard ");
switch (keyboard) {
case Configuration.KEYBOARD_NOKEYS:
sb.append("none");
break;
case Configuration.KEYBOARD_QWERTY:
sb.append("qwerty");
break;
case Configuration.KEYBOARD_12KEY:
sb.append("12key");
break;
default:
sb.append("unknown");
break;
}
sb.append(" hidden ");
switch (keyboardHidden) {
case Configuration.KEYBOARDHIDDEN_YES:
sb.append("1");
break;
case Configuration.KEYBOARDHIDDEN_NO:
sb.append("0");
break;
default:
sb.append("-1");
break;
}
sb.append(" hard_hidden ");
switch (hardKeyboardHidden) {
case Configuration.HARDKEYBOARDHIDDEN_YES:
sb.append("1");
break;
case Configuration.HARDKEYBOARDHIDDEN_NO:
sb.append("0");
break;
default:
sb.append("-1");
break;
}
return sb.toString();
}
/*
* Location and GPS status handling
*/
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onLocationChanged(Location location) {
String provider = location.getProvider();
if (provider == null) {
return;
}
synchronized (mLocations) {
mLocations.put(provider, location);
}
if (mSingleton.nativeTriggerLocation() < 0) {
stopLocating();
}
}
public static String getLocation() {
StringBuilder sb = new StringBuilder(128);
int k = 0;
synchronized (mLocations) {
for (Entry<String, Location> entry : mLocations.entrySet()) {
Location loc = entry.getValue();
if (loc.getProvider() != null) {
if (++k > 1) {
sb.append(" ");
}
sb.append(loc.getProvider()).append(" {");
sb.append("latitude ").append(loc.getLatitude());
sb.append(" longitude ").append(loc.getLongitude());
sb.append(" time ").append((loc.getTime() / 1000));
sb.append(" velocity ").append(loc.getSpeed());
sb.append(" altitude ").append(loc.getAltitude());
sb.append(" bearing ").append(loc.getBearing());
sb.append(" accuracy ").append(loc.getAccuracy());
sb.append("}");
}
}
}
return sb.toString();
}
public static String getGpsNmeaInfo(int which) {
if (mGpsInfo != null) {
switch (which) {
case 0:
return mGpsInfo.getInfo();
case 1:
return mGpsInfo.getSatellites();
}
}
if ((mNmeaInfo != null) && (which == 2)) {
return mNmeaInfo.getInfo();
}
return null;
}
public static void startLocating(final int minrate, final int mindist) {
if (mLocationMgr == null) {
return;
}
Runnable locStart = new Runnable() {
public void run() {
for (String provider : mLocationMgr.getAllProviders()) {
mLocationMgr.requestLocationUpdates(provider,
minrate, mindist,
mSingleton);
}
}
};
mSingleton.runOnUiThread(locStart);
}
public static void stopLocating() {
if (mLocationMgr == null) {
return;
}
mLocationMgr.removeUpdates(mSingleton);
synchronized (mLocations) {
mLocations.clear();
}
}
/*
* Network/connectivity info
*/
public static String getNetworkInfo() {
String result = "none";
if (mConnMgr == null) {
return result;
}
synchronized (mNetLock) {
if (mNetworkInfo == null) {
mNetworkInfo = mConnMgr.getActiveNetworkInfo();
}
if ((mNetworkInfo != null) && (mNetworkInfo.isConnected())) {
String type = mNetworkInfo.getTypeName();
if (type.toLowerCase().equals("mobile")) {
result = "mobile " + mNetworkInfo.getSubtypeName();
} else {
result = type.toLowerCase();
}
}
}
return result;
}
public static String[] getTetherInfo() {
String list[] = new String[6];
if (mConnMgr != null) {
synchronized (mNetLock) {
list[0] = "active";
if ((mNTa != null) && (mNTa.length > 0)) {
list[1] = mSingleton.encodeStringList(mNTa);
} else {
list[1] = null;
}
list[2] = "available";
if ((mNTl != null) && (mNTl.length > 0)) {
list[3] = mSingleton.encodeStringList(mNTl);
} else {
list[3] = null;
}
list[4] = "error";
if ((mNTe != null) && (mNTe.length > 0)) {
list[5] = mSingleton.encodeStringList(mNTe);
} else {
list[5] = null;
}
}
}
return list;
}
/*
* Location service info
*/
public static String getLocationInfo() {
String result = "none";
try {
LocationManager lm = (LocationManager)
mSingleton.getContext().getSystemService(Context.LOCATION_SERVICE);
boolean on =
androidx.core.location.LocationManagerCompat.isLocationEnabled(lm);
result = on ? "on" : "off";
} catch (Exception e) {
Log.e(TAG, "getLocationInfo failed: " + e.toString());
}
return result;
}
/*
* Desktop shortcuts
*/
public static void shortcut(int op, String name, String arg, String icon) {
switch (op) {
case 0: /* add */
if ((android.os.Build.VERSION.SDK_INT >= 19) &&
!hasPerm("com.android.launcher.permission.INSTALL_SHORTCUT")) {
return;
}
break;
case 1: /* delete */
if ((android.os.Build.VERSION.SDK_INT >= 19) &&
!hasPerm("com.android.launcher.permission.UNINSTALL_SHORTCUT")) {
return;
}
break;
default:
return;
}
Intent shortcut, toRun;
Intent.ShortcutIconResource iconRes = null;
Bitmap iconBitmap = null;
try {
iconRes =
Intent.ShortcutIconResource.fromContext(mSingleton.getContext(),
R.drawable.icon);
} catch (Exception e) {
iconRes = null;
}
if ((android.os.Build.VERSION.SDK_INT < 19) ||
(android.os.Build.VERSION.SDK_INT >= 26)) {
ComponentName comp =
new ComponentName(mSingleton.getPackageName(),
"tk.tcl.wish.AndroWishLauncher");
toRun = new Intent(Intent.ACTION_MAIN);
toRun.setComponent(comp);
} else {
toRun = new Intent(Intent.ACTION_VIEW);
toRun.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
toRun.addCategory(Intent.CATEGORY_DEFAULT);
}
if ((arg != null) && (arg.length() > 0)) {
if (android.os.Build.VERSION.SDK_INT < 14) {
toRun.putExtra("arg", arg);
} else {
toRun.setData(Uri.parse(arg));
}
}
if (icon != null) {
byte b[];
try {
b = android.util.Base64.decode(icon,
android.util.Base64.DEFAULT);
} catch (Exception be) {
b = null;
}
if ((b != null) && (b.length > 0)) {
iconBitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
}
}
switch (op) {
case 0: /* add */
if (android.os.Build.VERSION.SDK_INT >= 26) {
if (Shortcut_API26.add(mSingleton.getContext(),
toRun, name,
iconBitmap, R.drawable.icon)) {
Log.v(TAG, "add shortcut(API26) '" + name + "'" +
((arg != null) ? (" -> '" + arg + "'") : ""));
break;
}
}
shortcut = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");
shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
shortcut.putExtra("duplicate", false);
shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, toRun);
if (iconBitmap != null) {
shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON, iconBitmap);
} else if (iconRes != null) {
shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconRes);
}
Log.v(TAG, "add shortcut '" + name + "'" +
((arg != null) ? (" -> '" + arg + "'") : ""));
mSingleton.sendBroadcast(shortcut);
break;
case 1: /* delete */
if (android.os.Build.VERSION.SDK_INT >= 26) {
Log.v(TAG, "not deleting shortcut(API26) '" + name + "'");
break;
}
shortcut = new Intent("com.android.launcher.action.UNINSTALL_SHORTCUT");
shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
shortcut.putExtra("duplicate", false);
shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, toRun);
if (iconBitmap != null) {
shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON, iconBitmap);
} else if (iconRes != null) {
shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconRes);
}
Log.v(TAG, "delete shortcut '" + name + "'" +
((arg != null) ? (" -> '" + arg + "'") : ""));
mSingleton.sendBroadcast(shortcut);
break;
}
}
/*
* Notifications
*/
public static void notify(int op, int id, String title, String text,
String icon, String action, String uristr,
String type,
String[] cats, String[] comp, String types[],
String[] argv) {
NotificationCompat.Builder nb;
switch (op) {
case 0: /* add/modify */
Intent i;
if ((comp != null) && comp[0].equals("self")) {
i = new Intent(mSingleton.getContext(), mSingleton.getClass());
i.setAction(action);
comp = null;
} else if (action != null) {
i = new Intent(action);
} else {
i = new Intent();
}
i = buildIntent(i, uristr, type, cats, comp);
i.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
addArgsToIntent(i, types, argv);
PendingIntent pi =
PendingIntent.getActivity(mSingleton.getContext(), 0, i, 0);
/*
* if (android.os.Build.VERSION.SDK_INT <
* android.os.Build.VERSION_CODES.HONEYCOMB) {
* Notification n =
* new Notification(R.drawable.icon, title,
* System.currentTimeMillis());
* n.flags = Notification.FLAG_ONGOING_EVENT;
* n.setLatestEventInfo(mSingleton.getContext(), title, text, pi);
* mNotificationMgr.notify(id, n);
* break;
* }
*/
nb = new NotificationCompat.Builder(mSingleton.getContext());
nb.setSmallIcon(R.drawable.icon);
nb.setContentTitle(title);
nb.setContentText(text);
if (icon != null) {
byte b[];
try {
b = android.util.Base64.decode(icon,
android.util.Base64.DEFAULT);
} catch (Exception be) {
b = null;
}
if ((b != null) && (b.length > 0)) {
Bitmap iconBitmap =
BitmapFactory.decodeByteArray(b, 0, b.length);
nb.setLargeIcon(iconBitmap);
}
}
nb.setContentIntent(pi);
mNotificationMgr.notify(id, nb.build());
break;
case 1: /* delete */
mNotificationMgr.cancel(id);
break;
case 2: /* delete all */
mNotificationMgr.cancelAll();
break;
case 3: /* led */
if (android.os.Build.VERSION.SDK_INT <
android.os.Build.VERSION_CODES.HONEYCOMB) {
Notification n = new Notification();
n.defaults = 0;
n.flags = Notification.FLAG_SHOW_LIGHTS;
n.ledARGB = Integer.parseInt(action);
n.ledOffMS = Integer.parseInt(uristr);
n.ledOnMS = Integer.parseInt(type);
mNotificationMgr.notify(id, n);
break;
}
nb = new NotificationCompat.Builder(mSingleton.getContext());
nb.setDefaults(0);
nb.setLights(Integer.parseInt(action), Integer.parseInt(uristr),
Integer.parseInt(type));
nb.setPriority(2);
mNotificationMgr.notify(id, nb.build());
break;
}
}
/*
* Bluetooth
*/
public static String[] bluetooth(int op, String arg) {
if (mBluetoothAdap == null) {
return null;
}
String result[];
int i;
switch (op) {
case 0: /* devices */
Set<BluetoothDevice> btDevs = mBluetoothAdap.getBondedDevices();
i = 0;
for (BluetoothDevice dev : btDevs) {
i++;
}
result = new String[i * 2];
i = 0;
for (BluetoothDevice dev : btDevs) {
result[i++] = dev.getAddress();
result[i++] = dev.getName();
}
return result;
case 1: /* state */
result = new String[1];
i = mBluetoothAdap.getState();
switch (i) {
case BluetoothAdapter.STATE_OFF:
result[0] = "off";
return result;
case BluetoothAdapter.STATE_ON:
result[0] = "on";
return result;
case BluetoothAdapter.STATE_TURNING_OFF:
result[0] = "turning_off";
return result;
case BluetoothAdapter.STATE_TURNING_ON:
result[0] = "turning_on";
return result;
}
result[0] = mBluetoothAdap.isEnabled() ? "on" : "off";
return result;
case 2: /* scanmode */
result = new String[1];
i = mBluetoothAdap.getScanMode();
if (i == BluetoothAdapter.SCAN_MODE_NONE) {
result[0] = "passive";
} else if (i == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
result[0] = "connectable";
} else if (i ==
BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
result[0] = "visible";
} else {
result[0] = "off";
}
return result;
case 3: /* myaddress */
result = new String[2];
result[0] = mBluetoothAdap.getAddress();
result[1] = mBluetoothAdap.getName();
return result;
case 4: /* remoteaddress */
try {
BluetoothDevice btDev = mBluetoothAdap.getRemoteDevice(arg);
result = new String[1];
result[0] = btDev.getName();
return result;
} catch(Exception e) {
}
return null;
case 5: /* on */
result = new String[1];
i = mBluetoothAdap.getState();
if ((i == BluetoothAdapter.STATE_ON) ||
(i == BluetoothAdapter.STATE_TURNING_ON)) {
result[0] = "1";
} else if (!hasPerm(android.Manifest.permission.BLUETOOTH_ADMIN)) {
result[0] = "0";
} else {
result[0] = mBluetoothAdap.enable() ? "1" : "0";
}
return result;
case 6: /* off */
result = new String[1];
i = mBluetoothAdap.getState();
if ((i == BluetoothAdapter.STATE_OFF) ||
(i == BluetoothAdapter.STATE_TURNING_OFF)) {
result[0] = "1";
} else if (!hasPerm(android.Manifest.permission.BLUETOOTH_ADMIN)) {
result[0] = "0";
} else {
result[0] = mBluetoothAdap.disable() ? "1" : "0";
}
return result;
case 7: /* start_discovery */
result = new String[1];
i = mBluetoothAdap.getState();
if (i != BluetoothAdapter.STATE_ON) {
result[0] = "0";
} else if ((android.os.Build.VERSION.SDK_INT < 31) &&
!hasPerm(android.Manifest.permission.BLUETOOTH_ADMIN)) {
result[0] = "0";
} else if ((android.os.Build.VERSION.SDK_INT >= 31) &&
!hasPerm("android.permission.BLUETOOTH_SCAN")) {
result[0] = "0";
} else {
result[0] = mBluetoothAdap.startDiscovery() ? "1" : "0";
}
return result;
case 8: /* cancel_discovery */
result = new String[1];
i = mBluetoothAdap.getState();
if (i != BluetoothAdapter.STATE_ON) {
result[0] = "0";
} else if ((android.os.Build.VERSION.SDK_INT < 31) &&
!hasPerm(android.Manifest.permission.BLUETOOTH_ADMIN)) {
result[0] = "0";
} else if ((android.os.Build.VERSION.SDK_INT >= 31) &&
!hasPerm("android.permission.BLUETOOTH_SCAN")) {
result[0] = "0";
} else {
result[0] = mBluetoothAdap.cancelDiscovery() ? "1" : "0";
}
return result;
}
return null;
}
/*
* USB device information
*/
static String usbHex(int i) {
if ((i < 0) || (i > 0x0fff)) {
return Integer.toHexString(i);
} else if (i <= 0x000f) {
return "000" + Integer.toHexString(i);
} else if (i <= 0x00ff) {
return "00" + Integer.toHexString(i);
}
return "0" + Integer.toHexString(i);
}
static String usbIfString(UsbDevice dev) {
if (dev == null) {
return "";
}
int n, k, v;
int list[] = new int[dev.getInterfaceCount()];
for (n = k = 0; n < dev.getInterfaceCount(); n++) {
int i, csp;
v = dev.getInterface(n).getInterfaceClass();
if ((v < 0) || (v > 255)) {
continue;
}
csp = v << 16;
v = dev.getInterface(n).getInterfaceSubclass();
if ((v < 0) || (v > 255)) {
continue;
}
csp |= v << 8;
v = dev.getInterface(n).getInterfaceProtocol();
if ((v < 0) || (v > 255)) {
continue;
}
csp |= v;
for (i = 0; i < k; i++) {
if (list[i] == csp) {
break;
}
}
if (i >= k) {
list[k++] = csp;
}
}
if (k == 0) {
return "";
}
StringBuilder ifs = new StringBuilder(k * 8);
for (n = 0; n < k; n++) {
ifs.append(":");
v = 0xf00000;
while ((v != 0) && (v & list[n]) == 0) {
ifs.append("0");
v = v >> 4;
}
ifs.append(Integer.toHexString(list[n]));
}
if (n > 0) {
ifs.append(":");
}
return ifs.toString();
}
public static String[] usbdevices() {
return usbdevicesEx(0);
}
public static String[] usbdevicesEx(int op) {
if (mUsbManager == null) {
Log.v(TAG, "no USB manager");
return null;
}
int inc = (op == 0) ? 2 : 3;
HashMap<String, UsbDevice> list = mUsbManager.getDeviceList();
String result[] = new String[list.size() * inc];
Iterator<UsbDevice> iter = list.values().iterator();
int i = 0;
while (iter.hasNext()) {
UsbDevice dev = iter.next();
result[i] = dev.getDeviceName();
result[i + 1] = usbHex(dev.getVendorId()) + ":" +
usbHex(dev.getProductId());
if (inc > 2) {
result[i + 2] = usbIfString(dev);
}
Log.v(TAG, "USB '" + result[i] + "' -> " + result[i + 1]);
i += inc;
}
return result;
}
public static int usbpermission(String devName, int ask) {
if (mUsbManager == null) {
Log.v(TAG, "no USB manager");
return -1;
}
HashMap<String, UsbDevice> list = mUsbManager.getDeviceList();
int ret = -1;
for (final UsbDevice dev : list.values()) {
if (devName.equals(dev.getDeviceName())) {
if (mUsbManager.hasPermission(dev)) {
ret = 1;
} else if (ask == 0) {
ret = 0;
} else {
UsbPermissionTest uph = new UsbPermissionTest(mSingleton);
ret = uph.ask(dev);
}
if (ret == 1) {
/*
* Permission granted, open device and keep it
* open until unplugged when detected by broadcast
* receiver. This allows native code to fetch the
* /dev/bus/usb/* file descriptor using procfs.
*/
UsbDeviceConnection c = mUsbHold.get(devName);
if (c == null) {
c = mUsbManager.openDevice(dev);
if (c != null) {
mUsbHold.put(devName, c);
} else {
ret = 0;
}
}
}
break;
}
}
return ret;
}
public static UsbDevice getUsbDevCheck(String devName) {
if (mUsbManager == null) {
Log.v(TAG, "no USB manager");
return null;
}
HashMap<String, UsbDevice> list = mUsbManager.getDeviceList();
for (final UsbDevice dev : list.values()) {
if (devName.equals(dev.getDeviceName())) {
if (mUsbManager.hasPermission(dev)) {
return dev;
}
UsbPermissionTest uph = new UsbPermissionTest(mSingleton);
return (uph.ask(dev) == 1) ? dev : null;
}
}
return null;
}
/*
* Show/hide spinner
*/
public static void spinner(final int show) {
Runnable spin = new Runnable() {
public void run() {
if (show != 0) {
if (mSpinner != null) {
mSpinner.dismiss();
mSpinner = null;
}
mSpinner = ProgressDialog.show(mSingleton, null, null);
mSpinner.setContentView(new ProgressBar(mSingleton));
} else if (mSpinner != null) {
mSpinner.dismiss();
mSpinner = null;
}
}
};
mSingleton.runOnUiThread(spin);
}
/*
* Set/cancel alarms
*/
public static void alarm(int op, long trigger, long interval,
String action, String uristr, String type,
String[] cats, String[] comp, String types[],
String[] argv) {
Intent i;
if ((comp != null) && comp[0].equals("self")) {
i = new Intent(mSingleton.getContext(), mSingleton.getClass());
i.setAction(action);
comp = null;
} else {
i = new Intent(action);
}
i = buildIntent(i, uristr, type, cats, comp);
i.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
if ((op == 0) || (op == 1)) {
addArgsToIntent(i, types, argv);
}
if (op == 1) {
i.putExtra("tk.tcl.wish.wakeup", true);
}
PendingIntent pi =
PendingIntent.getActivity(mSingleton.getContext(), 0, i,
PendingIntent.FLAG_CANCEL_CURRENT);
switch (op) {
case 0:
if (interval > 0) {
mAlarmManager.setRepeating(AlarmManager.RTC, trigger,
interval, pi);
} else {
mAlarmManager.set(AlarmManager.RTC, trigger, pi);
}
break;
case 1:
if (interval > 0) {
mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, trigger,
interval, pi);
} else {
mAlarmManager.set(AlarmManager.RTC_WAKEUP, trigger, pi);
}
break;
default:
mAlarmManager.cancel(pi);
break;
}
}
/*
* Sensor information
*/
public static String[] sensorList() {
return mSensors.getList();
}
public static int sensorState(int op, int n) {
return mSensors.getState(op, n);
}
public static String sensorGet(int n) {
return mSensors.getData(n);
}
/*
* Phone information
*/
public static String getPhoneInfo() {
if (mPSListener == null) {
return "";
}
StringBuilder sb = new StringBuilder(512);
sb.append("device_id ");
sb.append(mSingleton.toElement(mPhoneManager.getDeviceId()));
sb.append(" device_sw_version ");
sb.append(mSingleton.toElement(mPhoneManager.getDeviceSoftwareVersion()));
sb.append(" line1_number ");
sb.append(mSingleton.toElement(mPhoneManager.getLine1Number()));
sb.append(" sim_serial_number ");
sb.append(mSingleton.toElement(mPhoneManager.getSimSerialNumber()));
sb.append(" voice_mail_alpha_tag ");
sb.append(mSingleton.toElement(mPhoneManager.getVoiceMailAlphaTag()));
sb.append(" voice_mail_number ");
sb.append(mSingleton.toElement(mPhoneManager.getVoiceMailNumber()));
sb.append(" network_country_iso ");
sb.append(mSingleton.toElement(mPhoneManager.getNetworkCountryIso()));
sb.append(" network_operator ");
sb.append(mSingleton.toElement(mPhoneManager.getNetworkOperator()));
sb.append(" network_operator_name ");
sb.append(mSingleton.toElement(mPhoneManager.getNetworkOperatorName()));
sb.append(" sim_country_iso ");
sb.append(mSingleton.toElement(mPhoneManager.getSimCountryIso()));
sb.append(" sim_operator ");
sb.append(mSingleton.toElement(mPhoneManager.getSimOperator()));
sb.append(" sim_operator_name ");
sb.append(mSingleton.toElement(mPhoneManager.getSimOperatorName()));
sb.append(" subscriber_id ");
sb.append(mSingleton.toElement(mPhoneManager.getSubscriberId()));
sb.append(" call_state ");
switch (mPhoneManager.getCallState()) {
case TelephonyManager.CALL_STATE_IDLE:
sb.append("idle");
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
sb.append("offhook");
break;
case TelephonyManager.CALL_STATE_RINGING:
sb.append("ringing");
break;
default:
sb.append("unknown");
break;
}
sb.append(" phone_number ");
synchronized (mPSListener.mLock) {
sb.append(mSingleton.toElement(mPSListener.mNumber));
}
sb.append(" data_state ");
switch (mPhoneManager.getDataState()) {
case TelephonyManager.DATA_DISCONNECTED:
sb.append("disconnected");
break;
case TelephonyManager.DATA_CONNECTING:
sb.append("connecting");
break;
case TelephonyManager.DATA_CONNECTED:
sb.append("connected");
break;
case TelephonyManager.DATA_SUSPENDED:
sb.append("suspended");
break;
default:
sb.append("unknown");
break;
}
sb.append(" data_activity ");
switch (mPhoneManager.getDataActivity()) {
case TelephonyManager.DATA_ACTIVITY_NONE:
sb.append("none");
break;
case TelephonyManager.DATA_ACTIVITY_IN:
sb.append("in");
break;
case TelephonyManager.DATA_ACTIVITY_OUT:
sb.append("out");
break;
case TelephonyManager.DATA_ACTIVITY_INOUT:
sb.append("inout");
break;
case TelephonyManager.DATA_ACTIVITY_DORMANT:
sb.append("dormant");
break;
default:
sb.append("unknown");
break;
}
sb.append(" sim_state ");
switch (mPhoneManager.getSimState()) {
case TelephonyManager.SIM_STATE_ABSENT:
sb.append("absent");
break;
case TelephonyManager.SIM_STATE_PIN_REQUIRED:
sb.append("pin_required");
break;
case TelephonyManager.SIM_STATE_PUK_REQUIRED:
sb.append("puk_required");
break;
case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
sb.append("network_locked");
break;
case TelephonyManager.SIM_STATE_READY:
sb.append("ready");
break;
case TelephonyManager.SIM_STATE_UNKNOWN:
default:
sb.append("unknown");
break;
}
sb.append(" phone_type ");
switch (mPhoneManager.getPhoneType()) {
case TelephonyManager.PHONE_TYPE_NONE:
sb.append("none");
break;
case TelephonyManager.PHONE_TYPE_GSM:
sb.append("gsm");
break;
case TelephonyManager.PHONE_TYPE_CDMA:
sb.append("cdma");
break;
case TelephonyManager.PHONE_TYPE_SIP:
sb.append("sip");
break;
default:
sb.append("unknown");
break;
}
sb.append(" network_type ");
switch (mPhoneManager.getNetworkType()) {
case TelephonyManager.NETWORK_TYPE_1xRTT:
sb.append("1xrtt");
break;
case TelephonyManager.NETWORK_TYPE_CDMA:
sb.append("cdma");
break;
case TelephonyManager.NETWORK_TYPE_EDGE:
sb.append("edge");
break;
case TelephonyManager.NETWORK_TYPE_EHRPD:
sb.append("ehrpd");
break;
case TelephonyManager.NETWORK_TYPE_EVDO_0:
sb.append("evdo_0");
break;
case TelephonyManager.NETWORK_TYPE_EVDO_A:
sb.append("evdo_a");
break;
case TelephonyManager.NETWORK_TYPE_EVDO_B:
sb.append("evdo_b");
break;
case TelephonyManager.NETWORK_TYPE_GPRS:
sb.append("gprs");
break;
case TelephonyManager.NETWORK_TYPE_HSPA:
sb.append("hspa");
break;
case 0x0F: /* TelephonyManager.NETWORK_TYPE_HSPAP, API 13 */
sb.append("hspa+");
break;
case TelephonyManager.NETWORK_TYPE_HSUPA:
sb.append("hsupa");
break;
case TelephonyManager.NETWORK_TYPE_IDEN:
sb.append("iden");
break;
case TelephonyManager.NETWORK_TYPE_LTE:
sb.append("lte");
break;
case TelephonyManager.NETWORK_TYPE_UMTS:
sb.append("umts");
break;
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
default:
sb.append("unknown");
break;
}
synchronized (mPSListener.mLock) {
sb.append(" cdma_dbm ");
sb.append("" + mPSListener.mCdmaDbm);
sb.append(" cdma_ecio ");
sb.append("" + mPSListener.mCdmaEcio);
sb.append(" cdma_ecio ");
sb.append("" + mPSListener.mCdmaEcio);
sb.append(" evdo_dbm ");
sb.append("" + mPSListener.mEvdoDbm);
sb.append(" evdo_ecio ");
sb.append("" + mPSListener.mEvdoEcio);
sb.append(" evdo_snr ");
sb.append("" + mPSListener.mEvdoSnr);
sb.append(" gsm_bit_error_rate ");
sb.append("" + mPSListener.mGsmBitErrorRate);
sb.append(" gsm_signal_strength ");
sb.append("" + mPSListener.mGsmSignalStrength);
sb.append(" is_gsm ");
sb.append(mPSListener.mIsGsm ? "1" : "0");
}
return sb.toString();
}
public static int brightness(int query, int percent) {
if (query != 0) {
WindowManager.LayoutParams lp =
mSingleton.getWindow().getAttributes();
return (int) (lp.screenBrightness * 100);
}
if (percent < 0) {
percent = -100;
} else if (percent > 100) {
percent = 100;
}
final float brightness = (float) (percent / 100.0);
final CountDownLatch latch = new CountDownLatch(1);
Runnable setBrightness = new Runnable() {
public void run() {
WindowManager.LayoutParams lp =
mSingleton.getWindow().getAttributes();
lp.screenBrightness = brightness;
mSingleton.getWindow().setAttributes(lp);
latch.countDown();
}
};
mSingleton.runOnUiThread(setBrightness);
try {
latch.await();
} catch (InterruptedException e) {
}
return 0;
}
/*
* Locale info
*/
public static String[] locale(String lang) {
if ((lang == null) || lang.equals("default")) {
return LocaleAPI1.get(null);
} else if (lang.equals("tts")) {
if (mTTS != null) {
return LocaleAPI1.get(mTTS.getLanguage());
}
return LocaleAPI1.get(null);
}
String[] str = lang.split("_|\\.", 3);
if (str != null) {
if (str.length > 2) {
return LocaleAPI1.get(new Locale(str[0], str[1], str[2]));
} else if (str.length > 1) {
return LocaleAPI1.get(new Locale(str[0], str[1]));
}
}
return LocaleAPI1.get(new Locale(lang));
}
public static String setLocale(String lang) {
if (lang == null) {
return null;
}
String[] str = lang.split("_|\\.", 3);
if (str != null) {
if (str.length > 2) {
Locale.setDefault(new Locale(str[0], str[1], str[2]));
} else if (str.length > 1) {
Locale.setDefault(new Locale(str[0], str[1]));
} else {
return null;
}
}
return Locale.getDefault().toString();
}
/*
* Camera methods
*/
public static boolean cameraOpen(int id) {
if (mCamera != null) {
return mCamera.open(id);
}
return false;
}
public static boolean cameraClose() {
if (mCamera != null) {
return mCamera.close();
}
return false;
}
public static boolean cameraStartStop(int start) {
if (mCamera != null) {
if (start != 0) {
return mCamera.startPreview();
}
return mCamera.stopPreview();
}
return false;
}
public static int cameraGetID(int current) {
if (mCamera != null) {
return mCamera.getID(current != 0);
}
return (current != 0) ? -1 : 0;
}
public static String cameraParameters(String set) {
if (mCamera != null) {
if (set == null) {
return mCamera.getParameters();
}
return mCamera.setParameters(set);
}
return null;
}
public static int cameraOrientation(int degrees) {
if (mCamera != null) {
return mCamera.setCameraDisplayOrientation(degrees);
}
return -1;
}
public static byte[] cameraGetImage(int[] info) {
if (mCamera != null) {
return mCamera.lastImage(info);
}
return null;
}
public static int cameraGetState() {
if (mCamera != null) {
return mCamera.getState();
}
return -1;
}
public static boolean cameraTakePicture() {
if (mCamera != null) {
return mCamera.takePicture();
}
return false;
}
public static byte[] cameraGetPicture() {
if (mCamera != null) {
return mCamera.lastPicture();
}
return null;
}
public static String cameraInfo() {
if (mCamera != null) {
return mCamera.getInfo();
}
return null;
}
/*
* Manifest permission
*/
public static boolean checkPermission(String perm) {
if (perm == null) {
return false;
}
return hasPerm(perm);
}
public static boolean[] checkPermission(int ask, String[] perms) {
if (perms == null) {
return null;
}
if (ask != 0) {
askForPermissions(perms);
}
int len = perms.length;
boolean ret[] = new boolean[len];
for (int i = 0; i < len; i++) {
ret[i] = hasPerm(perms[i]);
}
return ret;
}
/*
* Try to show input method picker popup window.
*/
public static void showIMPicker() {
Runnable r = new Runnable() {
public void run() {
InputMethodManager imm = (InputMethodManager)
mSingleton.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.showInputMethodPicker();
}
}
};
mSingleton.runOnUiThread(r);
}
/*
* android.os.Environment related
*/
public static String osenvGetString(int op, String arg) {
File f = null;
if ((op != 3) && (arg != null)) {
f = new File(arg);
}
switch (op) {
case 0:
return android.os.Environment.getDataDirectory().getAbsolutePath();
case 1:
return android.os.Environment.getDownloadCacheDirectory().getAbsolutePath();
case 2:
return android.os.Environment.getExternalStorageDirectory().getAbsolutePath();
case 3:
if (arg == null) {
return android.os.Environment.getExternalStorageDirectory().getAbsolutePath();
}
return android.os.Environment.getExternalStoragePublicDirectory(arg).getAbsolutePath();
case 4:
return android.os.Environment.getExternalStorageState();
case 5:
return android.os.Environment.getRootDirectory().getAbsolutePath();
default:
return null;
}
}
public static int osenvGetFlag(int op) {
boolean b = false;
switch (op) {
case 0:
b = android.os.Environment.isExternalStorageEmulated();
break;
case 1:
b = android.os.Environment.isExternalStorageRemovable();
break;
}
return b ? 1 : 0;
}
/*
* NFC related: read/write NDEF content to tag.
*/
public static String[] ndefRead(String tagstr, int cached) {
String result[] = new String[2];
Tag tag = mNfcTag;
byte tb[] = null;
result[0] = "unknown error";
result[1] = null;
try {
tb = android.util.Base64.decode(tagstr,
android.util.Base64.DEFAULT);
} catch (Exception te) {
tb = null;
}
if (tb == null) {
result[0] = "tag invalid";
Log.e(TAG, "ndefRead: " + result[0]);
return result;
}
if (tag == null) {
result[0] = "no current tag";
Log.e(TAG, "nfcRead: " + result[0]);
return result;
}
if (!Arrays.equals(tag.getId(), tb)) {
result[0] = "wrong tag id";
Log.e(TAG, "nfcRead: tag not current: " + tagstr + " expecting " +
android.util.Base64.encodeToString(tag.getId(),
android.util.Base64.DEFAULT));
return result;
}
try {
Ndef ndef = Ndef.get(tag);
NdefMessage ndmsg;
if (cached == 0) {
ndef.connect();
ndmsg = ndef.getNdefMessage();
ndef.close();
} else {
ndmsg = ndef.getCachedNdefMessage();
}
if (ndmsg != null) {
result[1] =
android.util.Base64.encodeToString(ndmsg.toByteArray(),
android.util.Base64.DEFAULT);
}
} catch (Exception e) {
result[0] = "read error";
result[1] = null;
Log.e(TAG, "nfcRead: exception: " + e.toString());
return result;
}
result[0] = null;
return result;
}
public static String ndefWrite(String tagstr, String msg) {
Tag tag = mNfcTag;
byte tb[];
try {
tb = android.util.Base64.decode(tagstr,
android.util.Base64.DEFAULT);
} catch (Exception te) {
tb = null;
}
if (tb == null) {
Log.e(TAG, "ndefWrite: tag invalid");
return "tag invalid";
}
byte mb[];
try {
mb = android.util.Base64.decode(msg,
android.util.Base64.DEFAULT);
} catch (Exception me) {
mb = null;
}
if (mb == null) {
Log.e(TAG, "ndefWrite: message invalid");
return "message invalid";
}
if (tag == null) {
Log.e(TAG, "ndefWrite: no current tag");
return "no current tag";
}
if (!Arrays.equals(tag.getId(), tb)) {
Log.e(TAG, "ndefWrite: tag not current: " + tagstr + " expecting " +
android.util.Base64.encodeToString(tag.getId(),
android.util.Base64.DEFAULT));
return "wrong tag id";
}
try {
NdefMessage ndmsg = new NdefMessage(mb);
int size = ndmsg.toByteArray().length;
Ndef ndef = Ndef.get(tag);
if (ndef == null) {
NdefFormatable nfmt = NdefFormatable.get(tag);
if (nfmt == null) {
Log.e(TAG, "ndefWrite: unsupported tech");
return "unsupported tech";
}
nfmt.connect();
nfmt.format(ndmsg);
nfmt.close();
} else {
ndef.connect();
if (!ndef.isWritable()) {
ndef.close();
Log.e(TAG, "ndefWrite: read only tag");
return "read only tag";
}
if (size > ndef.getMaxSize()) {
ndef.close();
Log.e(TAG, "ndefWrite: message too large (" + size + ">" +
ndef.getMaxSize() + ")");
return "message too large";
}
ndef.writeNdefMessage(ndmsg);
ndef.close();
}
} catch (FormatException fe) {
Log.e(TAG, "ndefWrite: format exception: " + fe.toString());
return "wrong ndef format";
} catch (IOException ie) {
Log.e(TAG, "ndefWrite: I/O exception: " + ie.toString());
return "I/O error";
} catch (Exception e) {
Log.e(TAG, "ndefWrite: other exception: " + e.toString());
return "unknown error";
}
return null;
}
public static String ndefFormat(String tagstr, String msg) {
Tag tag = mNfcTag;
byte tb[];
try {
tb = android.util.Base64.decode(tagstr,
android.util.Base64.DEFAULT);
} catch (Exception te) {
tb = null;
}
if (tb == null) {
Log.e(TAG, "ndefFormat: tag invalid");
return "tag invalid";
}
byte mb[] = null;
try {
mb = android.util.Base64.decode(msg,
android.util.Base64.DEFAULT);
} catch (Exception me) {
mb = null;
}
if (mb == null) {
Log.e(TAG, "ndefFormat: message invalid");
return "message invalid";
}
if (tag == null) {
Log.e(TAG, "ndefFormat: no current tag");
return "no current tag";
}
if (!Arrays.equals(tag.getId(), tb)) {
Log.e(TAG, "ndefFormat: tag not current: " + tagstr +
" expecting " +
android.util.Base64.encodeToString(tag.getId(),
android.util.Base64.DEFAULT));
return "wrong tag id";
}
try {
NdefFormatable nfmt = NdefFormatable.get(tag);
if (nfmt == null) {
Log.e(TAG, "ndefFormat: unsupported tech");
return "unsupported tech";
}
nfmt.connect();
NdefMessage ndmsg = new NdefMessage(mb);
nfmt.format(ndmsg);
nfmt.close();
} catch (FormatException fe) {
Log.e(TAG, "ndefFormat: format exception: " + fe.toString());
return "wrong ndef format";
} catch (IOException ie) {
Log.e(TAG, "ndefFormat: I/O exception: " + ie.toString());
return "I/O error";
} catch (Exception e) {
Log.e(TAG, "ndefFormat: other exception: " + e.toString());
return "unknown error";
}
return null;
}
/*
* SharedPreferences related: read/write configuration values.
*/
public static int shpGetBoolean(String file, String key, int def) {
SharedPreferences sp =
mSingleton.getContext().getSharedPreferences(file,
Context.MODE_PRIVATE);
return sp.getBoolean(key, (def != 0)) ? 1 : 0;
}
public static float shpGetFloat(String file, String key, float def) {
SharedPreferences sp =
mSingleton.getContext().getSharedPreferences(file,
Context.MODE_PRIVATE);
return sp.getFloat(key, def);
}
public static int shpGetInt(String file, String key, int def) {
SharedPreferences sp =
mSingleton.getContext().getSharedPreferences(file,
Context.MODE_PRIVATE);
return sp.getInt(key, def);
}
public static long shpGetLong(String file, String key, long def) {
SharedPreferences sp =
mSingleton.getContext().getSharedPreferences(file,
Context.MODE_PRIVATE);
return sp.getLong(key, def);
}
public static String shpGetString(String file, String key, String def) {
SharedPreferences sp =
mSingleton.getContext().getSharedPreferences(file,
Context.MODE_PRIVATE);
return sp.getString(key, def);
}
public static void shpSetBoolean(String file, String key, int val) {
SharedPreferences sp =
mSingleton.getContext().getSharedPreferences(file,
Context.MODE_PRIVATE);
SharedPreferences.Editor ed = sp.edit();
ed.putBoolean(key, (val != 0));
ed.apply();
}
public static void shpSetFloat(String file, String key, float val) {
SharedPreferences sp =
mSingleton.getContext().getSharedPreferences(file,
Context.MODE_PRIVATE);
SharedPreferences.Editor ed = sp.edit();
ed.putFloat(key, val);
ed.apply();
}
public static void shpSetInt(String file, String key, int val) {
SharedPreferences sp =
mSingleton.getContext().getSharedPreferences(file,
Context.MODE_PRIVATE);
SharedPreferences.Editor ed = sp.edit();
ed.putInt(key, val);
ed.apply();
}
public static void shpSetLong(String file, String key, long val) {
SharedPreferences sp =
mSingleton.getContext().getSharedPreferences(file,
Context.MODE_PRIVATE);
SharedPreferences.Editor ed = sp.edit();
ed.putLong(key, val);
ed.apply();
}
public static void shpSetString(String file, String key, String val) {
SharedPreferences sp =
mSingleton.getContext().getSharedPreferences(file,
Context.MODE_PRIVATE);
SharedPreferences.Editor ed = sp.edit();
ed.putString(key, val);
ed.apply();
}
public static void shpRemove(String file, String key) {
SharedPreferences sp =
mSingleton.getContext().getSharedPreferences(file,
Context.MODE_PRIVATE);
SharedPreferences.Editor ed = sp.edit();
ed.remove(key);
ed.apply();
}
public static void shpClear(String file) {
SharedPreferences sp =
mSingleton.getContext().getSharedPreferences(file,
Context.MODE_PRIVATE);
SharedPreferences.Editor ed = sp.edit();
ed.clear();
ed.apply();
}
public static String[] shpKeys(String file, int how) {
SharedPreferences sp =
mSingleton.getContext().getSharedPreferences(file,
Context.MODE_PRIVATE);
Map<String,?> keys = sp.getAll();
int size = keys.size();
if ((how >= 1) && (how <= 2)) {
size *= 2;
}
String result[] = new String[size];
int i = 0;
for (Map.Entry<String,?> entry : keys.entrySet()) {
Object obj = entry.getValue();
if ((obj == null) || (obj instanceof Set)) {
continue;
}
result[i] = entry.getKey();
i++;
if (how == 2) {
/* "all", i.e. Tcl array set form */
result[i] = obj.toString();
i++;
} else if (how == 1) {
/* "alltypes" */
if (obj instanceof Boolean) {
result[i] = "boolean";
} else if (obj instanceof Float) {
result[i] = "float";
} else if (obj instanceof Integer) {
result[i] = "int";
} else if (obj instanceof Long) {
result[i] = "long";
} else {
result[i] = "string";
}
i++;
}
}
return result;
}
public static String getPermissionList() {
ArrayList<String> r = new ArrayList<String>();
PackageManager pm = mSingleton.getContext().getPackageManager();
try {
CharSequence csg;
List<PermissionGroupInfo> g = pm.getAllPermissionGroups(0);
for (PermissionGroupInfo pgi : g) {
csg = pgi.loadLabel(pm);
try {
List<PermissionInfo> p =
pm.queryPermissionsByGroup(pgi.name, 0);
for (PermissionInfo pi : p) {
r.add(pi.name);
}
} catch (Exception e) {
}
}
} catch (Exception e2) {
}
return mSingleton.encodeToList(r);
}
/*
* Permission handling for API >= 24
*/
@Override
public void onRequestPermissionsResult(int requestCode,
String[] perms,
int[] results) {
/*
* No need to invoke
* super.onRequestPermissionsResult(requestCode, perms, results);
* since we do not derive the Activity from AppCompatActivity.
*/
if ((requestCode == 42) && (mPermLock != null)) {
mPermLock.countDown();
}
}
public static synchronized void askForPermissions(final String[] perms) {
/*
* The synchronized keyword above ensures only one
* invocation at any single time since we deal with
* mSingleton and mPermLock.
*/
mPermLock = new CountDownLatch(1);
Runnable r = new Runnable() {
public void run() {
ActivityCompat.requestPermissions(mSingleton, perms, 42);
}
};
mSingleton.runOnUiThread(r);
try {
mPermLock.await();
} catch (InterruptedException e) {
}
mPermLock = null;
}
/*
* Native setenv
*/
public static native int nativeSetenv(String name, String value);
/*
* Native add path component to environment variable
*/
public static native int nativeAddpath(String name, String path);
/*
* Native initializer
*/
public static native void nativeInit(int api, String lang);
/*
* Native set file to be sourced
*/
public static native void nativeSetStartupFile(String file);
/*
* Callback when activity reports result back
*/
public static native void nativeIntentCallback(int id, int ret,
String action,
String uristr,
String type,
String[] cats,
String[] args);
/*
* Callback to report broadcast intent
*/
public static native void nativeBroadcastCallback(int ret,
String action,
String uristr,
String type,
String[] cats,
String[] args);
/*
* Callback to indicate location update
*/
public static native int nativeTriggerLocation();
/*
* Callback to indicate GPS update
*/
public static native int nativeTriggerGpsUpdate();
/*
* Callback to indicate NMEA update
*/
public static native int nativeTriggerNmeaUpdate();
/*
* Callback to indicate network status update
*/
public static native int nativeTriggerNetworkInfo();
/*
* Callback to indicate tether status update
*/
public static native int nativeTriggerTetherInfo();
/*
* Callback to indicate bluetooth status update
*/
public static native int nativeTriggerBluetooth();
/*
* Callback to indicate keyboard status update
*/
public static native int nativeTriggerKeyboard();
/*
* Callback to indicate sensor update
*/
public static native int nativeTriggerSensor(int n);
/*
* Callback to indicate USB attached/detached
*/
public static native int nativeTriggerUsb(int n);
/*
* Callback to indicate Intent
*/
public static native void nativeTriggerIntent(String action, String uristr,
String type, String[] cats,
String[] args);
/*
* Callback to indicate RecognitionListener events
*/
public static native void nativeTriggerSpeech(String[] args);
/*
* Callback to indicate phone state events
*/
public static native void nativeTriggerPhoneState(int type);
/*
* Callback to indicate text-to-speech state events
*/
public static native void nativeTriggerTTS(int type, int id);
}
/*
* Helper class to carry out activity
*/
class Runner implements Runnable {
AndroWish mAW;
Intent mI;
int mId;
public Runner(AndroWish aw, Intent i, int id) {
mAW = aw;
mI = i;
mId = id;
}
public void run() {
try {
if (mId == -2) {
mAW.sendBroadcast(mI);
} else if (mId < 0) {
mAW.startActivity(mI);
} else {
mAW.startActivityForResult(mI, mId);
}
} catch (Exception e) {
}
}
}
/*
* Start AndroWish activity from a script
*/
class AndroWishScript extends AndroWish {
private static final String TAG = AndroWish.TAG;
/*
* Callback on application startup
*/
@Override
public void onCreate(Bundle savedInstanceData) {
Intent intent = getIntent();
String file = intent.getStringExtra("arg");
if (file == null) {
file = intent.getDataString();
}
super.onCreate(savedInstanceData);
if (file == null) {
finish();
return;
}
Log.v(TAG, "run script '" + file + "'");
nativeSetStartupFile(file);
}
}
/*
* Ice cream sandwich and beyond
*/
final class RadioVersion_ICS {
public static String getRadioVersion() {
return android.os.Build.getRadioVersion();
}
}
/*
* Sensor data of one sensor
*/
final class SensorItem {
Sensor mSensor;
int mType;
boolean mEnabled;
int mTrigger;
int mOverflow;
int mAccuracy;
float mValues[];
float mOrientation[];
float mInclination;
float mRotationMatrix[];
public SensorItem(Sensor sensor) {
mSensor = sensor;
mType = (sensor == null) ? -1 : sensor.getType();
mEnabled = false;
mTrigger = 0;
mOverflow = 0;
mAccuracy = 0;
mValues = null;
mOrientation = null;
mInclination = 0;
mRotationMatrix = null;
}
}
/*
* Helper class to deal with sensors
*/
class Sensors implements SensorEventListener {
AndroWish mAW;
SensorManager mSensorManager;
SensorItem mSensors[];
public Sensors(AndroWish aw) {
mAW = aw;
mSensorManager =
(SensorManager) mAW.getSystemService(Context.SENSOR_SERVICE);
List<Sensor> l = mSensorManager.getSensorList(Sensor.TYPE_ALL);
mSensors = new SensorItem[26];
int i = 0;
for (Sensor s : l) {
if (s.getType() == 17) {
/* Sensor.TYPE_SIGNIFICANT_MOTION not handled */
continue;
}
mSensors[i++] = new SensorItem(s);
if (i >= mSensors.length) {
break;
}
}
}
void pause() {
mSensorManager.unregisterListener(this);
}
void resume() {
for (int i = 0; i < mSensors.length; i++) {
SensorItem si = mSensors[i];
if ((si != null) && si.mEnabled) {
mSensorManager.registerListener(this, si.mSensor,
SensorManager.SENSOR_DELAY_NORMAL);
}
}
}
public void onSensorChanged(SensorEvent event) {
int i, ti = -1;
SensorItem sa = null, sm = null, st = null;
for (i = 0; i < mSensors.length; i++) {
SensorItem si = mSensors[i];
if ((si != null) && (si.mSensor == event.sensor)) {
boolean doRM = false;
if (si.mEnabled) {
if (si.mType == Sensor.TYPE_ACCELEROMETER) {
sa = si;
} else if (si.mType == Sensor.TYPE_MAGNETIC_FIELD) {
sm = si;
}
if ((si.mType == Sensor.TYPE_ROTATION_VECTOR) ||
(si.mType == 15) || /* Sensor.TYPE_GAME_ROTATION_VECTOR */
(si.mType == 20)) { /* Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR */
doRM = true;
}
}
synchronized (si) {
si.mAccuracy = event.accuracy;
if ((si.mValues == null) ||
(si.mValues.length != event.values.length)) {
si.mValues = new float[event.values.length];
}
System.arraycopy(event.values, 0, si.mValues, 0,
event.values.length);
if (doRM) {
if (si.mRotationMatrix == null) {
si.mRotationMatrix = new float[16];
}
SensorManager.getRotationMatrixFromVector(si.mRotationMatrix,
event.values);
}
if (si.mEnabled) {
st = si;
ti = i;
}
}
break;
}
}
if ((sa != null) || (sm != null)) {
if (sa == null) {
for (i = 0; i < mSensors.length; i++) {
SensorItem si = mSensors[i];
if ((si != null) &&
(si.mType == Sensor.TYPE_ACCELEROMETER)) {
if (si.mEnabled && (si.mValues != null)) {
sa = si;
}
break;
}
}
} else {
for (i = 0; i < mSensors.length; i++) {
SensorItem si = mSensors[i];
if ((si != null) &&
(si.mType == Sensor.TYPE_MAGNETIC_FIELD)) {
if (si.mEnabled && (si.mValues != null)) {
sm = si;
}
break;
}
}
}
}
if ((sa != null) && (sm != null)) {
float R[] = new float[9];
float I[] = new float[9];
if (SensorManager.getRotationMatrix(R, I, sa.mValues, sm.mValues)) {
synchronized (sm) {
if (sm.mOrientation == null) {
sm.mOrientation = new float[3];
}
SensorManager.getOrientation(R, sm.mOrientation);
sm.mInclination = SensorManager.getInclination(I);
}
}
}
if (st != null) {
synchronized (st) {
if (st.mTrigger < 4) {
if (mAW.nativeTriggerSensor(ti) > 0) {
st.mTrigger++;
} else {
st.mOverflow++;
}
} else {
st.mOverflow++;
}
if (st.mOverflow > 64) {
mSensorManager.unregisterListener(this, st.mSensor);
st.mEnabled = false;
st.mOverflow = 0;
st.mOrientation = null;
st.mRotationMatrix = null;
}
}
}
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
for (int i = 0; i < mSensors.length; i++) {
SensorItem si = mSensors[i];
if ((si != null) && (si.mSensor == sensor)) {
synchronized (si) {
si.mAccuracy = accuracy;
if (si.mEnabled) {
if (si.mTrigger < 4) {
if (mAW.nativeTriggerSensor(i) > 0) {
si.mTrigger++;
} else {
si.mOverflow++;
}
} else {
si.mOverflow++;
}
}
if (si.mOverflow > 64) {
mSensorManager.unregisterListener(this, si.mSensor);
si.mEnabled = false;
si.mOverflow = 0;
si.mOrientation = null;
si.mRotationMatrix = null;
}
}
}
}
}
public String[] getList() {
int i, n;
for (n = i = 0; n < mSensors.length; n++) {
if (mSensors[n] != null) {
i++;
}
}
if (i == 0) {
return null;
}
String ret[] = new String[i];
for (n = i = 0; n < mSensors.length; n++) {
if (mSensors[n] == null) {
continue;
}
Sensor s = mSensors[n].mSensor;
StringBuilder sb = new StringBuilder(256);
sb.append("index ").append(i).append(" name ");
sb.append(mAW.toElement(s.getName()));
sb.append(" type ");
switch (mSensors[n].mType) {
case Sensor.TYPE_ACCELEROMETER:
sb.append("accelerometer");
break;
case Sensor.TYPE_AMBIENT_TEMPERATURE:
case Sensor.TYPE_TEMPERATURE:
sb.append("temperature");
break;
case 15: /* Sensor.TYPE_GAME_ROTATION_VECTOR */
sb.append("game_rotation_vector");
break;
case 20: /* Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR */
sb.append("geomagnetic_rotation_vector");
break;
case Sensor.TYPE_GRAVITY:
sb.append("gravity");
break;
case Sensor.TYPE_GYROSCOPE:
sb.append("gyroscope");
break;
case 16: /* Sensor.TYPE_GYROSCOPE_UNCALIBRATED */
sb.append("gyroscope_uncalibrated");
break;
case Sensor.TYPE_LIGHT:
sb.append("light");
break;
case Sensor.TYPE_LINEAR_ACCELERATION:
sb.append("linear_acceleration");
break;
case Sensor.TYPE_MAGNETIC_FIELD:
sb.append("magnetic_field");
break;
case 14: /* Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED */
sb.append("magnetic_field_uncalibrated");
break;
case Sensor.TYPE_ORIENTATION:
sb.append("orientation");
break;
case Sensor.TYPE_PRESSURE:
sb.append("pressure");
break;
case Sensor.TYPE_PROXIMITY:
sb.append("proximity");
break;
case Sensor.TYPE_RELATIVE_HUMIDITY:
sb.append("relative_humidity");
break;
case 11: /* Sensor.TYPE_ROTATION_VECTOR */
sb.append("rotation_vector");
break;
case 17: /* Sensor.TYPE_SIGNIFICANT_MOTION */
sb.append("significant_motion");
break;
case 19: /* Sensor.TYPE_STEP_COUNTER */
sb.append("step_counter");
break;
case 18: /* Sensor.TYPE_STEP_DETECTOR */
sb.append("step_detector");
break;
default:
sb.append("unknown");
break;
}
int mindelay = s.getMinDelay();
if (mindelay > 0) {
mindelay = mindelay / 1000; /* milliseconds */
if (mindelay <= 0) {
mindelay = 1;
}
} else {
mindelay = 0;
}
sb.append(" mindelay ").append(mindelay);
sb.append(" maxrange ").append(s.getMaximumRange());
sb.append(" resolution ").append(s.getResolution());
sb.append(" power ").append(s.getPower());
ret[i++] = sb.toString();
}
return ret;
}
public int getState(int op, int n) {
if (n < 0 || n > mSensors.length) {
return 0;
}
if (mSensors[n] == null) {
return 0;
}
final SensorItem si = mSensors[n];
final Sensors s = this;
switch (op) {
case 0: /* enable */
if (!si.mEnabled) {
final SensorManager sensorManager = mSensorManager;
Runnable doit = new Runnable() {
public void run() {
if (sensorManager.registerListener(s, si.mSensor,
SensorManager.SENSOR_DELAY_NORMAL)) {
si.mEnabled = true;
}
}
};
mAW.runOnUiThread(doit);
}
n = 1;
break;
case 1: /* disable */
if (si.mEnabled) {
final SensorManager sensorManager = mSensorManager;
Runnable doit = new Runnable() {
public void run() {
sensorManager.unregisterListener(s, si.mSensor);
si.mEnabled = false;
si.mOrientation = null;
}
};
mAW.runOnUiThread(doit);
}
n = 1;
break;
default: /* query */
n = si.mEnabled ? 1 : 0;
break;
}
return n;
}
public String getData(int n) {
if (n < 0 || n > mSensors.length) {
return null;
}
if (mSensors[n] == null) {
return null;
}
SensorItem si = mSensors[n];
StringBuilder sb = new StringBuilder(256);
sb.append("index ").append(n);
sb.append(" enabled ").append(si.mEnabled ? 1 : 0);
sb.append(" maxrange ").append(si.mSensor.getMaximumRange());
sb.append(" resolution ").append(si.mSensor.getResolution());
synchronized (si) {
sb.append(" accuracy ").append(si.mAccuracy).append(" values {");
if (si.mValues != null) {
for (n = 0; n < si.mValues.length; n++) {
if (n > 0) {
sb.append(" ");
}
sb.append(si.mValues[n]);
}
}
sb.append("}");
if ((si.mType == Sensor.TYPE_MAGNETIC_FIELD) &&
(si.mOrientation != null)) {
sb.append(" orientation {");
for (n = 0; n < si.mOrientation.length; n++) {
if (n > 0) {
sb.append(" ");
}
sb.append(si.mOrientation[n]);
}
sb.append("} inclination ").append(si.mInclination);
} else if ((si.mType == Sensor.TYPE_PRESSURE) &&
(si.mValues != null)) {
sb.append(" altitude ");
sb.append(SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, si.mValues[0]));
}
if (si.mRotationMatrix != null && si.mRotationMatrix.length > 0) {
sb.append(" rotation_matrix {");
for (n = 0; n < si.mRotationMatrix.length; n++) {
if (n > 0) {
sb.append(" ");
}
sb.append(si.mRotationMatrix[n]);
}
sb.append("}");
}
si.mTrigger = si.mOverflow = 0;
}
return sb.toString();
}
}
/*
* GPS status handling
*/
class GpsInfo implements GpsStatus.Listener {
AndroWish mAW;
boolean mStarted;
int mFirstFix;
GpsStatus mGpsStatus;
public GpsInfo(AndroWish aw) {
mStarted = false;
mFirstFix = 0;
mGpsStatus = null;
mAW = aw;
}
@Override
public void onGpsStatusChanged(int event) {
boolean trigger = false;
synchronized (this) {
switch (event) {
case GpsStatus.GPS_EVENT_STARTED:
mGpsStatus = mAW.mLocationMgr.getGpsStatus(mGpsStatus);
if (mGpsStatus != null) {
mFirstFix = mGpsStatus.getTimeToFirstFix();
}
mStarted = true;
trigger = true;
break;
case GpsStatus.GPS_EVENT_STOPPED:
mGpsStatus = null;
mStarted = false;
mFirstFix = 0;
trigger = true;
break;
case GpsStatus.GPS_EVENT_FIRST_FIX:
case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
mGpsStatus = mAW.mLocationMgr.getGpsStatus(mGpsStatus);
if (mGpsStatus != null) {
mFirstFix = mGpsStatus.getTimeToFirstFix();
trigger = true;
}
break;
}
}
if (event == GpsStatus.GPS_EVENT_STOPPED) {
if (mAW.mNmeaInfo != null) {
mAW.mNmeaInfo.clear();
}
}
if (trigger) {
mAW.nativeTriggerGpsUpdate();
}
}
public String getInfo() {
StringBuilder sb = new StringBuilder(64);
synchronized (this) {
sb.append("state ").append(mStarted ? "on" : "off");
sb.append(" first_fix ").append(mFirstFix);
}
return sb.toString();
}
public String getSatellites() {
StringBuilder sb = new StringBuilder(4096);
synchronized (this) {
if (mGpsStatus != null) {
int n = 0;
for (GpsSatellite s : mGpsStatus.getSatellites()) {
if (n > 0) {
sb.append(" ");
}
sb.append(n).append(" {index ").append(n);
sb.append(" azimuth ").append(s.getAzimuth());
sb.append(" elevation ").append(s.getElevation());
sb.append(" prn ").append(s.getPrn());
sb.append(" snr ").append(s.getSnr());
sb.append(" almanac ").append(s.hasAlmanac() ? 1 : 0);
sb.append(" ephemeris ").append(s.hasEphemeris() ? 1 : 0);
sb.append(" infix ").append(s.usedInFix() ? 1 : 0);
sb.append("}");
n++;
}
}
}
return sb.toString();
}
}
/*
* NMEA sentence handling
*/
class NmeaInfo implements GpsStatus.NmeaListener {
AndroWish mAW;
long mTime0, mTime1;
StringBuilder mNmeaBuf;
String mNmeaStr;
public NmeaInfo(AndroWish aw) {
mAW = aw;
mTime0 = System.currentTimeMillis() / 1000;
mTime1 = 0;
mNmeaBuf = new StringBuilder(4096);
mNmeaStr = null;
}
@Override
public void onNmeaReceived(long time, String nmea) {
boolean trigger = false;
time = time / 1000;
synchronized (this) {
if (mTime0 != time) {
if (mNmeaBuf.length() > 0) {
mTime1 = mTime0;
mNmeaStr = new String(mNmeaBuf);
trigger = true;
}
}
}
if (trigger) {
mAW.nativeTriggerNmeaUpdate();
mTime0 = time;
mNmeaBuf.setLength(0);
}
mNmeaBuf.append(nmea);
}
public void clear() {
mTime0 = System.currentTimeMillis() / 1000;
mNmeaBuf.setLength(0);
}
public String getInfo() {
long time;
String nmea = null;
synchronized (this) {
time = mTime1;
if ((mNmeaStr != null) && (mNmeaStr.length() > 0)) {
nmea = mNmeaStr;
}
}
if (nmea != null) {
StringBuilder sb = new StringBuilder(nmea.length() + 64);
sb.append("time ").append(time);
sb.append(" nmea ").append(mAW.toElement(nmea));
return sb.toString();
}
return null;
}
}
/*
* Helper class to carry out speech recognition call
*/
class SpRunner implements Runnable {
SpeechRec mSpeechRec;
Intent mI;
int mOp;
public SpRunner(SpeechRec sr, Intent i, int op) {
mSpeechRec = sr;
mI = i;
mOp = op;
}
public void run() {
try {
switch (mOp) {
case -1: /* cancel */
if (mSpeechRec != null) {
mSpeechRec.cancel();
}
break;
case -2: /* stop */
if (mSpeechRec != null) {
mSpeechRec.stop();
}
break;
case -3: /* start */
if (mSpeechRec != null) {
mSpeechRec.start(mI);
}
break;
}
} catch (Exception e) {
}
}
}
/*
* RecognitionListener helper class
*/
class SpeechRec implements RecognitionListener {
private static final String TAG = AndroWish.TAG;
AndroWish mAW;
SpeechRecognizer mSpeechRecognizer;
public SpeechRec(AndroWish aw) {
mAW = aw;
mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(mAW);
}
public void onBeginningOfSpeech() {
speechCallback("begin");
}
public void onBufferReceived(byte[] buffer) {
}
public void onEndOfSpeech() {
speechCallback("end");
}
public void onError(int error) {
speechCallback("error", error);
}
public void onEvent(int eventType, Bundle params) {
speechCallback("event", eventType, params);
}
public void onPartialResults(Bundle partialResults) {
speechCallback("partialresult", partialResults);
}
public void onReadyForSpeech(Bundle params) {
speechCallback("ready", params);
}
public void onResults(Bundle results) {
speechCallback("result", results);
}
public void onRmsChanged(float rmsdB) {
speechCallback("rms", rmsdB);
}
void speechCallback(String op) {
int k = 0;
String args[] = new String[2];
args[k++] = "type";
args[k++] = op;
Log.v(TAG, "nativeTriggerSpeech: " + op + " " + args);
mAW.nativeTriggerSpeech(args);
}
void speechCallback(String op, Bundle data) {
int k = 0;
String args[];
if (data != null) {
String[] addargs = new String[2];
addargs[0] = "type";
addargs[1] = op;
args = mAW.formatBundle(data, addargs);
} else {
args = new String[2];
args[0] = "type";
args[1] = op;
}
Log.v(TAG, "nativeTriggerSpeech: " + op + " " + args);
mAW.nativeTriggerSpeech(args);
}
void speechCallback(String op, int num, Bundle data) {
int k = 0;
String args[];
if (data != null) {
String[] addargs = new String[4];
addargs[0] = "type";
addargs[1] = op;
addargs[2] = "value";
addargs[3] = "" + num;
args = mAW.formatBundle(data, addargs);
} else {
args = new String[4];
args[0] = "type";
args[1] = op;
args[2] = "value";
args[3] = "" + num;
}
Log.v(TAG, "nativeTriggerSpeech: " + op + " " + args);
mAW.nativeTriggerSpeech(args);
}
void speechCallback(String op, int data) {
String args[] = new String[4];
args[0] = "type";
args[1] = op;
args[2] = "value";
args[3] = "" + data;
Log.v(TAG, "nativeTriggerSpeech: " + op + " " + args);
mAW.nativeTriggerSpeech(args);
}
void speechCallback(String op, float data) {
String args[] = new String[4];
args[0] = "type";
args[1] = op;
args[2] = "value";
args[3] = "" + data;
Log.v(TAG, "nativeTriggerSpeech: " + op + " " + args);
mAW.nativeTriggerSpeech(args);
}
public void cancel() {
if (mSpeechRecognizer != null) {
mSpeechRecognizer.cancel();
}
}
public void stop () {
if (mSpeechRecognizer != null) {
mSpeechRecognizer.stopListening();
}
}
public void start(Intent i) {
if (mSpeechRecognizer != null) {
mSpeechRecognizer.cancel();
mSpeechRecognizer.setRecognitionListener(this);
mSpeechRecognizer.startListening(i);
}
}
public void destroy() {
if (mSpeechRecognizer != null) {
mSpeechRecognizer.cancel();
mSpeechRecognizer.destroy();
mSpeechRecognizer = null;
}
}
}
/*
* Phone state listener helper class
*/
class PSListener extends PhoneStateListener {
private static final String TAG = AndroWish.TAG;
AndroWish mAW;
Object mLock;
String mNumber;
int mCdmaDbm;
int mCdmaEcio;
int mEvdoDbm;
int mEvdoEcio;
int mEvdoSnr;
int mGsmBitErrorRate;
int mGsmSignalStrength;
boolean mIsGsm;
public PSListener(AndroWish aw) {
super();
mAW = aw;
mLock = new Object();
}
public void onCallForwardingIndicatorChanged(boolean cfi) {
}
public void onCallStateChanged(int state, String incomingNumber) {
if ((state != TelephonyManager.CALL_STATE_OFFHOOK) &&
(state != TelephonyManager.CALL_STATE_RINGING)) {
incomingNumber = null;
}
synchronized (mLock) {
if (incomingNumber != null) {
mNumber = new String(incomingNumber);
} else {
mNumber = null;
}
}
mAW.nativeTriggerPhoneState(1);
}
public void onCellLocationChanged(CellLocation location) {
}
public void onDataActivity(int direction) {
mAW.nativeTriggerPhoneState(2);
}
public void onDataConnectionStateChanged(int state) {
mAW.nativeTriggerPhoneState(3);
}
public void onDataConnectionStateChanged(int state, int networkType) {
}
public void onMessageWaitingIndicatorChanged(boolean mwi) {
}
public void onServiceStateChanged(ServiceState serviceState) {
mAW.nativeTriggerPhoneState(4);
}
public void onSignalStrengthChanged(int asu) {
}
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
synchronized (mLock) {
mCdmaDbm = signalStrength.getCdmaDbm();
mCdmaEcio = signalStrength.getCdmaEcio();
mEvdoDbm = signalStrength.getEvdoDbm();
mEvdoEcio = signalStrength.getEvdoEcio();
mEvdoSnr = signalStrength.getEvdoSnr();
mGsmBitErrorRate = signalStrength.getGsmBitErrorRate();
mGsmSignalStrength = signalStrength.getGsmSignalStrength();
mIsGsm = signalStrength.isGsm();
}
mAW.nativeTriggerPhoneState(5);
}
}
class BCRecvr extends BroadcastReceiver {
AndroWish mAW;
public BCRecvr(AndroWish aw) {
super();
mAW = aw;
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null) {
mAW.broadcastReceived(intent, getResultCode());
}
}
}
/*
* Helper class to deal with java.util.Locale
*/
class LocaleAPI1 {
public static String[] get(Locale loc) {
if (loc == null) {
loc = Locale.getDefault();
}
int i = 0;
String result[] = new String[18];
result[i++] = "country";
result[i++] = loc.getCountry();
result[i++] = "display_country";
result[i++] = loc.getDisplayCountry();
result[i++] = "display_language";
result[i++] = loc.getDisplayLanguage();
result[i++] = "display_name";
result[i++] = loc.getDisplayName();
result[i++] = "display_variant";
result[i++] = loc.getDisplayVariant();
result[i++] = "iso3_country";
result[i++] = loc.getISO3Country();
result[i++] = "iso3_language";
result[i++] = loc.getISO3Language();
result[i++] = "language";
result[i++] = loc.getLanguage();
result[i++] = "variant";
result[i++] = loc.getVariant();
return result;
}
}
/*
* Helper class for USB permission dialog
*/
class UsbPermissionTest {
AndroWish mAW;
UsbDevice mDev;
boolean mGranted;
final String USB_PERMISSION = "tk.tcl.wish.UsbPermission";
public UsbPermissionTest(AndroWish aw) {
mAW = aw;
mDev = null;
mGranted = false;
}
public int ask(UsbDevice dev) {
mDev = dev;
mGranted = false;
final CountDownLatch lock = new CountDownLatch(1);
final BroadcastReceiver recvr = new BroadcastReceiver() {
public void onReceive(Context ctx, Intent i) {
String a = i.getAction();
if (a.equals(USB_PERMISSION)) {
UsbDevice d = (UsbDevice) i.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (i.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if ((d != null) &&
(d.getDeviceName().equals(mDev.getDeviceName()))) {
mGranted = true;
}
} else {
Log.e("AndroWish", "permission denied for device " +
d.getDeviceName());
}
lock.countDown();
}
}
};
Context ctx = mAW.getContext();
PendingIntent pi =
PendingIntent.getBroadcast(ctx, 0, new Intent(USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(USB_PERMISSION);
ctx.registerReceiver(recvr, filter);
mAW.mUsbManager.requestPermission(dev, pi);
try {
lock.await();
} catch (InterruptedException ie) {
pi.cancel();
}
ctx.unregisterReceiver(recvr);
return mGranted ? 1 : 0;
}
}
/*
* Helper class for text-to-speech events
*/
class TTS_UPL extends android.speech.tts.UtteranceProgressListener
implements TextToSpeech.OnInitListener {
AndroWish mAW;
public TTS_UPL(AndroWish aw) {
mAW = aw;
}
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
mAW.mTTS.setOnUtteranceProgressListener(this);
mAW.mTTSAvailable = true;
}
mAW.nativeTriggerTTS(0, status);
}
public void onShutdown() {
mAW.mTTSAvailable = false;
mAW.nativeTriggerTTS(0, -2);
}
public void onDone(String id) {
mAW.nativeTriggerTTS(2, Integer.parseInt(id));
}
public void onError(String id) {
mAW.nativeTriggerTTS(3, Integer.parseInt(id));
}
public void onStart(String id) {
mAW.nativeTriggerTTS(1, Integer.parseInt(id));
}
}
final class TextFromHtml {
public static android.text.Spanned text(String text) {
android.text.Spanned s = null;
if (android.os.Build.VERSION.SDK_INT >= 24) {
Method m = null;
try {
Class c[] = new Class[2];
c[0] = String.class;
c[1] = int.class;
m = android.text.Html.class.getMethod("fromHtml", c);
} catch (Exception e) {
}
if (m != null) {
try {
s = (android.text.Spanned) m.invoke(null, text, 0);
} catch (Exception e) {
}
}
}
if (s == null) {
s = android.text.Html.fromHtml(text);
}
return s;
}
}