@Kroll.method
  public void takeScreenshot(KrollFunction callback) {
    Activity a = TiApplication.getAppCurrentActivity();

    if (a == null) {
      Log.w(TAG, "Could not get current activity for takeScreenshot.", Log.DEBUG_MODE);
      callback.callAsync(getKrollObject(), new Object[] {null});
      return;
    }

    while (a.getParent() != null) {
      a = a.getParent();
    }

    Window w = a.getWindow();

    while (w.getContainer() != null) {
      w = w.getContainer();
    }

    KrollDict image = TiUIHelper.viewToImage(null, w.getDecorView());
    if (callback != null) {
      callback.callAsync(getKrollObject(), new Object[] {image});
    }
  }
 @Override
 public void onError(Activity activity, int requestCode, Exception e) {
   if (imageFile != null) {
     imageFile.delete();
   }
   String msg = "Camera problem: " + e.getMessage();
   Log.e(TAG, msg, e);
   if (errorCallback != null) {
     errorCallback.callAsync(getKrollObject(), createErrorResponse(UNKNOWN_ERROR, msg));
   }
 }
 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
   switch (item.getItemId()) {
     case android.R.id.home:
       if (activityProxy != null) {
         ActionBarProxy actionBarProxy = activityProxy.getActionBar();
         if (actionBarProxy != null) {
           KrollFunction onHomeIconItemSelected =
               (KrollFunction) actionBarProxy.getProperty(TiC.PROPERTY_ON_HOME_ICON_ITEM_SELECTED);
           KrollDict event = new KrollDict();
           event.put(TiC.EVENT_PROPERTY_SOURCE, actionBarProxy);
           if (onHomeIconItemSelected != null) {
             onHomeIconItemSelected.call(activityProxy.getKrollObject(), new Object[] {event});
           }
         }
       }
       return true;
     default:
       return menuHelper.onOptionsItemSelected(item);
   }
 }
  private void doInvokeCallback(
      TiBaseActivity callbackActivity,
      KrollFunction callback,
      KrollObject krollObject,
      KrollDict callbackArgs) {
    if (callbackActivity.isResumed) {
      callback.callAsync(krollObject, callbackArgs);

    } else {
      CallbackWrapper callbackWrapper =
          new CallbackWrapper(callbackActivity, callback, krollObject, callbackArgs);
      Message message = getRuntimeHandler().obtainMessage(MSG_INVOKE_CALLBACK, callbackWrapper);
      message.sendToTarget();
    }
  }
  @Kroll.method
  public void previewImage(KrollDict options) {
    Activity activity = TiApplication.getAppCurrentActivity();
    if (activity == null) {
      Log.w(TAG, "Unable to get current activity for previewImage.", Log.DEBUG_MODE);
      return;
    }

    KrollFunction successCallback = null;
    KrollFunction errorCallback = null;
    TiBlob image = null;

    if (options.containsKey("success")) {
      successCallback = (KrollFunction) options.get("success");
    }
    if (options.containsKey("error")) {
      errorCallback = (KrollFunction) options.get("error");
    }
    if (options.containsKey("image")) {
      image = (TiBlob) options.get("image");
    }

    if (image == null) {
      if (errorCallback != null) {
        errorCallback.callAsync(
            getKrollObject(), createErrorResponse(UNKNOWN_ERROR, "Missing image property"));
      }
    }

    TiBaseFile f = (TiBaseFile) image.getData();

    final KrollFunction fSuccessCallback = successCallback;
    final KrollFunction fErrorCallback = errorCallback;

    Log.d(TAG, "openPhotoGallery called", Log.DEBUG_MODE);

    TiActivitySupport activitySupport = (TiActivitySupport) activity;

    Intent intent = new Intent(Intent.ACTION_VIEW);
    TiIntentWrapper previewIntent = new TiIntentWrapper(intent);
    String mimeType = image.getMimeType();

    if (mimeType != null && mimeType.length() > 0) {
      intent.setDataAndType(Uri.parse(f.nativePath()), mimeType);
    } else {
      intent.setData(Uri.parse(f.nativePath()));
    }

    previewIntent.setWindowId(TiIntentWrapper.createActivityName("PREVIEW"));

    final int code = activitySupport.getUniqueResultCode();
    activitySupport.launchActivityForResult(
        intent,
        code,
        new TiActivityResultHandler() {

          public void onResult(Activity activity, int requestCode, int resultCode, Intent data) {
            Log.e(TAG, "OnResult called: " + resultCode);
            if (fSuccessCallback != null) {
              KrollDict response = new KrollDict();
              response.putCodeAndMessage(NO_ERROR, null);
              fSuccessCallback.callAsync(getKrollObject(), response);
            }
          }

          public void onError(Activity activity, int requestCode, Exception e) {
            String msg = "Gallery problem: " + e.getMessage();
            Log.e(TAG, msg, e);
            if (fErrorCallback != null) {
              fErrorCallback.callAsync(getKrollObject(), createErrorResponse(UNKNOWN_ERROR, msg));
            }
          }
        });
  }
    @Override
    public void onResult(Activity activity, int requestCode, int resultCode, Intent data) {
      if (resultCode == Activity.RESULT_CANCELED) {
        if (imageFile != null) {
          imageFile.delete();
        }
        if (cancelCallback != null) {
          KrollDict response = new KrollDict();
          response.putCodeAndMessage(NO_ERROR, null);
          cancelCallback.callAsync(getKrollObject(), response);
        }

      } else {
        // If data is null, the image is not automatically saved to the gallery, so we need to
        // process it with
        // the one we created
        if (data == null) {
          processImage(activity);

        } else {
          // Get the content information about the saved image
          String[] projection = {
            Images.Media.TITLE,
            Images.Media.DISPLAY_NAME,
            Images.Media.MIME_TYPE,
            Images.ImageColumns.BUCKET_ID,
            Images.ImageColumns.BUCKET_DISPLAY_NAME,
            MediaColumns.DATA,
            Images.ImageColumns.DATE_TAKEN
          };

          String title = null;
          String displayName = null;
          String mimeType = null;
          String bucketId = null;
          String bucketDisplayName = null;
          String dataPath = null;
          String dateTaken = null;

          Cursor c = null;
          boolean isDataValid = true;
          boolean isQueriedPhotoValid = true;
          Uri uriData = data.getData();
          if (uriData != null) {
            c = activity.getContentResolver().query(uriData, projection, null, null, null);
          }
          if (c == null) {
            c =
                activity
                    .getContentResolver()
                    .query(
                        Images.Media.EXTERNAL_CONTENT_URI,
                        projection,
                        null,
                        null,
                        Images.ImageColumns.DATE_TAKEN);
            isDataValid = false;
          }
          if (c != null) {
            try {
              boolean isCursorValid = false;
              if (uriData != null && isDataValid) {
                isCursorValid = c.moveToNext();
              } else {
                isCursorValid = c.moveToLast();
              }
              if (isCursorValid) {
                title = c.getString(0);
                displayName = c.getString(1);
                mimeType = c.getString(2);
                bucketId = c.getString(3);
                bucketDisplayName = c.getString(4);
                dataPath = c.getString(5);
                dateTaken = c.getString(6);

                if (!isDataValid && dateTaken.equals(dateTaken_lastImageInExternalContentURI)) {
                  isQueriedPhotoValid = false;
                }

                Log.d(
                    TAG,
                    "Image { title: "
                        + title
                        + " displayName: "
                        + displayName
                        + " mimeType: "
                        + mimeType
                        + " bucketId: "
                        + bucketId
                        + " bucketDisplayName: "
                        + bucketDisplayName
                        + " path: "
                        + dataPath
                        + " }",
                    Log.DEBUG_MODE);
              }
            } finally {
              c.close();
              c = null;
            }
          } else {
            // If we can't get query the image, process it from the imageFile
            processImage(activity);
            return;
          }
          if (!isQueriedPhotoValid) {
            // If the queried image is not the one we just captured, process it from the imageFile
            processImage(activity);
            return;
          }

          String localImageUrl = dataPath;
          URL url;
          try {
            if (!saveToPhotoGallery) {
              // We need to move the image from dataPath to the temp file which will be deleted
              // when the app exits.
              url = new URL(imageUrl);
              moveImage(dataPath, url.getPath());

              // Delete the saved the image entry from the gallery DB.
              if (uriData != null && isDataValid) {
                activity.getContentResolver().delete(uriData, null, null);
              } else {
                activity
                    .getContentResolver()
                    .delete(
                        Images.Media.EXTERNAL_CONTENT_URI,
                        "datetaken = ?",
                        new String[] {dateTaken});
              }

              localImageUrl = imageUrl; // make sure it's a good URL before setting it to pass back.
            } else if (imageUrl != null) {
              // Delete the temp file since we want to use the one from the photo gallery
              url = new URL(imageUrl);
              File source = new File(url.getPath());
              source.delete();
            }
          } catch (MalformedURLException e) {
            Log.e(TAG, "Invalid URL not moving image: " + e.getMessage());
          }
          invokeSuccessCallback(activity, localImageUrl);
        }
      }
    }
  @Kroll.method
  public void showCamera(@SuppressWarnings("rawtypes") HashMap options) {
    Activity activity = TiApplication.getInstance().getCurrentActivity();

    Log.d(TAG, "showCamera called", Log.DEBUG_MODE);

    KrollFunction successCallback = null;
    KrollFunction cancelCallback = null;
    KrollFunction errorCallback = null;
    boolean autohide = true;
    boolean saveToPhotoGallery = false;

    if (options.containsKey("success")) {
      successCallback = (KrollFunction) options.get("success");
    }
    if (options.containsKey("cancel")) {
      cancelCallback = (KrollFunction) options.get("cancel");
    }
    if (options.containsKey("error")) {
      errorCallback = (KrollFunction) options.get("error");
    }

    Object autohideOption = options.get("autohide");
    if (autohideOption != null) {
      autohide = TiConvert.toBoolean(autohideOption);
    }

    Object saveToPhotoGalleryOption = options.get("saveToPhotoGallery");
    if (saveToPhotoGalleryOption != null) {
      saveToPhotoGallery = TiConvert.toBoolean(saveToPhotoGalleryOption);
    }

    // Use our own custom camera activity when an overlay is provided.
    if (options.containsKey("overlay")) {
      TiCameraActivity.overlayProxy = (TiViewProxy) options.get("overlay");

      TiCameraActivity.callbackContext = getKrollObject();
      TiCameraActivity.successCallback = successCallback;
      TiCameraActivity.errorCallback = errorCallback;
      TiCameraActivity.cancelCallback = cancelCallback;
      TiCameraActivity.saveToPhotoGallery = saveToPhotoGallery;
      TiCameraActivity.whichCamera = CAMERA_REAR; // default.

      // This option is only applicable when running the custom
      // TiCameraActivity, since we can't direct the built-in
      // Activity to open a specific camera.
      Object whichCamera = options.get("whichCamera");
      if (whichCamera != null) {
        TiCameraActivity.whichCamera = TiConvert.toInt(whichCamera);
      }
      TiCameraActivity.autohide = autohide;

      Intent intent = new Intent(activity, TiCameraActivity.class);
      activity.startActivity(intent);
      return;
    }

    Camera camera = null;
    try {
      camera = Camera.open();
      if (camera != null) {
        camera.release();
        camera = null;
      }

    } catch (Throwable t) {
      if (camera != null) {
        camera.release();
      }

      if (errorCallback != null) {
        errorCallback.call(
            getKrollObject(),
            new Object[] {createErrorResponse(NO_CAMERA, "Camera not available.")});
      }

      return;
    }

    TiActivitySupport activitySupport = (TiActivitySupport) activity;
    TiFileHelper tfh = TiFileHelper.getInstance();

    TiIntentWrapper cameraIntent = new TiIntentWrapper(new Intent());
    if (TiCameraActivity.overlayProxy == null) {
      cameraIntent.getIntent().setAction(MediaStore.ACTION_IMAGE_CAPTURE);
      cameraIntent.getIntent().addCategory(Intent.CATEGORY_DEFAULT);
    } else {
      cameraIntent
          .getIntent()
          .setClass(TiApplication.getInstance().getBaseContext(), TiCameraActivity.class);
    }

    cameraIntent.setWindowId(TiIntentWrapper.createActivityName("CAMERA"));
    PackageManager pm = (PackageManager) activity.getPackageManager();
    List<ResolveInfo> activities =
        pm.queryIntentActivities(cameraIntent.getIntent(), PackageManager.MATCH_DEFAULT_ONLY);

    // See if it's the HTC camera app
    boolean isHTCCameraApp = false;

    for (ResolveInfo rs : activities) {
      try {
        if (rs.activityInfo.applicationInfo.sourceDir.contains("HTC")
            || Build.MANUFACTURER.equals("HTC")) {
          isHTCCameraApp = true;
          break;
        }
      } catch (NullPointerException e) {
        // Ignore
      }
    }

    File imageDir = null;
    File imageFile = null;

    try {
      if (saveToPhotoGallery) {
        // HTC camera application will create its own gallery image
        // file.
        if (!isHTCCameraApp) {
          imageFile = createGalleryImageFile();
        }

      } else {
        if (activity.getIntent() != null) {
          String name = TiApplication.getInstance().getAppInfo().getName();
          // For HTC cameras, specifying the directory from
          // getExternalStorageDirectory is /mnt/sdcard and
          // using that path prevents the gallery from recognizing it.
          // To avoid this we use /sdcard instead
          // (this is a legacy path we've been using)
          if (isHTCCameraApp) {
            imageDir = new File(PHOTO_DCIM_CAMERA, name);
          } else {
            File rootsd = Environment.getExternalStorageDirectory();
            imageDir = new File(rootsd.getAbsolutePath() + "/dcim/Camera/", name);
          }
          if (!imageDir.exists()) {
            imageDir.mkdirs();
            if (!imageDir.exists()) {
              Log.w(TAG, "Attempt to create '" + imageDir.getAbsolutePath() + "' failed silently.");
            }
          }

        } else {
          imageDir = tfh.getDataDirectory(false);
        }

        imageFile = tfh.getTempFile(imageDir, ".jpg", true);
      }

    } catch (IOException e) {
      Log.e(TAG, "Unable to create temp file", e);
      if (errorCallback != null) {
        errorCallback.callAsync(
            getKrollObject(), createErrorResponse(UNKNOWN_ERROR, e.getMessage()));
      }

      return;
    }

    // Get the taken date for the last image in EXTERNAL_CONTENT_URI.
    String[] projection = {Images.ImageColumns.DATE_TAKEN};
    String dateTaken = null;
    Cursor c =
        activity
            .getContentResolver()
            .query(
                Images.Media.EXTERNAL_CONTENT_URI,
                projection,
                null,
                null,
                Images.ImageColumns.DATE_TAKEN);
    if (c != null) {
      if (c.moveToLast()) {
        dateTaken = c.getString(0);
      }
      c.close();
      c = null;
    }

    CameraResultHandler resultHandler = new CameraResultHandler();
    resultHandler.imageFile = imageFile;
    resultHandler.saveToPhotoGallery = saveToPhotoGallery;
    resultHandler.successCallback = successCallback;
    resultHandler.cancelCallback = cancelCallback;
    resultHandler.errorCallback = errorCallback;
    resultHandler.activitySupport = activitySupport;
    resultHandler.cameraIntent = cameraIntent.getIntent();
    resultHandler.dateTaken_lastImageInExternalContentURI = dateTaken;

    if (imageFile != null) {
      String imageUrl = "file://" + imageFile.getAbsolutePath();
      cameraIntent.getIntent().putExtra(MediaStore.EXTRA_OUTPUT, Uri.parse(imageUrl));
      resultHandler.imageUrl = imageUrl;
    }

    activity.runOnUiThread(resultHandler);
  }
  @SuppressWarnings({"unchecked", "rawtypes"})
  @Kroll.method
  public void voiceRecognition(KrollDict options) {

    Log.d(TAG, "Voice Recognition entry point");
    Activity currentActivity = getActivity();
    Intent intent =
        new Intent(currentActivity.getApplicationContext(), VoiceRecognitionActivity.class);
    intent.setAction(Intent.ACTION_VIEW);

    //
    //  You can override callback functions.
    //
    if (callback == null
        && options != null
        && options.containsKey("callback")
        && (options.get("callback") instanceof KrollFunction)) {
      Log.d(TAG, "overriding callback value");
      callback = (KrollFunction) options.get("callback");
    }

    //
    //  activity result handling
    //
    Handler handler =
        new Handler() {
          public void handleMessage(Message msg) {
            Log.d(TAG, "Voice Recognition result handled.");
            if (callback != null) {
              Bundle bundle = msg.getData();
              ArrayList<String> matches = bundle.getStringArrayList("VOICE_RESULT");
              for (int i = 0; i < matches.size(); i++) {
                Log.d(TAG, " Recognize result -> " + matches.get(i));
              }
              HashMap resultmap = new HashMap();
              String[] result_string_array = matches.toArray(new String[0]);
              resultmap.put("voice_results", result_string_array);
              resultmap.put("voice_enabled", bundle.getBoolean("VOICE_RECOGNITION_ENABLED"));
              resultmap.put("voice_canceled", bundle.getBoolean("VOICE_RECOGNITION_CANCELED"));
              callback.callAsync(getKrollObject(), resultmap);
            }
          }
        };

    //
    //    invoke main Activity.
    //
    Messenger msger = new Messenger(handler);
    intent.putExtra("VOICE_RESULT_MESSENGER", msger);
    if (options != null) {
      if (options.containsKey(RecognizerIntent.EXTRA_LANGUAGE_MODEL)) {
        String extraVal = (String) options.get(RecognizerIntent.EXTRA_LANGUAGE_MODEL);
        Log.d(TAG, "overriding RecognizerIntent.EXTRA_LANGUAGE_MODEL value -> " + extraVal);
        intent.putExtra("android.speech.extra.LANGUAGE_MODEL", extraVal);
      }
      if (options.containsKey(RecognizerIntent.EXTRA_PROMPT)) {
        String extraVal = (String) options.get(RecognizerIntent.EXTRA_PROMPT);
        Log.d(TAG, "overriding RecognizerIntent.EXTRA_PROMPT value");
        intent.putExtra(RecognizerIntent.EXTRA_PROMPT, extraVal);
      }
    }
    try {
      currentActivity.startActivity(intent);
    } catch (ActivityNotFoundException e) {
      Log.e(TAG, "VoiceRecognitionActivity not found, check your activity setting in tiapp.xml");
      HashMap resultmap = new HashMap();
      resultmap.put("voice_results", new ArrayList<String>());
      resultmap.put("voice_enabled", false);
      resultmap.put("voice_canceled", false);
      callback.callAsync(getKrollObject(), resultmap);
    }
  }