content: package cordova.plugin.alan;
  
  import android.Manifest;
  import android.annotation.TargetApi;
  import android.app.Activity;
  import android.util.Log;
  import android.view.Gravity;
  import android.view.View;
  import android.widget.FrameLayout;
  
  import com.alan.alansdk.Alan;
  import com.alan.alansdk.AlanState;
  import com.alan.alansdk.AlanCallback;
  import com.alan.alansdk.AlanConfig;
  import com.alan.alansdk.alanbase.ConnectionState;
  import com.alan.alansdk.button.AlanButton;
  import com.alan.alansdk.events.EventCommand;
  
  import org.apache.cordova.CallbackContext;
  import org.apache.cordova.CordovaInterface;
  import org.apache.cordova.CordovaPlugin;
  import org.apache.cordova.CordovaWebView;
  import org.apache.cordova.PermissionHelper;
  import org.apache.cordova.PluginResult;
  import org.json.JSONArray;
  import org.json.JSONException;
  import org.json.JSONObject;
  
  public class AlanVoice extends CordovaPlugin {
  
      private AlanButton alanButton;
      private CallbackContext callbackContext;
  
      private final static int REQUEST_PERMISSIONS = 1001;
  
      @Override
      public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
          if (this.callbackContext == null) {
              this.callbackContext = callbackContext;
          }
  
          switch (action) {
              case "addButton":
                  addButton(args);
                  return true;
              case "removeButton":
                  this.callbackContext = null;
                  removeButton();
                  return true;
              case "showButton":
                  showAlanButton();
                  return true;
              case "hideButton":
                  hideAlanButton();
                  return true;
              case "callProjectApi":
                  callProjectApi(args);
                  return true;
              case "setVisualState":
                  setVisualState(args);
                  return true;
              case "playText":
                  playText(args);
                  return true;
              case "playCommand":
                  playData(args);
                  return true;
              case "activate":
                  turnOn(callbackContext);
                  return true;
              case "deactivate":
                  turnOff();
                  return true;
              case "isActive":
                  isActive();
                  return true;
  
          }
          return false;
      }
  
  
      /**
       * Checks for needed permissions for API =< 23.
       */
      @TargetApi(23)
      private boolean checkPermissions() {
          if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
              boolean recordAudioPermission = PermissionHelper.hasPermission(this, Manifest.permission.RECORD_AUDIO);
  
              if (!recordAudioPermission) {
                  PermissionHelper.requestPermissions(this, REQUEST_PERMISSIONS, new String[]{Manifest.permission.RECORD_AUDIO});
                  return false;
              } else {
                  return true;
              }
          } else {
              // Call Smart Capture if version is less that API 23.
              return true;
          }
      }
  
      private void callProjectApi(JSONArray args) {
          if (alanButton == null) {
              return;
          }
          if (args.length() < 2) {
              return;
          }
  
          String command = null;
          JSONObject jsonArgs = null;
          try {
              jsonArgs = args.getJSONObject(1);
              command = args.getString(0);
          } catch (JSONException e) {
              Log.e("AlanResponse", e.getMessage());
              return;
          }
  
          alanButton.callProjectApi(command, jsonArgs.toString(), (methodName, response, error) -> {
              if (error != null && !error.isEmpty()) {
                  Log.i("AlanResponse", methodName + " failed with: " + error);
                  sendPluginResult(new PluginResult(PluginResult.Status.ERROR, error));
              } else {
                  Log.i("AlanResponse", methodName + " response is: " + response);
                  sendPluginResult(new PluginResult(PluginResult.Status.OK, response));
              }
          });
      }
  
      private void setVisualState(JSONArray args) {
          if (alanButton == null) {
              return;
          }
          if (args.length() < 1) {
              return;
          }
  
          JSONObject jsonArgs = null;
          try {
              jsonArgs = args.getJSONObject(0);
          } catch (JSONException e) {
              Log.e("AlanResponse", e.getMessage());
              return;
          }
  
          alanButton.setVisualState(jsonArgs.toString());
          sendPluginResult(new PluginResult(PluginResult.Status.OK));
      }
  
      private void playText(JSONArray args) {
          if (alanButton == null) {
              return;
          }
          if (args.length() < 1) {
              return;
          }
  
          String text = null;
          try {
              text = args.getString(0);
          } catch (JSONException e) {
              Log.e("AlanResponse", e.getMessage());
              return;
          }
  
          alanButton.playText(text, (methodName, response, error) -> {
              if (error != null && !error.isEmpty()) {
                  Log.i("AlanResponse", methodName + " failed with: " + error);
                  sendPluginResult(new PluginResult(PluginResult.Status.ERROR, error));
              } else {
                  Log.i("AlanResponse", methodName + " response is: " + response);
                  sendPluginResult(new PluginResult(PluginResult.Status.OK, response));
              }
          });
      }
  
      private void playData(JSONArray args) {
          if (alanButton == null) {
              return;
          }
          if (args.length() < 1) {
              return;
          }
  
  
          JSONObject data = null;
          try {
              data = args.getJSONObject(0);
          } catch (JSONException e) {
              Log.e("AlanResponse", e.getMessage());
              return;
          }
  
          alanButton.playCommand(data.toString(), (methodName, response, error) -> {
              if (error != null && !error.isEmpty()) {
                  Log.i("AlanResponse", methodName + " failed with: " + error);
                  sendPluginResult(new PluginResult(PluginResult.Status.ERROR, error));
              } else {
                  Log.i("AlanResponse", methodName + " response is: " + response);
                  sendPluginResult(new PluginResult(PluginResult.Status.OK, response));
              }
          });
      }
  
      private void turnOn(CallbackContext turnOnCallback) {
          if (alanButton == null) {
              return;
          }
          cordova.getThreadPool().submit(
                  () -> {
                      alanButton.activate();
                      try {
                          Thread.sleep(300);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      sendPluginResult(turnOnCallback, new PluginResult(PluginResult.Status.OK));
                  }
          );
      }
  
      private void turnOff() {
          if (alanButton == null) {
              return;
          }
  
  
          alanButton.deactivate();
          sendPluginResult(new PluginResult(PluginResult.Status.OK));
      }
  
      private void isActive() {
          if (alanButton == null) {
              return;
          }
  
          sendPluginResult(new PluginResult(PluginResult.Status.OK, alanButton.isActive()));
      }
  
      private void addButton(JSONArray args) {
          Alan.PLATFORM_SUFFIX = "ionic";
          Alan.PLATFORM_VERSION_SUFFIX = Version.NUMBER;
  
          if (alanButton == null) {
              createButton();
          }
  
          String projectId = "";
          int left = -1;
          int right = -1;
          int bottom = -1;
          int zindex = -1;
          int top = -1;
  
          try {
              projectId = args.getString(0);
  
              if (args.length() > 1) {
                  left = extractValue(args.getString(1), false);
              }
  
              if (args.length() > 2) {
                  right = extractValue(args.getString(2), false);
              }
  
              if (args.length() > 3) {
                  bottom = extractValue(args.getString(3), true);
              }
  
              if (args.length() > 4) {
                  zindex = extractValue(args.getString(4), true);
              }
  
              if (args.length() > 5) {
                  top = extractValue(args.getString(5), true);
              }
  
          } catch (JSONException e) {
              Log.e("AlanResponse", e.getMessage());
              return;
          }
  
          AlanConfig config = AlanConfig.builder()
                  .setProjectId(projectId)
                  .build();
          alanButton.initWithConfig(config);
          alanButton.getSDK().registerCallback(new IonicListener());
  
          if (zindex != -1) {
              alanButton.setElevation(zindex);
          }
  
          cordova.getActivity().runOnUiThread(new InitRunnable(
              this.cordova.getActivity().getResources().getDisplayMetrics().widthPixels,
                  left,
                  right,
                  bottom,
                  top
          ));
  
      }
  
      private class InitRunnable implements Runnable {
  
  
          private int screenWidth;
          private int left;
          private int right;
          private int bottom;
          private int top;
  
          public InitRunnable(int screenWidth,
                              int left,
                              int right,
                              int bottom,
                              int top) {
              this.screenWidth = screenWidth;
              this.left = left;
              this.right = right;
              this.bottom = bottom;
              this.top = top;
          }
  
          @Override
          public void run() {
  
              if (left != -1) {
  
                  alanButton.stopStick = true;
                  alanButton.button.setX(left);
              }
  
              if (right != -1) {
  
                  int buttonWidth = cordova.getContext().getResources().getDimensionPixelOffset(com.alan.alansdk.R.dimen.alan_button_size);
                  alanButton.stopStick = true;
                  alanButton.button.setX(screenWidth - right - buttonWidth);
              }
  
              if (left == -1 && right == -1) {
                  alanButton.setButtonAlign(AlanButton.BUTTON_RIGHT);
              }
  
              if (bottom != -1) {
                  alanButton.setTranslationY(-bottom);
              }
  
              if (top != -1) {
                  alanButton.setY(top);
              }
  
              alanButton.showButton();
              alanButton.invalidate();
              alanButton.requestLayout();
          }
      }
  
      private int extractValue(String arg, boolean height) {
          if (arg == null || arg.isEmpty() || arg.equals("null")) {
              return -1;
          }
  
          int result;
          if (arg.contains("px")) {
              result = Integer.valueOf(arg.replaceAll("[^\\d.]", ""));
          } else if (arg.contains("%")) {
              int screenSize;
              if (height) {
                  screenSize = this.cordova.getActivity().getResources().getDisplayMetrics().heightPixels;
              } else {
                  screenSize = this.cordova.getActivity().getResources().getDisplayMetrics().widthPixels;
              }
              double q = Double.valueOf(arg.replaceAll("[^\\d.]", "")) / 100;
              result = (int) Math.round(Math.floor(q * screenSize));
          } else {
              result = Integer.valueOf(arg.replaceAll("[^\\d.]", ""));
          }
          return result;
      }
  
      private AlanButton createButton() {
          Activity cordovaActivity = this.cordova.getActivity();
  
          FrameLayout rootView = cordovaActivity.findViewById(android.R.id.content);
  
          alanButton = new AlanButton(cordovaActivity, null);
          FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                  FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
          params.gravity = Gravity.BOTTOM | Gravity.END;
          alanButton.setLayoutParams(params);
          alanButton.setVisibility(View.GONE);
          alanButton.setHintPanelVisibility(true);
  
          cordova.getActivity().runOnUiThread(() -> {
              rootView.addView(alanButton);
          });
  
          return alanButton;
      }
  
      private boolean removeButton() {
          Activity cordovaActivity = this.cordova.getActivity();
  
          FrameLayout rootView = cordovaActivity.findViewById(android.R.id.content);
  
          alanButton.getSDK().clearCallbacks();
          alanButton.getSDK().stop();
          cordova.getActivity().runOnUiThread(() -> {
              alanButton.disableButton();
              rootView.removeView(alanButton);
              alanButton = null;
          });
  
          return true;
      }
  
      private void showAlanButton() {
          if (alanButton != null) {
              cordova.getActivity().runOnUiThread(() -> alanButton.showButton());
          }
      }
  
      private void hideAlanButton() {
          if (alanButton != null) {
              cordova.getActivity().runOnUiThread(() -> alanButton.hideButton());
          }
      }
  
      @Override
      public void initialize(CordovaInterface cordova, CordovaWebView webView) {
          super.initialize(cordova, webView);
  
          createButton();
      }
  
      class IonicListener extends AlanCallback {
  
          @Override
          public void onCommandReceived(EventCommand eventCommand) {
              super.onCommandReceived(eventCommand);
              AlanCallbackWrapper<JSONObject> command = null;
              if (eventCommand.getData().has("data")) {
                  try {
                      command = new AlanCallbackWrapper<>(AlanCallbackWrapper.TYPE_COMMAND,
                              eventCommand.getData().getJSONObject("data"));
                  } catch (JSONException e) {
                      Log.e("AlanVoice", e.getMessage());
                  }
              } else {
                  command = new AlanCallbackWrapper<>(AlanCallbackWrapper.TYPE_COMMAND,
                          eventCommand.getData());
              }
              if (command != null) {
                  sendPluginResult(new PluginResult(PluginResult.Status.OK, command.toJSON()));
              }
          }
  
          @Override
          public void onAlanStateChanged(AlanState alanState) {
              super.onAlanStateChanged(alanState);
              switch (alanState) {
                  case ONLINE:
                      sendConnectionStateCallback("connected");
                      break;
                  case OFFLINE:
                  case UNKNOWN:
                          sendConnectionStateCallback("disconnected");
                          break;
                      default:
                          ;
              }
  
          }
      }
  
      private void sendConnectionStateCallback(String state) {
          AlanCallbackWrapper<String> connectionStateArg =
                  new AlanCallbackWrapper<>(AlanCallbackWrapper.TYPE_CONNECTION_STATE, state);
          sendPluginResult(new PluginResult(PluginResult.Status.OK, connectionStateArg.toJSON()));
      }
  
      private void sendPluginResult(PluginResult result) {
          sendPluginResult(callbackContext, result);
      }
  
      private void sendPluginResult(CallbackContext callback, PluginResult result) {
          if (callback != null && !callback.isFinished()) {
              result.setKeepCallback(true);
              callback.sendPluginResult(result);
          }
      }
  }
  