key.secretkey.crypto.PgpHandler.java Source code

Java tutorial

Introduction

Here is the source code for key.secretkey.crypto.PgpHandler.java

Source

package key.secretkey.crypto;

import android.app.Activity;
import android.app.DialogFragment;
import android.app.PendingIntent;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.graphics.Typeface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.text.method.PasswordTransformationMethod;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.google.common.primitives.Longs;
import key.secretkey.BuildConfig;
import key.secretkey.R;
import key.secretkey.passwordGenerationFragment;
import key.secretkey.SelectFolderFragment;
import key.secretkey.utils.PasswordStorage;

import org.apache.commons.io.FileUtils;
import org.openintents.openpgp.IOpenPgpService2;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

/**   CODE EMPRUNT
 *      Les lignes suivantes proviennent du projet open source
 *      Android-Password-Store sous license GPL 3.0 de l'auteur Zeapo
 *      Cette classe permet de grer les appels  l'API openpgp
 */
public class PgpHandler extends AppCompatActivity implements OpenPgpServiceConnection.OnBound {
    private static DelayShow delayTask;
    private OpenPgpServiceConnection mServiceConnection;
    private Set<String> keyIDs = new HashSet<>();
    SharedPreferences settings;
    private Activity activity;
    ClipboardManager clipboard;

    SelectFolderFragment passwordList;
    private Intent selectFolderData;

    private boolean registered;

    public static final int REQUEST_CODE_SIGN = 9910;
    public static final int REQUEST_CODE_ENCRYPT = 9911;
    public static final int REQUEST_CODE_SIGN_AND_ENCRYPT = 9912;
    public static final int REQUEST_CODE_DECRYPT_AND_VERIFY = 9913;
    public static final int REQUEST_CODE_GET_KEY = 9914;
    public static final int REQUEST_CODE_GET_KEY_IDS = 9915;
    public static final int REQUEST_CODE_EDIT = 9916;
    public static final int REQUEST_CODE_SELECT_FOLDER = 9917;

    private String decodedPassword = "";

    public final class Constants {
        public static final String TAG = "Keychain";
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);

