/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 2000-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine is distributed in the hope that it will * * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * * This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 * * A copy of this license is located in file license.txt at the root of this * * SDK or can be downloaded here: * * http://www.gnu.org/licenses/lgpl-3.0.txt * * * *********************************************************************************/ package totalcross.android; import android.app.*; import android.content.*; import android.content.res.*; import android.database.*; import android.net.*; import android.os.*; import android.provider.*; import android.util.*; import android.view.*; import android.view.inputmethod.*; import com.intermec.aidc.*; import java.io.*; import java.util.*; import java.util.concurrent.*; import totalcross.*; import totalcross.android.compat.*; public class Loader extends Activity implements BarcodeReadListener { public static boolean IS_EMULATOR = android.os.Build.MODEL.toLowerCase().indexOf("sdk") >= 0; public Handler achandler; private boolean runningVM; private static final int TAKE_PHOTO = 1234324330; private static final int JUST_QUIT = 1234324331; private static final int MAP_RETURN = 1234324332; private static final int ZXING_RETURN = 1234324333; private static final int EXTCAMERA_RETURN = 1234324334; private static final int CAMERA_PIC_REQUEST = 1337; private static boolean onMainLoop; public static boolean isFullScreen; private static boolean onCreateCalled; // /** Called when the activity is first created. */ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); try { AndroidUtils.initialize(this); if (isSingleApk() && onCreateCalled) // bypass bug that will cause a new instance each time the app is minimized and called again (2282) { System.exit(2); return; } onCreateCalled = true; AndroidUtils.checkInstall(); runVM(); } catch (Throwable e) { String stack = Log.getStackTraceString(e); AndroidUtils.debug(stack); AndroidUtils.error("An exception was issued when launching the program. Please inform this stack trace to your software's vendor:\n\n"+stack,true); } } public void onRestart() { super.onRestart(); } protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case Level5.BT_MAKE_DISCOVERABLE: Level5.getInstance().setResponse(resultCode != Activity.RESULT_CANCELED,null); break; case JUST_QUIT: finish(); break; case TAKE_PHOTO: Launcher4A.pictureTaken(resultCode != RESULT_OK ? 1 : 0); break; case CAMERA_PIC_REQUEST: Launcher4A.pictureTaken(resultCode != RESULT_OK ? 1 : 0); break; case MAP_RETURN: Launcher4A.showingMap = false; break; case ZXING_RETURN: Launcher4A.zxingResult = resultCode == RESULT_OK ? data.getStringExtra("SCAN_RESULT") : null; Launcher4A.callingZXing = false; break; case EXTCAMERA_RETURN: String[] projection = {MediaStore.Images.Media.DATA, BaseColumns._ID}; Cursor cursor = managedQuery(capturedImageURI, projection, null, null, null); int dataIdx = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); int idIdx = cursor.getColumnIndexOrThrow(BaseColumns._ID); cursor.moveToFirst(); String capturedImageFilePath = cursor.getString(dataIdx); if (capturedImageFilePath == null || !AndroidUtils.copyFile(capturedImageFilePath,imageFN,cameraType == CAMERA_NATIVE_NOCOPY)) resultCode = RESULT_OK+1; // error else if (cameraType == CAMERA_NATIVE_NOCOPY) // if the file was deleted, delete from database too try{ getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, BaseColumns._ID + "=" + cursor.getString(idIdx), null);} catch (Exception e) {AndroidUtils.handleException(e,false);} Launcher4A.pictureTaken(resultCode != RESULT_OK ? 1 : 0); break; } } private static final int SHOW_SATELLITE_PHOTOS = 1; public static final int USE_WAZE = 2; private void callRoute(double latI, double lonI, double latF, double lonF, String coord, int flags) { try { if ((flags & USE_WAZE) != 0) { try { String url = "waze://?ll="+latI+","+lonI+"&navigate=yes"; Intent intent = new Intent( Intent.ACTION_VIEW, Uri.parse( url ) ); Launcher4A.showingMap = false; // note: waze runs as a separate app, so we just return directly from here startActivity(intent); return; } catch ( ActivityNotFoundException ex) { AndroidUtils.debug("Waze not found, using default app"); callGoogleMap(latI, lonI, (flags & SHOW_SATELLITE_PHOTOS) != 0); return; } } Intent intent = new Intent(this, Class.forName(totalcrossPKG+".RouteViewer")); intent.putExtra("latI",latI); intent.putExtra("lonI",lonI); intent.putExtra("latF",latF); intent.putExtra("lonF",lonF); intent.putExtra("coord",coord); intent.putExtra("sat",(flags & SHOW_SATELLITE_PHOTOS) != 0); startActivityForResult(intent, MAP_RETURN); } catch (Throwable e) { AndroidUtils.handleException(e,false); } } private void callGoogleMap(double lat, double lon, boolean sat) { try { Intent intent = new Intent(this, Class.forName(totalcrossPKG+".MapViewer")); intent.putExtra("lat",lat); intent.putExtra("lon",lon); intent.putExtra("sat",sat); startActivityForResult(intent, MAP_RETURN); } catch (Throwable e) { AndroidUtils.handleException(e,false); } } private void callGoogleMap(String items, boolean sat) { try { Intent intent = new Intent(this, Class.forName(totalcrossPKG+".MapViewer")); intent.putExtra("items",items); intent.putExtra("sat",sat); startActivityForResult(intent, MAP_RETURN); } catch (Throwable e) { AndroidUtils.handleException(e,false); } } private String imageFN; //private static final int CAMERA_CUSTOM = 0; private static final int CAMERA_NATIVE = 1; private static final int CAMERA_NATIVE_NOCOPY = 2; private int cameraType; private void captureCamera(String s, int quality, int width, int height, boolean allowRotation, int cameraType) { try { imageFN = s; this.cameraType = cameraType; String deviceId = Build.MANUFACTURER.replaceAll("\\P{ASCII}", " ") + " " + Build.MODEL.replaceAll("\\P{ASCII}", " "); if (cameraType == CAMERA_NATIVE || cameraType == CAMERA_NATIVE_NOCOPY) { ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.TITLE, "tctemp.jpg"); capturedImageURI = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, capturedImageURI); startActivityForResult(intent, EXTCAMERA_RETURN); } else if ("SK GT-7340".equals(deviceId)) { Uri outputFileUri = Uri.fromFile(new File(s)); Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, outputFileUri); startActivityForResult(intent, CAMERA_PIC_REQUEST); } else { Intent intent = new Intent(this, Class.forName(totalcrossPKG+".CameraViewer")); intent.putExtra("file",s); intent.putExtra("quality",quality); intent.putExtra("width",width); intent.putExtra("height",height); intent.putExtra("allowRotation", allowRotation); startActivityForResult(intent, TAKE_PHOTO); Launcher4A.instance.nativeInitSize(null,-998,0); } } catch (Throwable e) { AndroidUtils.handleException(e,false); } } private void dialNumber(String number) { startActivity(new Intent(Intent.ACTION_CALL, Uri.parse("tel:"+number))); } public static final int DIAL = 1; public static final int CAMERA = 2; public static final int TITLE = 3; public static final int EXEC = 4; public static final int LEVEL5 = 5; public static final int MAP = 6; public static final int FULLSCREEN = 7; public static final int ROUTE = 8; public static final int ZXING_SCAN = 9; public static final int MAPITEMS = 10; public static String tcz; private String totalcrossPKG = "totalcross.android"; public boolean isSingleApk() { return !AndroidUtils.pinfo.sharedUserId.equals("totalcross.app.sharedid"); } private void runVM() { if (runningVM) return; runningVM = true; Hashtable<String,String> ht = AndroidUtils.readVMParameters(); String tczname = tcz = ht.get("tczname"); boolean isSingleAPK = false; if (tczname == null) { // this is a single apk. get the app name from the package String sharedId = AndroidUtils.pinfo.sharedUserId; if (sharedId.equals("totalcross.app.sharedid")) // is it the default shared id? AndroidUtils.error("Launching parameters not found",true); else { tczname = sharedId.substring(sharedId.lastIndexOf('.')+1); totalcrossPKG = "totalcross."+tczname; ht.put("apppath", AndroidUtils.pinfo.applicationInfo.dataDir); isSingleAPK = true; } } String appPath = ht.get("apppath"); String fc = ht.get("fullscreen"); isFullScreen = fc != null && fc.equalsIgnoreCase("true"); setTitle(tczname); if (isFullScreen) getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // start the vm achandler = new EventHandler(); String cmdline = ht.get("cmdline"); setContentView(new Launcher4A(this, tczname, appPath, cmdline, isSingleAPK)); onMainLoop = true; } class EventHandler extends Handler { public void handleMessage(Message msg) { Bundle b = msg.getData(); switch (b.getInt("type")) { case LEVEL5: Level5.getInstance().processMessage(b); break; case DIAL: dialNumber(b.getString("dial.number")); break; case CAMERA: captureCamera(b.getString("showCamera.fileName"),b.getInt("showCamera.quality"),b.getInt("showCamera.width") ,b.getInt("showCamera.height"),b.getBoolean("showCamera.allowRotation"),b.getInt("showCamera.cameraType")); break; case TITLE: setTitle(b.getString("setDeviceTitle.title")); break; case EXEC: intentExec(b.getString("command"), b.getString("args"), b.getInt("launchCode"), b.getBoolean("wait")); break; case MAPITEMS: callGoogleMap(b.getString("items"), b.getBoolean("sat")); break; case MAP: callGoogleMap(b.getDouble("lat"), b.getDouble("lon"), b.getBoolean("sat")); break; case ROUTE: callRoute(b.getDouble("latI"), b.getDouble("lonI"),b.getDouble("latF"), b.getDouble("lonF"), b.getString("coord"), b.getInt("flags")); break; case FULLSCREEN: { boolean setAndHide = b.getBoolean("fullScreen"); InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); Window w = getWindow(); if (setAndHide) { try // for galaxy tab2 bar { java.lang.reflect.Method m = View.class.getMethod("setSystemUiVisibility", new Class[]{Integer.class}); final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; m.invoke(Launcher4A.instance, new Integer(SYSTEM_UI_FLAG_HIDE_NAVIGATION)); } catch (Exception e) {} imm.hideSoftInputFromWindow(Launcher4A.instance.getWindowToken(), 0, Launcher4A.instance.siprecv); w.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); w.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); } else { imm.showSoftInput(Launcher4A.instance, 0); w.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); w.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); } break; } case ZXING_SCAN: { String cmd = b.getString("zxing.mode"); StringTokenizer st = new StringTokenizer(cmd,"&"); String mode = "SCAN_MODE"; String scanmsg = ""; while (st.hasMoreTokens()) { String s = st.nextToken(); int i = s.indexOf('='); if (i == -1) continue; String s1 = s.substring(0,i); String s2 = s.substring(i+1); if (s1.equalsIgnoreCase("mode")) mode = s2; else if (s1.equalsIgnoreCase("msg")) scanmsg = s2; } mode = mode.equalsIgnoreCase("2D") ? "QR_CODE_MODE" : mode.equalsIgnoreCase("1D") ? "ONE_D_MODE" : "SCAN_MODE"; Intent intent = new Intent("totalcross.zxing.client.android.SCAN"); intent.putExtra("SCAN_MODE", mode);//for Qr code, its "QR_CODE_MODE" instead of "PRODUCT_MODE" intent.putExtra("SAVE_HISTORY", false);//this stops saving ur barcode in barcode scanner app's history intent.putExtra("SCAN_MESSAGE", scanmsg); startActivityForResult(intent, ZXING_RETURN); //if (harder) decodeHints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); //Result result = reader.decode(bitmap, decodeHints); break; } } } } Uri capturedImageURI; // Vm.exec("url","http://www.google.com/search?hl=en&source=hp&q=abraham+lincoln",0,false): launches a url // Vm.exec("totalcross.app.UIGadgets",null,0,false): launches another TotalCross' application // Vm.exec("viewer","file:///sdcard/G3Assets/541.jpg", 0, true); // Vm.exec("/sdcard/ private void intentExec(String command, String args, int launchCode, boolean wait) { try { if (command.equalsIgnoreCase("broadcast")) { Intent intent = new Intent(); if (launchCode != 0) intent.addFlags(launchCode); intent.setAction(args); sendBroadcast(intent); } else if (command.equalsIgnoreCase("cmd")) { java.lang.Process process = Runtime.getRuntime().exec(args); if (wait) process.waitFor(); } else if (command.equalsIgnoreCase("kingsoft")) { File f = new File(args); if (f.exists()) { Intent intent = new Intent(); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setAction(android.content.Intent.ACTION_VIEW); intent.setClassName("cn.wps.moffice_eng", "cn.wps.moffice.documentmanager.PreStartActivity"); Uri uri = Uri.fromFile(f); intent.setData(uri); startActivity(intent); } } else if (command.equalsIgnoreCase("viewer")) { String argl = args.toLowerCase(); if (android.os.Build.VERSION.SDK_INT >= 8 && AndroidUtils.isImage(argl)) { Intent intent = new Intent(this, Class.forName(totalcrossPKG+".TouchImageViewer")); intent.putExtra("file",args); if (!wait) startActivityForResult(intent, JUST_QUIT); else startActivity(intent); return; } if (argl.endsWith(".pdf")) { File pdfFile = new File(args); if(pdfFile.exists()) { Uri path = Uri.fromFile(pdfFile); Intent pdfIntent = new Intent(Intent.ACTION_VIEW); pdfIntent.setDataAndType(path, "application/pdf"); pdfIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); try { startActivity(pdfIntent); } catch (ActivityNotFoundException e) { AndroidUtils.debug("THERE'S NO PDF READER TO OPEN "+args); e.printStackTrace(); } } } else { Intent intent; if (argl.indexOf("youtu.be") >= 0 || argl.indexOf("youtube") >= 0) intent = new Intent(Intent.ACTION_VIEW, Uri.parse(args)); else { intent = new Intent(this, Class.forName(totalcrossPKG+".WebViewer")); intent.putExtra("url",args); } if (!wait) startActivityForResult(intent, JUST_QUIT); else startActivity(intent); return; } } else if (command.equalsIgnoreCase("url")) { if (args != null) { Intent i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse(args)); startActivity(i); } } else if (command.toLowerCase().endsWith(".apk")) { Intent i = new Intent(Intent.ACTION_VIEW); i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); i.setDataAndType(Uri.fromFile(new File(command)), "application/vnd.android.package-archive"); startActivity(i); } else { Intent i = new Intent(); i.setClassName(command,command+"."+args); boolean isService = args.equalsIgnoreCase("TCService"); AndroidUtils.debug("*** Vm.exec "+command+" . "+args+": "+isService); if (isService) startService(i); else startActivity(i); } } catch (Throwable e) { AndroidUtils.handleException(e,false); } if (!wait) finish(); } public void onConfigurationChanged(Configuration config) { super.onConfigurationChanged(config); Launcher4A.hardwareKeyboardIsVisible = config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO || config.keyboard == Configuration.KEYBOARD_QWERTY; // motorola titanium returns HARDKEYBOARDHIDDEN_YES but KEYBOARD_QWERTY. In soft inputs, it returns KEYBOARD_NOKEYS } protected void onSaveInstanceState(Bundle outState) { outState.clear(); } protected void onDestroy() { if (runningVM) // guich@tc126_60: call app 1, home, call app 2: onDestroy is called quitVM(); super.onDestroy(); } protected void onPause() { if (runningVM) Launcher4A.sendCloseSIPEvent(); Launcher4A.appPaused = true; if (onMainLoop) Launcher4A.appPaused(); super.onPause(); if (isFinishing() && runningVM) // guich@tc126_60: stop the vm if finishing is true, since onDestroy is not guaranteed to be called quitVM(); // call app 1, exit, call app 2: onPause is called but onDestroy not } private void quitVM() { runningVM = onMainLoop = false; Launcher4A.stopVM(); while (!Launcher4A.canQuit) try {Thread.sleep(100);} catch (Exception e) {} Launcher4A.closeTCZs(); //Level5.getInstance().destroy(); android.os.Process.killProcess(android.os.Process.myPid()); // with these two lines, the application may have problems when then stub tries to load another vm instance. //try {Thread.sleep(1000);} catch (Exception e) {} // let the app take time to exit // System.exit(0); // make sure all threads will stop. also ensures that one app is not called as the app launched previously } protected void onResume() { if (onMainLoop) Launcher4A.appResumed(); Launcher4A.appPaused = false; super.onResume(); } public String strBarcodeData; public static Semaphore semaphore = new Semaphore(1); public void barcodeRead(BarcodeReadEvent aBarcodeReadEvent) { try { semaphore.acquire(); } catch (InterruptedException exception) {} strBarcodeData = aBarcodeReadEvent.getBarcodeData(); semaphore.release(); Launcher4A.instance._postEvent(Launcher4A.BARCODE_READ, 0, 0, 0, 0, 0); } public void onNewIntent(Intent i) { AndroidUtils.debug("on new intent "+i); super.onNewIntent(i); } }