content: /*
   * Copyright (C) 2014 AChep@xda <artemchep@gmail.com>
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version 2
   * of the License, or (at your option) any later version.
   *
   * This program 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.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   * MA  02110-1301, USA.
   */
  package com.achep.base.ui.fragments.dialogs;
  
  import android.annotation.SuppressLint;
  import android.app.Activity;
  import android.app.Dialog;
  import android.content.Context;
  import android.content.Intent;
  import android.content.pm.PackageInfo;
  import android.content.pm.PackageManager;
  import android.net.Uri;
  import android.os.Bundle;
  import androidx.annotation.NonNull;
  import androidx.annotation.Nullable;
  import androidx.fragment.app.DialogFragment;
  import androidx.appcompat.app.AppCompatActivity;
  import android.view.View;
  import android.view.ViewGroup;
  import android.view.ViewStub;
  import android.widget.AdapterView;
  import android.widget.CheckBox;
  import android.widget.EditText;
  import android.widget.Spinner;
  
  import com.achep.acdisplay.Config;
  import com.achep.acdisplay.R;
  import com.achep.acdisplay.providers.LogAttachmentProvider;
  import com.achep.acdisplay.ui.DialogHelper;
  import com.achep.base.Build;
  import com.achep.base.Device;
  import com.achep.base.content.ConfigBase;
  import com.achep.base.providers.LogsProviderBase;
  import com.achep.base.utils.FileUtils;
  import com.achep.base.utils.IntentUtils;
  import com.achep.base.utils.PackageUtils;
  import com.achep.base.utils.ResUtils;
  import com.achep.base.utils.ToastUtils;
  import com.achep.base.utils.ViewUtils;
  import com.achep.base.utils.logcat.Logcat;
  import com.afollestad.materialdialogs.DialogAction;
  import com.afollestad.materialdialogs.MaterialDialog;
  
  import org.json.JSONException;
  import org.json.JSONObject;
  
  import java.io.File;
  import java.text.SimpleDateFormat;
  import java.util.Date;
  import java.util.Locale;
  import java.util.TimeZone;
  
  import static com.achep.base.Build.DEBUG;
  
  /**
   * Feedback dialog fragment.
   * <p/>
   * Provides an UI for sending bugs & suggestions on my email.
   */
  public class FeedbackDialog extends DialogFragment implements ConfigBase.OnConfigChangedListener {
  
      private View mFaqContainer;
  
      private Spinner mSpinner;
      private EditText mEditText;
      private CheckBox mAttachLogCheckBox;
  
      private final AdapterView.OnItemSelectedListener mListener =
              new AdapterView.OnItemSelectedListener() {
                  @Override
                  public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                      // Show "Attach log" checkbox only if the type
                      // of this message is "Issue".
                      ViewUtils.setVisible(mAttachLogCheckBox, position == 0);
                  }
  
                  @Override
                  public void onNothingSelected(AdapterView<?> parent) {
                      ViewUtils.setVisible(mAttachLogCheckBox, false);
                  }
              };
  
      @Override
      public void onResume() {
          super.onResume();
  
          Config config = Config.getInstance();
          config.registerListener(this);
  
          updateFaqPanel(config.getTriggers().isHelpRead());
      }
  
      @Override
      public void onPause() {
          Config config = Config.getInstance();
          config.unregisterListener(this);
  
          super.onPause();
      }
  
      @Override
      public void onConfigChanged(@NonNull ConfigBase config,
                                  @NonNull String key,
                                  @NonNull Object value) {
          switch (key) {
              case Config.KEY_TRIG_HELP_READ:
                  boolean read = (boolean) value;
                  updateFaqPanel(read);
                  break;
          }
      }
  
      @NonNull
      @Override
      public Dialog onCreateDialog(Bundle savedInstanceState) {
          Activity activity = getActivity();
          assert activity != null;
  
          MaterialDialog md = new MaterialDialog.Builder(activity)
                  .iconRes(R.drawable.ic_email_white_24dp)
                  .title(R.string.feedback_dialog_title)
                  .customView(R.layout.feedback_dialog, true)
                  .negativeText(android.R.string.cancel)
                  .positiveText(R.string.send)
                  .onPositive(new MaterialDialog.SingleButtonCallback() {
                      @Override
                      public void onClick(@NonNull MaterialDialog materialDialog,
                                          @NonNull DialogAction dialogAction) {
                          Context context = getActivity();
                          CharSequence message = mEditText.getText();
  
                          if (isMessageLongEnough(message)) {
                              boolean attachLog = mAttachLogCheckBox.isChecked()
                                      && mAttachLogCheckBox.getVisibility() == View.VISIBLE;
  
                              int type = mSpinner.getSelectedItemPosition();
                              CharSequence title = createTitle(context, type);
                              CharSequence body = createBody(context, message);
                              send(title, body, attachLog);
                          } else {
                              String toastText = getString(
                                      R.string.feedback_error_msg_too_short,
                                      getMinMessageLength());
                              ToastUtils.showShort(context, toastText);
                          }
                      }
                  })
                  .onNegative(new MaterialDialog.SingleButtonCallback() {
                      @Override
                      public void onClick(@NonNull MaterialDialog materialDialog,
                                          @NonNull DialogAction dialogAction) {
                          dismiss();
                      }
                  })
                  .autoDismiss(false)
                  .build();
  
          View view = md.getCustomView();
          assert view != null;
          mSpinner = (Spinner) view.findViewById(R.id.type);
          mSpinner.setOnItemSelectedListener(mListener);
          mEditText = (EditText) view.findViewById(R.id.message);
          mAttachLogCheckBox = (CheckBox) view.findViewById(R.id.checkbox);
  
          // Frequently asked questions panel
          Config.Triggers triggers = Config.getInstance().getTriggers();
          if (!triggers.isHelpRead()) initFaqPanel((ViewGroup) view);
  
          return md;
      }
  
      /**
       * Initialize Frequently asked questions panel. This panel is here to reduce
       * the number of already answered questions.
       */
      private void initFaqPanel(@NonNull ViewGroup root) {
          mFaqContainer = ((ViewStub) root.findViewById(R.id.faq)).inflate();
          mFaqContainer.findViewById(R.id.faq).setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View v) {
                  AppCompatActivity activity = (AppCompatActivity) getActivity();
                  DialogHelper.showHelpDialog(activity);
              }
          });
      }
  
      /**
       * Removes Frequently asked questions panel from the view
       * and sets {@link #mFaqContainer} to null.
       * After calling this method you no longer able to get panel back.
       */
      private void recycleFaqPanel() {
          ViewUtils.removeViewParent(mFaqContainer);
          mFaqContainer = null;
      }
  
      /**
       * {@link #recycleFaqPanel() Recycles} Frequently asked questions panel when it's not needed
       * anymore.
       *
       * @param isHelpRead {@code true} to recycle panel, {@code false} to do nothing.
       */
      private void updateFaqPanel(boolean isHelpRead) {
          if (mFaqContainer != null && isHelpRead) {
              recycleFaqPanel();
          }
      }
  
      private void send(@NonNull CharSequence title,
                        @NonNull CharSequence body, boolean attachLog) {
          Activity context = getActivity();
          String[] recipients = {Build.SUPPORT_EMAIL};
          Intent intent = new Intent()
                  .putExtra(Intent.EXTRA_EMAIL, recipients)
                  .putExtra(Intent.EXTRA_SUBJECT, title)
                  .putExtra(Intent.EXTRA_TEXT, body);
  
          if (attachLog) {
              attachLog(intent);
              intent.setAction(Intent.ACTION_SEND);
              intent.setType("message/rfc822");
          } else {
              intent.setAction(Intent.ACTION_SENDTO);
              intent.setData(Uri.parse("mailto:")); // only email apps should handle it
          }
  
          if (IntentUtils.hasActivityForThat(context, intent)) {
              startActivity(intent);
              dismiss();
          } else {
              ToastUtils.showLong(context, R.string.feedback_error_no_app);
          }
      }
  
      /**
       * Creates the title of the email.
       *
       * @param type one of the following types:
       *             0 - issue
       *             1 - suggestion
       *             2 - other
       * @return the title of the email.
       */
      @NonNull
      private CharSequence createTitle(@NonNull Context context, int type) {
          CharSequence osVersion = Device.API_VERSION_NAME_SHORT;
          CharSequence[] types = new CharSequence[]{"issue", "suggestion", "other"};
          return AboutDialog.getVersionName(context) + ": " + osVersion + ", " + types[type];
      }
  
      /**
       * Creates the body of the email. It automatically adds some
       * info about the device.
       *
       * @param msg the message that been typed by user.
       * @return the body of the email
       */
      @NonNull
      private CharSequence createBody(@NonNull Context context, @NonNull CharSequence msg) {
          final String extra;
  
          do {
              PackageInfo pi;
              try {
                  pi = context
                          .getPackageManager()
                          .getPackageInfo(PackageUtils.getName(context), 0);
              } catch (PackageManager.NameNotFoundException e) {
                  extra = "There was an exception while getting my own package info.";
                  break;
              }
  
              JSONObject obj = new JSONObject();
              try {
                  Config config = Config.getInstance();
  
                  // App related stuff
                  obj.put("app_version_code", pi.versionCode);
                  obj.put("app_version_name", pi.versionName);
                  obj.put("app_timestamp", Build.TIME_STAMP);
                  obj.put("app_is_debug", DEBUG);
                  obj.put("app_is_help_read", config.getTriggers().isHelpRead());
                  obj.put("app_launch_count", config.getTriggers().getLaunchCount());
  
                  // Device related stuff
                  obj.put("language", Locale.getDefault().getLanguage());
                  obj.put("android_version_release", android.os.Build.VERSION.RELEASE);
                  obj.put("android_version_sdk_int", android.os.Build.VERSION.SDK_INT);
                  obj.put("android_build_display", android.os.Build.DISPLAY);
                  obj.put("android_build_brand", android.os.Build.BRAND);
                  obj.put("android_build_model", android.os.Build.MODEL);
              } catch (JSONException ignored) {
                  extra = "There was an exception while building JSON.";
                  break;
              }
  
              extra = obj.toString().replaceAll(",\"", ", \"");
          } while (false);
  
          return msg + "\n\nExtras (added automatically & do not change):\n" + extra;
      }
  
      private boolean isMessageLongEnough(@Nullable CharSequence message) {
          return message != null && message.length() >= getMinMessageLength();
      }
  
      private int getMinMessageLength() {
          return getResources().getInteger(R.integer.config_feedback_minMessageLength);
      }
  
      private void attachLog(@NonNull Intent intent) {
          Context context = getActivity();
  
          try {
              String log = Logcat.capture();
              if (log == null)
                  throw new Exception("Failed to capture the logcat.");
  
              // Prepare cache directory.
              File cacheDir = context.getCacheDir();
              if (cacheDir == null)
                  throw new Exception("Cache directory is inaccessible");
              File directory = new File(cacheDir, LogsProviderBase.DIRECTORY);
              FileUtils.deleteRecursive(directory); // Clean-up cache folder
              if (!directory.mkdirs())
                  throw new Exception("Failed to create cache directory.");
  
              // Create log file.
              @SuppressLint("SimpleDateFormat")
              SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
              sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
              String fileName = "AcDisplay_log_" + sdf.format(new Date()) + ".txt";
              File file = new File(directory, fileName);
  
              // Write to the file.
              if (!FileUtils.writeToFile(file, log))
                  throw new Exception("Failed to write log to the file.");
  
              // Put extra stream to the intent.
              Uri uri = Uri.parse("content://" + LogAttachmentProvider.AUTHORITY + "/" + fileName);
              intent.putExtra(Intent.EXTRA_STREAM, uri);
          } catch (Exception e) {
              String message = ResUtils.getString(getResources(), R.string.feedback_error_accessing_log, e.getMessage());
              ToastUtils.showLong(context, message);
          }
      }
  
  }
  