        this.activity = this;
        this.clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);

        if (getIntent().getStringExtra("Operation").equals("ENCRYPT")) {
            setTitle("New password");
        }

        // some persistance
        settings = PreferenceManager.getDefaultSharedPreferences(this);
        String providerPackageName = settings.getString("openpgp_provider_list", "");
        keyIDs = settings.getStringSet("openpgp_key_ids_set", new HashSet<String>());

        registered = false;

        if (TextUtils.isEmpty(providerPackageName)) {
            Toast.makeText(this, this.getResources().getString(R.string.provider_toast_text), Toast.LENGTH_LONG)
                    .show();
            Intent intent = new Intent(this, key.secretkey.SettingsActivity.class);
            startActivity(intent);
            // a small hack to avoid eternal loop later, have to be solved via a startactivityforresult()
            setResult(RESULT_CANCELED);
            finish();

        } else {

            // bind to service
            mServiceConnection = new OpenPgpServiceConnection(PgpHandler.this, providerPackageName, this);
            mServiceConnection.bindToService();
            registered = true;
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if (this.registered && this.mServiceConnection.isBound())
            try {
                this.mServiceConnection.unbindFromService();
            } catch (Exception e) {

            }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        switch (getIntent().getStringExtra("Operation")) {
        case "ENCRYPT":
            getMenuInflater().inflate(R.menu.pgp_handler_new_password, menu);
            break;
        case "SELECTFOLDER":
            getMenuInflater().inflate(R.menu.pgp_handler_select_folder, menu);
            break;
        default:
            getMenuInflater().inflate(R.menu.pgp_handler, menu);
        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        switch (id) {
        case android.R.id.home:
            setResult(RESULT_CANCELED);
            finish();
            return true;
        case R.id.copy_password:
            copyToClipBoard();
            break;
        case R.id.share_password_as_plaintext:
            shareAsPlaintext();
            break;
        case R.id.edit_password:
            editPassword();
            break;
        case R.id.crypto_confirm_add:
            encrypt(new Intent());
            break;
        case R.id.crypto_cancel_add:
            setResult(RESULT_CANCELED);
            finish();
            return true;
        //            case R.id.crypto_select:
        //                selectFolder();
        //                break;
        }
        return super.onOptionsItemSelected(item);
    }

    //    private void selectFolder() {
    //        if (selectFolderData == null || passwordList == null){
    //            Log.wtf(Constants.TAG,"Folder selected while the app didn't ask for one to be selected?");
    //        }
    //        selectFolderData.putExtra("SELECTED_FOLDER_PATH",passwordList.getCurrentDir().getAbsolutePath());
    //        setResult(RESULT_OK,selectFolderData);
    //        finish();
    //    }

    public void editPassword() {
        // if in encrypt or in decrypt and password is invisible
        // (because !showPassword, so this will instantly close), do nothing
        if (findViewById(R.id.crypto_password_show) == null
                || findViewById(R.id.crypto_container).getVisibility() != View.VISIBLE)
            return;

        CharSequence category = ((TextView) findViewById(R.id.crypto_password_category)).getText();
        CharSequence file = ((TextView) findViewById(R.id.crypto_password_file)).getText();
        CharSequence password = ((TextView) findViewById(R.id.crypto_password_show)).getText();
        CharSequence extra = ((TextView) findViewById(R.id.crypto_extra_show)).getText();

        setContentView(R.layout.encrypt_layout);
        Typeface monoTypeface = Typeface.createFromAsset(getAssets(), "fonts/sourcecodepro.ttf");
        ((EditText) findViewById(R.id.crypto_password_edit)).setTypeface(monoTypeface);
        ((EditText) findViewById(R.id.crypto_extra_edit)).setTypeface(monoTypeface);

        ((TextView) findViewById(R.id.crypto_password_category)).setText(category);
        ((EditText) findViewById(R.id.crypto_password_file_edit)).setText(file);
        ((EditText) findViewById(R.id.crypto_password_edit)).setText(password);
        ((EditText) findViewById(R.id.crypto_extra_edit)).setText(extra);

        // strictly editing only i.e. can't save this password's info to another file by changing name
        findViewById(R.id.crypto_password_file_edit).setEnabled(false);

        // the original intent was to decrypt so FILE_PATH will have the file, not enclosing dir
        // PgpCallback expects the dir when encrypting
        String filePath = getIntent().getExtras().getString("FILE_PATH");
        String directoryPath = filePath.substring(0, filePath.lastIndexOf(File.separator));
        Intent intent = new Intent(this, PgpHandler.class);
        intent.putExtra("FILE_PATH", directoryPath);
        intent.putExtra("Operation", "ENCRYPT");
        intent.putExtra("fromDecrypt", true);
        setIntent(intent);

        // recreate the options menu to be the encrypt one
        invalidateOptionsMenu();
    }

    public void shareAsPlaintext() {

        if (findViewById(R.id.share_password_as_plaintext) == null)
            return;

        Intent sendIntent = new Intent();
        sendIntent.setAction(Intent.ACTION_SEND);
        sendIntent.putExtra(Intent.EXTRA_TEXT, decodedPassword);
        sendIntent.setType("text/plain");
        startActivity(
                Intent.createChooser(sendIntent, getResources().getText(R.string.send_plaintext_password_to)));//Always show a picker to give the user a chance to cancel
    }

    public void copyToClipBoard() {

        if (findViewById(R.id.crypto_password_show) == null)
            return;

        if (decodedPassword.isEmpty()) {
            return;
        }

        setTimer();

        ClipData clip = ClipData.newPlainText("pgp_handler_result_pm", decodedPassword);
        clipboard.setPrimaryClip(clip);

        try {
            showToast(this.getResources().getString(R.string.clipboard_beginning_toast_text) + " "
                    + Integer.parseInt(settings.getString("general_show_time", "45")) + " "
                    + this.getResources().getString(R.string.clipboard_ending_toast_text));
        } catch (NumberFormatException e) {
            showToast(this.getResources().getString(R.string.clipboard_beginning_toast_text) + " 45 "
                    + this.getResources().getString(R.string.clipboard_ending_toast_text));
        }
    }

    public void handleClick(View view) {
        switch (view.getId()) {
        case R.id.crypto_show_button:
            decryptAndVerify(new Intent());
            break;
        case R.id.crypto_delete_button:
            //                deletePassword();
            break;
        case R.id.crypto_get_key_ids:
            getKeyIds(new Intent());
            break;
        case R.id.generate_password:
            DialogFragment df = new passwordGenerationFragment();
            df.show(getFragmentManager(), "generator");
        default:
            Log.wtf(Constants.TAG, "This should not happen.... PgpHandler.java#handleClick(View) default reached.");
            // should not happen

        }
    }

    private void handleError(final OpenPgpError error) {
        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                Toast.makeText(PgpHandler.this, "Error from OpenKeyChain : " + error.getMessage(),
                        Toast.LENGTH_LONG).show();
                Log.e(Constants.TAG, "onError getErrorId:" + error.getErrorId());
                Log.e(Constants.TAG, "onError getMessage:" + error.getMessage());
            }
        });
    }

    private void showToast(final String message) {
        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                Toast.makeText(PgpHandler.this, message, Toast.LENGTH_SHORT).show();
            }
        });
    }

    public class DelayShow extends AsyncTask<Void, Integer, Boolean> {
        ProgressBar pb;
        boolean clearClipboard = true;
        int current, showTime;

        @Override
        protected void onPreExecute() {
            try {
                showTime = Integer.parseInt(settings.getString("general_show_time", "45"));
            } catch (NumberFormatException e) {
                showTime = 45;
            }
            current = 0;

            LinearLayout container = (LinearLayout) findViewById(R.id.crypto_container);
            container.setVisibility(View.VISIBLE);

            TextView extraText = (TextView) findViewById(R.id.crypto_extra_show);

            if (extraText.getText().length() != 0)
                findViewById(R.id.crypto_extra_show_layout).setVisibility(View.VISIBLE);

            if (showTime == 0) {
                // treat 0 as forever, and the user must exit and/or clear clipboard on their own
                cancel(true);
            } else {
                this.pb = (ProgressBar) findViewById(R.id.pbLoading);
                this.pb.setMax(showTime);
            }
        }

        @Override
        protected Boolean doInBackground(Void... params) {
            while (current < showTime) {
                SystemClock.sleep(1000);
                current++;
                publishProgress(current);
            }
            return true;
        }

        @Override
        protected void onPostExecute(Boolean b) {
            // only clear the clipboard if we automatically copied the password to it
            if (settings.getBoolean("copy_on_decrypt", true) && clearClipboard) {
                Log.d("DELAY_SHOW", "Clearing the clipboard");
                ClipData clip = ClipData.newPlainText("pgp_handler_result_pm", "");
                clipboard.setPrimaryClip(clip);
                if (settings.getBoolean("clear_clipboard_20x", false)) {
                    Handler handler = new Handler();
                    for (int i = 0; i < 19; i++) {
                        final String count = String.valueOf(i);
                        handler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                clipboard.setPrimaryClip(ClipData.newPlainText(count, count));
                            }
                        }, i * 500);
                    }
                }
            }
            decodedPassword = "";
            if (findViewById(R.id.crypto_password_show) != null) {
                // clear password; if decrypt changed to encrypt layout via edit button, no need
                ((TextView) findViewById(R.id.crypto_password_show)).setText("");
                ((TextView) findViewById(R.id.crypto_extra_show)).setText("");
                findViewById(R.id.crypto_extra_show_layout).setVisibility(View.INVISIBLE);
                findViewById(R.id.crypto_container).setVisibility(View.INVISIBLE);
                activity.setResult(RESULT_CANCELED);
                activity.finish();
            }
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            this.pb.setProgress(values[0]);
        }

        public void setClearClipboard(boolean value) {
            clearClipboard = value;
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Log.d(Constants.TAG, "onActivityResult resultCode: " + resultCode);

        // try again after user interaction
        if (resultCode == RESULT_OK) {
            /*
             * The data originally given to one of the methods above, is again
             * returned here to be used when calling the method again after user
             * interaction. The Intent now also contains results from the user
             * interaction, for example selected key ids.
             */
            switch (requestCode) {
            case REQUEST_CODE_ENCRYPT: {
                encrypt(data);
                break;
            }
            case REQUEST_CODE_DECRYPT_AND_VERIFY: {
                findViewById(R.id.progress_bar_label).setVisibility(View.GONE);
                decryptAndVerify(data);
                break;
            }
            case REQUEST_CODE_GET_KEY_IDS:
                getKeyIds(data);
                break;
            case REQUEST_CODE_EDIT: {
                edit(data);
                break;
            }
            }
        } else if (resultCode == RESULT_CANCELED) {
            setResult(RESULT_CANCELED, data);
            finish();
        }
    }

    private void selectFolder(Intent data) {

        if (data.getStringExtra("Operation") == null || !data.getStringExtra("Operation").equals("SELECTFOLDER")) {
            Log.e(Constants.TAG, "PgpHandler#selectFolder(Intent) triggered with incorrect intent.");
            if (BuildConfig.DEBUG) {
                throw new UnsupportedOperationException("Triggered with incorrect intent.");
            }
            return;
        }

        Log.d(Constants.TAG, "PgpHandler#selectFolder(Intent).");

        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

        passwordList = new SelectFolderFragment();
        Bundle args = new Bundle();
        //        args.putString("Path", PasswordStorage.getRepositoryDirectory(getApplicationContext()).getAbsolutePath());

        passwordList.setArguments(args);

        getSupportActionBar().show();

        fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

        fragmentTransaction.replace(R.id.pgp_handler_linearlayout, passwordList, "PasswordsList");
        fragmentTransaction.commit();

        this.selectFolderData = data;
    }

    public class PgpCallback implements OpenPgpApi.IOpenPgpCallback {
        boolean returnToCiphertextField;
        ByteArrayOutputStream os;
        int requestCode;

        private PgpCallback(boolean returnToCiphertextField, ByteArrayOutputStream os, int requestCode) {
            this.returnToCiphertextField = returnToCiphertextField;
            this.os = os;
            this.requestCode = requestCode;
        }

        @Override
        public void onReturn(Intent result) {

            switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
            case OpenPgpApi.RESULT_CODE_SUCCESS: {
                // encrypt/decrypt/sign/verify
                final TextView textViewPassword = (TextView) findViewById(R.id.crypto_password_show);
                if (requestCode == REQUEST_CODE_DECRYPT_AND_VERIFY && os != null) {
                    try {
                        if (returnToCiphertextField) {
                            findViewById(R.id.progress_bar).setVisibility(View.GONE);
                            findViewById(R.id.progress_bar_label).setVisibility(View.GONE);

                            boolean showPassword = settings.getBoolean("show_password", true);
                            findViewById(R.id.crypto_container).setVisibility(View.VISIBLE);

                            Typeface monoTypeface = Typeface.createFromAsset(getAssets(),
                                    "fonts/sourcecodepro.ttf");
                            final String[] passContent = os.toString("UTF-8").split("\n");
                            textViewPassword.setTypeface(monoTypeface);
                            textViewPassword.setText(passContent[0]);

                            Button toggleVisibilityButton = (Button) findViewById(R.id.crypto_password_toggle_show);
                            toggleVisibilityButton.setVisibility(showPassword ? View.GONE : View.VISIBLE);
                            textViewPassword.setTransformationMethod(showPassword ? null
                                    : new HoldToShowPasswordTransformation(toggleVisibilityButton, new Runnable() {
                                        @Override
                                        public void run() {
                                            textViewPassword.setText(passContent[0]);
                                        }
                                    }));
                            decodedPassword = passContent[0];

                            String extraContent = os.toString("UTF-8").replaceFirst(".*\n", "");
                            if (extraContent.length() != 0) {
                                ((TextView) findViewById(R.id.crypto_extra_show)).setTypeface(monoTypeface);
                                ((TextView) findViewById(R.id.crypto_extra_show)).setText(extraContent);
                            }

                            if (settings.getBoolean("copy_on_decrypt", true)) {
                                copyToClipBoard();
                            }
                        } else {
                            Log.d("PGPHANDLER", "Error message after decrypt : " + os.toString());
                        }
                    } catch (UnsupportedEncodingException e) {
                        Log.e(Constants.TAG, "UnsupportedEncodingException", e);
                    }
                }
                // encrypt
                if (requestCode == REQUEST_CODE_ENCRYPT && os != null) {
                    try {
                        String path = getIntent().getExtras().getString("FILE_PATH") + "/"
                                + ((EditText) findViewById(R.id.crypto_password_file_edit)).getText().toString()
                                + ".gpg";
                        OutputStream outputStream = FileUtils.openOutputStream(new File(path));
                        outputStream.write(os.toByteArray());
                        outputStream.close();
                        Intent data = new Intent();
                        data.putExtra("CREATED_FILE", path);
                        data.putExtra("NAME",
                                ((EditText) findViewById(R.id.crypto_password_file_edit)).getText().toString());
                        // if coming from decrypt screen->edit button
                        if (getIntent().getBooleanExtra("fromDecrypt", false)) {
                            data.putExtra("needCommit", true);
                        }
                        setResult(RESULT_OK, data);
                        finish();
                    } catch (Exception e) {
                        Log.e(Constants.TAG, "UnsupportedEncodingException", e);
                    }
                }

                // get key ids
                if (result.hasExtra(OpenPgpApi.RESULT_KEY_IDS)) {
                    long[] ids = result.getLongArrayExtra(OpenPgpApi.RESULT_KEY_IDS);
                    Set<String> keys = new HashSet<String>();

                    for (long id : ids)
                        keys.add(String.valueOf(id)); // use Long
                    settings.edit().putStringSet("openpgp_key_ids_set", keys).apply();

                    showToast("PGP key selected");
                    setResult(RESULT_OK);
                    finish();
                }

                // edit
                if (requestCode == REQUEST_CODE_EDIT && os != null) {
                    try {
                        if (returnToCiphertextField) {
                            findViewById(R.id.progress_bar).setVisibility(View.GONE);
                            findViewById(R.id.progress_bar_label).setVisibility(View.GONE);

                            findViewById(R.id.crypto_container).setVisibility(View.VISIBLE);

                            Typeface monoTypeface = Typeface.createFromAsset(getAssets(),
                                    "fonts/sourcecodepro.ttf");
                            String[] passContent = os.toString("UTF-8").split("\n");
                            textViewPassword.setTypeface(monoTypeface);
                            textViewPassword.setText(passContent[0]);
                            decodedPassword = passContent[0];

                            String extraContent = os.toString("UTF-8").replaceFirst(".*\n", "");
                            if (extraContent.length() != 0) {
                                ((TextView) findViewById(R.id.crypto_extra_show)).setTypeface(monoTypeface);
                                ((TextView) findViewById(R.id.crypto_extra_show)).setText(extraContent);
                            }

                            editPassword();
                        } else {
                            Log.d("PGPHANDLER", "Error message after decrypt : " + os.toString());
                        }
                    } catch (UnsupportedEncodingException e) {
                        Log.e(Constants.TAG, "UnsupportedEncodingException", e);
                    }
                }
                break;
            }
            case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: {
                Log.i("PgpHandler", "RESULT_CODE_USER_INTERACTION_REQUIRED");

                View progress_bar_label = findViewById(R.id.progress_bar_label);
                if (progress_bar_label != null) {
                    progress_bar_label.setVisibility(View.VISIBLE);
                }

                PendingIntent pi = result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
                try {
                    PgpHandler.this.startIntentSenderForResult(pi.getIntentSender(), requestCode, null, 0, 0, 0);
                } catch (IntentSender.SendIntentException e) {
                    Log.e(Constants.TAG, "SendIntentException", e);
                }
                break;
            }
            case OpenPgpApi.RESULT_CODE_ERROR: {
                // TODO show what kind of error it is
                /* For example:
                 * No suitable key found -> no key in OpenKeyChain
                 *
                 * Check in open-pgp-lib how their definitions and error code
                 */
                OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
                handleError(error);

                break;
            }

            }
        }
    }

    public void getKeyIds(Intent data) {
        data.setAction(OpenPgpApi.ACTION_GET_KEY_IDS);
        OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
        api.executeApiAsync(data, null, null, new PgpCallback(false, null, PgpHandler.REQUEST_CODE_GET_KEY_IDS));
    }

    public void decryptAndVerify(Intent data) {
        data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);

        findViewById(R.id.progress_bar).setVisibility(View.VISIBLE);

        try {
            InputStream is = FileUtils.openInputStream(new File(getIntent().getExtras().getString("FILE_PATH")));

            ByteArrayOutputStream os = new ByteArrayOutputStream();

            OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
            api.executeApiAsync(data, is, os, new PgpCallback(true, os, REQUEST_CODE_DECRYPT_AND_VERIFY));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Encrypts a password file
     *
     * @param data
     */
    public void encrypt(Intent data) {
        data.setAction(OpenPgpApi.ACTION_ENCRYPT);

        ArrayList<Long> longKeys = new ArrayList<>();
        for (String keyId : keyIDs)
            longKeys.add(Long.valueOf(keyId));
        data.putExtra(OpenPgpApi.EXTRA_KEY_IDS, Longs.toArray(longKeys));

        data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);

        String name = ((EditText) findViewById(R.id.crypto_password_file_edit)).getText().toString();
        String pass = ((EditText) findViewById(R.id.crypto_password_edit)).getText().toString();
        String extra = ((EditText) findViewById(R.id.crypto_extra_edit)).getText().toString();

        if (name.isEmpty()) {
            showToast(this.getResources().getString(R.string.file_toast_text));
            return;
        }

        if (pass.isEmpty() && extra.isEmpty()) {
            showToast(this.getResources().getString(R.string.empty_toast_text));
            return;
        }

        ByteArrayInputStream is;

        try {
            is = new ByteArrayInputStream((pass + "\n" + extra).getBytes("UTF-8"));

            ByteArrayOutputStream os = new ByteArrayOutputStream();

            OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
            api.executeApiAsync(data, is, os, new PgpCallback(true, os, REQUEST_CODE_ENCRYPT));

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void edit(Intent data) {
        // exactly same as decrypt, only we want a different callback
        data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);

        findViewById(R.id.progress_bar).setVisibility(View.VISIBLE);

        try {
            InputStream is = FileUtils.openInputStream(new File(getIntent().getExtras().getString("FILE_PATH")));

            ByteArrayOutputStream os = new ByteArrayOutputStream();

            OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
            api.executeApiAsync(data, is, os, new PgpCallback(true, os, REQUEST_CODE_EDIT));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // TODO (low priority but still...) android M potential permissions crashes
    @Override
    public void onBound(IOpenPgpService2 service) {
        Log.i("PGP", "ISBOUND!!");

        Bundle extra = getIntent().getExtras();
        final String operation = extra.getString("Operation");
        if (operation == null) {
            return;
        }
        if (operation.equals("DECRYPT")) {
            setContentView(R.layout.decrypt_layout);
            ((TextView) findViewById(R.id.crypto_password_file)).setText(extra.getString("NAME"));
            String path = extra.getString("FILE_PATH")
                    .replace(PasswordStorage.getRepositoryDirectory(getApplicationContext()).getAbsolutePath(), "");
            String cat = new File(path).getParentFile().getName();

            ((TextView) findViewById(R.id.crypto_password_category)).setText(cat + "/");
            decryptAndVerify(new Intent());
        } else if (operation.equals("ENCRYPT")) {
            setContentView(R.layout.encrypt_layout);
            Typeface monoTypeface = Typeface.createFromAsset(getAssets(), "fonts/sourcecodepro.ttf");
            ((EditText) findViewById(R.id.crypto_password_edit)).setTypeface(monoTypeface);
            ((EditText) findViewById(R.id.crypto_extra_edit)).setTypeface(monoTypeface);
            String cat = extra.getString("FILE_PATH");
            cat = cat.replace(PasswordStorage.getRepositoryDirectory(getApplicationContext()).getAbsolutePath(),
                    "");
            cat = cat + "/";
            ((TextView) findViewById(R.id.crypto_password_category)).setText(cat);
        } else if (operation.equals("GET_KEY_ID")) {
            getKeyIds(new Intent());

            //            setContentView(R.layout.key_id);
            //            if (!keyIDs.isEmpty()) {
            //                String keys = keyIDs.split(",").length > 1 ? keyIDs : keyIDs.split(",")[0];
            //                ((TextView) findViewById(R.id.crypto_key_ids)).setText(keys);
            //            }
        } else if (operation.equals("EDIT")) {
            setContentView(R.layout.decrypt_layout);
            ((TextView) findViewById(R.id.crypto_password_file)).setText(extra.getString("NAME"));
            String cat = new File(extra.getString("FILE_PATH")
                    .replace(PasswordStorage.getRepositoryDirectory(getApplicationContext()).getAbsolutePath(), ""))
                            .getParentFile().getName();

            ((TextView) findViewById(R.id.crypto_password_category)).setText(cat + "/");
            edit(new Intent());
        } else if (operation.equals("SELECTFOLDER")) {
            setContentView(R.layout.select_folder_layout);
            selectFolder(getIntent());
        }
    }

    @Override
    public void onError(Exception e) {

    }

    private class HoldToShowPasswordTransformation extends PasswordTransformationMethod
            implements View.OnTouchListener {
        private final Runnable onToggle;
        private boolean shown = false;

        private HoldToShowPasswordTransformation(Button button, Runnable onToggle) {
            this.onToggle = onToggle;
            button.setOnTouchListener(this);
        }

        @Override
        public CharSequence getTransformation(CharSequence charSequence, View view) {
            return shown ? charSequence : super.getTransformation("12345", view);
        }

        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_DOWN:
                shown = true;
                onToggle.run();
                break;
            case MotionEvent.ACTION_UP:
                shown = false;
                onToggle.run();
                break;
            }
            return false;
        }
    }

    private void setTimer() {
        // If a task already exist, let it finish without clearing the clipboard
        if (delayTask != null) {
            delayTask.setClearClipboard(false);
        }

        delayTask = new DelayShow();
        delayTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }
}

/*
 *  FIN DU CODE EMPRUNT
 */