Experimental work on a fragment-based encrypt activity

This commit is contained in:
Dominik Schürmann 2014-04-01 13:25:44 +02:00
parent 8f2abf36f8
commit 4c3506f4b1
15 changed files with 1308 additions and 1182 deletions

View File

@ -22,23 +22,13 @@ import android.net.Uri;
import android.os.Bundle;
import android.support.v4.view.PagerTabStrip;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.beardedhen.androidbootstrap.BootstrapButton;
import com.devspark.appmsg.AppMsg;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.util.Log;
@ -47,7 +37,6 @@ import java.util.regex.Matcher;
public class DecryptActivity extends DrawerActivity {
/* Intents */
// without permission
public static final String ACTION_DECRYPT = Constants.INTENT_PREFIX + "DECRYPT";
/* EXTRA keys for input */
@ -64,9 +53,7 @@ public class DecryptActivity extends DrawerActivity {
private static final int PAGER_TAB_MESSAGE = 0;
private static final int PAGER_TAB_FILE = 1;
private void initView() {
// Pager
mViewPager = (ViewPager) findViewById(R.id.decrypt_pager);
mPagerTabStrip = (PagerTabStrip) findViewById(R.id.decrypt_pager_tab_strip);

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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
@ -49,20 +49,19 @@ import java.io.File;
public class DecryptFileFragment extends DecryptFragment {
public static final String ARG_FILENAME = "filename";
private static final int RESULT_CODE_FILE = 0x00007003;
// view
private EditText mFilename;
private CheckBox mDeleteAfter;
private BootstrapButton mBrowse;
private BootstrapButton mDecryptButton;
private String mInputFilename = null;
private String mOutputFilename = null;
private FileDialogFragment mFileDialog;
private static final int RESULT_CODE_FILE = 0x00007003;
/**
* Inflate the layout for this fragment
*/
@ -72,14 +71,14 @@ public class DecryptFileFragment extends DecryptFragment {
mFilename = (EditText) view.findViewById(R.id.decrypt_file_filename);
mBrowse = (BootstrapButton) view.findViewById(R.id.decrypt_file_browse);
mDeleteAfter = (CheckBox) view.findViewById(R.id.decrypt_file_delete_after_decryption);
mDecryptButton = (BootstrapButton) view.findViewById(R.id.decrypt_file_action_decrypt);
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FileHelper.openFile(DecryptFileFragment.this, mFilename.getText().toString(), "*/*",
RESULT_CODE_FILE);
}
});
mDeleteAfter = (CheckBox) view.findViewById(R.id.decrypt_file_delete_after_decryption);
mDecryptButton = (BootstrapButton) view.findViewById(R.id.decrypt_file_action_decrypt);
mDecryptButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -87,12 +86,17 @@ public class DecryptFileFragment extends DecryptFragment {
}
});
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
String filename = getArguments().getString(ARG_FILENAME);
if (filename != null) {
mFilename.setText(filename);
}
return view;
}
private void guessOutputFilename() {
@ -193,14 +197,12 @@ public class DecryptFileFragment extends DecryptFragment {
// get returned data bundle
Bundle returnData = message.getData();
PgpDecryptVerifyResult decryptVerifyResult =
returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT);
if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) {
showPassphraseDialog(decryptVerifyResult.getKeyIdPassphraseNeeded());
} else {
if (mDeleteAfter.isChecked()) {
// Create and show dialog to delete original file
DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment
@ -208,13 +210,11 @@ public class DecryptFileFragment extends DecryptFragment {
deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
}
OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
// display signature result in activity
onSignatureResult(signatureResult);
}
}
}
};

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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
@ -187,6 +187,10 @@ public class DecryptFragment extends Fragment {
}
}
/**
* Should be overridden by MessageFragment and FileFragment to start actual decryption
* @param passphrase
*/
protected void decryptStart(String passphrase) {
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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
@ -50,7 +50,6 @@ public class DecryptMessageFragment extends DecryptFragment {
private BootstrapButton mDecryptButton;
private BootstrapButton mDecryptFromCLipboardButton;
/**
* Inflate the layout for this fragment
*/
@ -61,14 +60,12 @@ public class DecryptMessageFragment extends DecryptFragment {
mMessage = (EditText) view.findViewById(R.id.message);
mDecryptButton = (BootstrapButton) view.findViewById(R.id.action_decrypt);
mDecryptFromCLipboardButton = (BootstrapButton) view.findViewById(R.id.action_decrypt_from_clipboard);
mDecryptButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
decryptStart(null);
}
});
mDecryptFromCLipboardButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@ -93,6 +90,7 @@ public class DecryptMessageFragment extends DecryptFragment {
private void decryptFromClipboard() {
CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity());
// only decrypt if clipboard content is available and a pgp message or cleartext signature
if (clipboardText != null) {
Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(clipboardText);
if (!matcher.matches()) {
@ -146,14 +144,12 @@ public class DecryptMessageFragment extends DecryptFragment {
// get returned data bundle
Bundle returnData = message.getData();
PgpDecryptVerifyResult decryptVerifyResult =
returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT);
if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) {
showPassphraseDialog(decryptVerifyResult.getKeyIdPassphraseNeeded());
} else {
AppMsg.makeText(getActivity(), R.string.decryption_successful,
AppMsg.STYLE_INFO).show();
@ -162,13 +158,11 @@ public class DecryptMessageFragment extends DecryptFragment {
mMessage.setText(new String(decryptedMessage));
mMessage.setHorizontallyScrolling(false);
OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
// display signature result in activity
onSignatureResult(signatureResult);
}
}
}
};
@ -184,5 +178,4 @@ public class DecryptMessageFragment extends DecryptFragment {
getActivity().startService(intent);
}
}

View File

@ -0,0 +1,14 @@
package org.sufficientlysecure.keychain.ui;
public interface EncryptActivityInterface {
public boolean isModeSymmetric();
public long getSignatureKey();
public long[] getEncryptionKeys();
public String getPassphrase();
public String getPassphraseAgain();
}

View File

@ -0,0 +1,283 @@
/*
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import java.util.Vector;
public class EncryptAsymmetricFragment extends Fragment {
public static final String ARG_SIGNATURE_KEY_ID = "signature_key_id";
public static final String ARG_ENCRYPTION_KEY_IDS = "encryption_key_ids";
public static final int RESULT_CODE_PUBLIC_KEYS = 0x00007001;
public static final int RESULT_CODE_SECRET_KEYS = 0x00007002;
OnAsymmetricKeySelection mKeySelectionListener;
// view
private BootstrapButton mSelectKeysButton;
private CheckBox mSign;
private TextView mMainUserId;
private TextView mMainUserIdRest;
// model
private long mSecretKeyId = Id.key.none;
private long mEncryptionKeyIds[] = null;
// Container Activity must implement this interface
public interface OnAsymmetricKeySelection {
public void onSigningKeySelected(long signingKeyId);
public void onEncryptionKeysSelected(long[] encryptionKeyIds);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mKeySelectionListener = (OnAsymmetricKeySelection) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnAsymmetricKeySelection");
}
}
private void setSignatureKeyId(long signatureKeyId) {
mSecretKeyId = signatureKeyId;
mKeySelectionListener.onSigningKeySelected(signatureKeyId);
}
private void setEncryptionKeyIds(long[] encryptionKeyIds) {
mEncryptionKeyIds = encryptionKeyIds;
mKeySelectionListener.onEncryptionKeysSelected(encryptionKeyIds);
}
/**
* Inflate the layout for this fragment
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.encrypt_asymmetric_fragment, container, false);
mSelectKeysButton = (BootstrapButton) view.findViewById(R.id.btn_selectEncryptKeys);
mSign = (CheckBox) view.findViewById(R.id.sign);
mMainUserId = (TextView) view.findViewById(R.id.mainUserId);
mMainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
mSelectKeysButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
selectPublicKeys();
}
});
mSign.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
CheckBox checkBox = (CheckBox) v;
if (checkBox.isChecked()) {
selectSecretKey();
} else {
setSignatureKeyId(Id.key.none);
updateView();
}
}
});
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
long signatureKeyId = getArguments().getLong(ARG_SIGNATURE_KEY_ID);
long[] encryptionKeyIds = getArguments().getLongArray(ARG_ENCRYPTION_KEY_IDS);
// preselect keys given by arguments (given by Intent to EncryptActivity)
preselectKeys(signatureKeyId, encryptionKeyIds);
}
/**
* If an Intent gives a signatureKeyId and/or encryptionKeyIds, preselect those!
*
* @param preselectedSignatureKeyId
* @param preselectedEncryptionKeyIds
*/
private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds) {
if (preselectedSignatureKeyId != 0) {
PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(getActivity(),
preselectedSignatureKeyId);
PGPSecretKey masterKey;
if (keyRing != null) {
masterKey = PgpKeyHelper.getMasterKey(keyRing);
if (masterKey != null) {
Vector<PGPSecretKey> signKeys = PgpKeyHelper.getUsableSigningKeys(keyRing);
if (signKeys.size() > 0) {
setSignatureKeyId(masterKey.getKeyID());
}
}
}
}
if (preselectedEncryptionKeyIds != null) {
Vector<Long> goodIds = new Vector<Long>();
for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) {
PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(getActivity(),
preselectedEncryptionKeyIds[i]);
PGPPublicKey masterKey;
if (keyRing == null) {
continue;
}
masterKey = PgpKeyHelper.getMasterKey(keyRing);
if (masterKey == null) {
continue;
}
Vector<PGPPublicKey> encryptKeys = PgpKeyHelper.getUsableEncryptKeys(keyRing);
if (encryptKeys.size() == 0) {
continue;
}
goodIds.add(masterKey.getKeyID());
}
if (goodIds.size() > 0) {
long[] keyIds = new long[goodIds.size()];
for (int i = 0; i < goodIds.size(); ++i) {
keyIds[i] = goodIds.get(i);
}
setEncryptionKeyIds(keyIds);
}
}
}
private void updateView() {
if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
mSelectKeysButton.setText(getString(R.string.select_keys_button_default));
} else {
mSelectKeysButton.setText(getResources().getQuantityString(
R.plurals.select_keys_button, mEncryptionKeyIds.length,
mEncryptionKeyIds.length));
}
if (mSecretKeyId == Id.key.none) {
mSign.setChecked(false);
mMainUserId.setText("");
mMainUserIdRest.setText("");
} else {
String uid = getResources().getString(R.string.user_id_no_name);
String uidExtra = "";
// TODO: make it nice and use helper!
PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(getActivity(),
mSecretKeyId);
if (keyRing != null) {
PGPSecretKey key = PgpKeyHelper.getMasterKey(keyRing);
if (key != null) {
String userId = PgpKeyHelper.getMainUserIdSafe(getActivity(), key);
String chunks[] = userId.split(" <", 2);
uid = chunks[0];
if (chunks.length > 1) {
uidExtra = "<" + chunks[1];
}
}
}
mMainUserId.setText(uid);
mMainUserIdRest.setText(uidExtra);
mSign.setChecked(true);
}
//TODO
// updateActionBarButtons();
}
private void selectPublicKeys() {
Intent intent = new Intent(getActivity(), SelectPublicKeyActivity.class);
Vector<Long> keyIds = new Vector<Long>();
if (mSecretKeyId != 0) {
keyIds.add(mSecretKeyId);
}
if (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) {
for (int i = 0; i < mEncryptionKeyIds.length; ++i) {
keyIds.add(mEncryptionKeyIds[i]);
}
}
long[] initialKeyIds = null;
if (keyIds.size() > 0) {
initialKeyIds = new long[keyIds.size()];
for (int i = 0; i < keyIds.size(); ++i) {
initialKeyIds[i] = keyIds.get(i);
}
}
intent.putExtra(SelectPublicKeyActivity.EXTRA_SELECTED_MASTER_KEY_IDS, initialKeyIds);
startActivityForResult(intent, Id.request.public_keys);
}
private void selectSecretKey() {
Intent intent = new Intent(getActivity(), SelectSecretKeyActivity.class);
startActivityForResult(intent, Id.request.secret_keys);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case RESULT_CODE_PUBLIC_KEYS: {
if (resultCode == Activity.RESULT_OK) {
Bundle bundle = data.getExtras();
setEncryptionKeyIds(bundle
.getLongArray(SelectPublicKeyActivity.RESULT_EXTRA_MASTER_KEY_IDS));
}
updateView();
break;
}
case RESULT_CODE_SECRET_KEYS: {
if (resultCode == Activity.RESULT_OK) {
Uri uri_master_key = data.getData();
setSignatureKeyId(Long.valueOf(uri_master_key.getLastPathSegment()));
} else {
setSignatureKeyId(Id.key.none);
}
updateView();
break;
}
default: {
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
}

View File

@ -0,0 +1,415 @@
/*
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Spinner;
import com.beardedhen.androidbootstrap.BootstrapButton;
import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Choice;
import org.sufficientlysecure.keychain.util.Log;
import java.io.File;
public class EncryptFileFragment extends Fragment {
public static final String ARG_FILENAME = "filename";
public static final String ARG_ASCII_ARMOR = "ascii_armor";
private static final int RESULT_CODE_FILE = 0x00007003;
private EncryptActivityInterface mEncryptInterface;
// view
private CheckBox mAsciiArmor = null;
private Spinner mFileCompression = null;
private EditText mFilename = null;
private CheckBox mDeleteAfter = null;
private CheckBox mShareAfter = null;
private BootstrapButton mBrowse = null;
private BootstrapButton mEncryptFile;
private FileDialogFragment mFileDialog;
// model
private String mInputFilename = null;
private String mOutputFilename = null;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mEncryptInterface = (EncryptActivityInterface) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement EncryptActivityInterface");
}
}
/**
* Inflate the layout for this fragment
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.encrypt_file_fragment, container, false);
mEncryptFile = (BootstrapButton) view.findViewById(R.id.action_encrypt_file);
mEncryptFile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
encryptClicked();
}
});
mFilename = (EditText) view.findViewById(R.id.filename);
mBrowse = (BootstrapButton) view.findViewById(R.id.btn_browse);
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FileHelper.openFile(EncryptFileFragment.this, mFilename.getText().toString(), "*/*",
Id.request.filename);
}
});
mFileCompression = (Spinner) view.findViewById(R.id.fileCompression);
Choice[] choices = new Choice[]{
new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " ("
+ getString(R.string.compression_fast) + ")"),
new Choice(Id.choice.compression.zip, "ZIP ("
+ getString(R.string.compression_fast) + ")"),
new Choice(Id.choice.compression.zlib, "ZLIB ("
+ getString(R.string.compression_fast) + ")"),
new Choice(Id.choice.compression.bzip2, "BZIP2 ("
+ getString(R.string.compression_very_slow) + ")"),};
ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(getActivity(),
android.R.layout.simple_spinner_item, choices);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mFileCompression.setAdapter(adapter);
int defaultFileCompression = Preferences.getPreferences(getActivity()).getDefaultFileCompression();
for (int i = 0; i < choices.length; ++i) {
if (choices[i].getId() == defaultFileCompression) {
mFileCompression.setSelection(i);
break;
}
}
mDeleteAfter = (CheckBox) view.findViewById(R.id.deleteAfterEncryption);
mShareAfter = (CheckBox) view.findViewById(R.id.shareAfterEncryption);
mAsciiArmor = (CheckBox) view.findViewById(R.id.asciiArmour);
mAsciiArmor.setChecked(Preferences.getPreferences(getActivity()).getDefaultAsciiArmour());
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
String filename = getArguments().getString(ARG_FILENAME);
if (filename != null) {
mFilename.setText(filename);
}
boolean asciiArmor = getArguments().getBoolean(ARG_ASCII_ARMOR);
if (asciiArmor) {
mAsciiArmor.setChecked(asciiArmor);
}
}
/**
* Guess output filename based on input path
*
* @param path
* @return Suggestion for output filename
*/
private String guessOutputFilename(String path) {
// output in the same directory but with additional ending
File file = new File(path);
String ending = (mAsciiArmor.isChecked() ? ".asc" : ".gpg");
String outputFilename = file.getParent() + File.separator + file.getName() + ending;
return outputFilename;
}
private void showOutputFileDialog() {
// Message is received after file is selected
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
if (message.what == FileDialogFragment.MESSAGE_OKAY) {
Bundle data = message.getData();
mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
encryptStart();
}
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(returnHandler);
mFileDialog = FileDialogFragment.newInstance(messenger,
getString(R.string.title_encrypt_to_file),
getString(R.string.specify_file_to_encrypt_to), mOutputFilename, null);
mFileDialog.show(getActivity().getSupportFragmentManager(), "fileDialog");
}
private void encryptClicked() {
String currentFilename = mFilename.getText().toString();
if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
mInputFilename = mFilename.getText().toString();
}
mOutputFilename = guessOutputFilename(mInputFilename);
if (mInputFilename.equals("")) {
AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show();
return;
}
if (!mInputFilename.startsWith("content")) {
File file = new File(mInputFilename);
if (!file.exists() || !file.isFile()) {
AppMsg.makeText(
getActivity(),
getString(R.string.error_message,
getString(R.string.error_file_not_found)), AppMsg.STYLE_ALERT)
.show();
return;
}
}
if (mEncryptInterface.isModeSymmetric()) {
// symmetric encryption
if (!mEncryptInterface.getPassphrase().equals(mEncryptInterface.getPassphraseAgain())) {
AppMsg.makeText(getActivity(), R.string.passphrases_do_not_match, AppMsg.STYLE_ALERT).show();
return;
}
boolean gotPassPhrase = (mEncryptInterface.getPassphrase().length() != 0);
if (!gotPassPhrase) {
AppMsg.makeText(getActivity(), R.string.passphrase_must_not_be_empty, AppMsg.STYLE_ALERT)
.show();
return;
}
} else {
// asymmetric encryption
boolean encryptIt = (mEncryptInterface.getEncryptionKeys() != null
&& mEncryptInterface.getEncryptionKeys().length > 0);
// for now require at least one form of encryption for files
if (!encryptIt) {
AppMsg.makeText(getActivity(), R.string.select_encryption_key, AppMsg.STYLE_ALERT).show();
return;
}
if (!encryptIt && mEncryptInterface.getSignatureKey() == 0) {
AppMsg.makeText(getActivity(), R.string.select_encryption_or_signature_key,
AppMsg.STYLE_ALERT).show();
return;
}
if (mEncryptInterface.getSignatureKey() != 0
&& PassphraseCacheService.getCachedPassphrase(getActivity(), mEncryptInterface.getSignatureKey()) == null) {
showPassphraseDialog();
return;
}
}
showOutputFileDialog();
}
private void encryptStart() {
// Send all information needed to service to edit key in other thread
Intent intent = new Intent(getActivity(), KeychainIntentService.class);
// fill values for this action
Bundle data = new Bundle();
boolean useAsciiArmor = true;
long encryptionKeyIds[] = null;
int compressionId = 0;
boolean signOnly = false;
long mSecretKeyIdToPass = 0;
if (mEncryptInterface.isModeSymmetric()) {
Log.d(Constants.TAG, "Symmetric encryption enabled!");
String passphrase = mEncryptInterface.getPassphrase();
if (passphrase.length() == 0) {
passphrase = null;
}
data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passphrase);
} else {
mSecretKeyIdToPass = mEncryptInterface.getSignatureKey();
encryptionKeyIds = mEncryptInterface.getEncryptionKeys();
signOnly = (mEncryptInterface.getEncryptionKeys() == null
|| mEncryptInterface.getEncryptionKeys().length == 0);
}
intent.setAction(KeychainIntentService.ACTION_ENCRYPT_SIGN);
// choose default settings, target and data bundle by target
useAsciiArmor = mAsciiArmor.isChecked();
compressionId = ((Choice) mFileCompression.getSelectedItem()).getId();
data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_URI);
Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename="
+ mOutputFilename);
data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename);
data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename);
data.putLong(KeychainIntentService.ENCRYPT_SECRET_KEY_ID, mSecretKeyIdToPass);
data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, useAsciiArmor);
data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, encryptionKeyIds);
data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, compressionId);
// data.putBoolean(KeychainIntentService.ENCRYPT_GENERATE_SIGNATURE, mGenerateSignature);
data.putBoolean(KeychainIntentService.ENCRYPT_SIGN_ONLY, signOnly);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
// Message is received after encrypting is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
// get returned data bundle
Bundle data = message.getData();
String output;
AppMsg.makeText(getActivity(), R.string.encryption_successful,
AppMsg.STYLE_INFO).show();
if (mDeleteAfter.isChecked()) {
// Create and show dialog to delete original file
DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment
.newInstance(mInputFilename);
deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
}
if (mShareAfter.isChecked()) {
// Share encrypted file
Intent sendFileIntent = new Intent(Intent.ACTION_SEND);
sendFileIntent.setType("*/*");
sendFileIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(mOutputFilename));
startActivity(Intent.createChooser(sendFileIntent,
getString(R.string.title_send_file)));
}
}
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
// show progress dialog
saveHandler.showProgressDialog(getActivity());
// start service with intent
getActivity().startService(intent);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case RESULT_CODE_FILE: {
if (resultCode == Activity.RESULT_OK && data != null) {
try {
String path = FileHelper.getPath(getActivity(), data.getData());
Log.d(Constants.TAG, "path=" + path);
mFilename.setText(path);
} catch (NullPointerException e) {
Log.e(Constants.TAG, "Nullpointer while retrieving path!");
}
}
return;
}
default: {
super.onActivityResult(requestCode, resultCode, data);
break;
}
}
}
/**
* Shows passphrase dialog to cache a new passphrase the user enters for using it later for
* encryption
*/
private void showPassphraseDialog() {
// Message is received after passphrase is cached
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
showOutputFileDialog();
}
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(returnHandler);
try {
PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(
getActivity(), messenger, mEncryptInterface.getSignatureKey());
passphraseDialog.show(getActivity().getSupportFragmentManager(), "passphraseDialog");
} catch (PgpGeneralException e) {
Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!");
// send message to handler to start encryption directly
returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
}
}
}

View File

@ -0,0 +1,289 @@
/*
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import com.beardedhen.androidbootstrap.BootstrapButton;
import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
public class EncryptMessageFragment extends Fragment {
public static final String ARG_TEXT = "text";
private EditText mMessage = null;
private BootstrapButton mEncryptShare;
private BootstrapButton mEncryptClipboard;
private EncryptActivityInterface mEncryptInterface;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mEncryptInterface = (EncryptActivityInterface) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement EncryptActivityInterface");
}
}
/**
* Inflate the layout for this fragment
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.encrypt_message_fragment, container, false);
mMessage = (EditText) view.findViewById(R.id.message);
mEncryptClipboard = (BootstrapButton) view.findViewById(R.id.action_encrypt_clipboard);
mEncryptShare = (BootstrapButton) view.findViewById(R.id.action_encrypt_share);
mEncryptClipboard.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
encryptClicked(true);
}
});
mEncryptShare.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
encryptClicked(false);
}
});
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
String text = getArguments().getString(ARG_TEXT);
if (text != null) {
mMessage.setText(text);
}
}
/**
* Fixes bad message characters for gmail
*
* @param message
* @return
*/
private String fixBadCharactersForGmail(String message) {
// fix the message a bit, trailing spaces and newlines break stuff,
// because GMail sends as HTML and such things fuck up the
// signature,
// TODO: things like "<" and ">" also fuck up the signature
message = message.replaceAll(" +\n", "\n");
message = message.replaceAll("\n\n+", "\n\n");
message = message.replaceFirst("^\n+", "");
// make sure there'll be exactly one newline at the end
message = message.replaceFirst("\n*$", "\n");
return message;
}
private void encryptClicked(boolean toClipboard) {
if (mEncryptInterface.isModeSymmetric()) {
// symmetric encryption
boolean gotPassPhrase = false;
if (!mEncryptInterface.getPassphrase().equals(mEncryptInterface.getPassphraseAgain())) {
AppMsg.makeText(getActivity(), R.string.passphrases_do_not_match, AppMsg.STYLE_ALERT).show();
return;
}
gotPassPhrase = (mEncryptInterface.getPassphrase().length() != 0);
if (!gotPassPhrase) {
AppMsg.makeText(getActivity(), R.string.passphrase_must_not_be_empty, AppMsg.STYLE_ALERT)
.show();
return;
}
} else {
// asymmetric encryption
boolean encryptIt = (mEncryptInterface.getEncryptionKeys() != null
&& mEncryptInterface.getEncryptionKeys().length > 0);
if (!encryptIt && mEncryptInterface.getSignatureKey() == 0) {
AppMsg.makeText(getActivity(), R.string.select_encryption_or_signature_key,
AppMsg.STYLE_ALERT).show();
return;
}
if (mEncryptInterface.getSignatureKey() != 0
&& PassphraseCacheService.getCachedPassphrase(getActivity(), mEncryptInterface.getSignatureKey()) == null) {
showPassphraseDialog(toClipboard);
return;
}
}
encryptStart(toClipboard);
}
private void encryptStart(final boolean toClipboard) {
// Send all information needed to service to edit key in other thread
Intent intent = new Intent(getActivity(), KeychainIntentService.class);
// fill values for this action
Bundle data = new Bundle();
long encryptionKeyIds[] = null;
int compressionId = 0;
boolean signOnly = false;
long mSecretKeyIdToPass = 0;
if (mEncryptInterface.isModeSymmetric()) {
Log.d(Constants.TAG, "Symmetric encryption enabled!");
String passphrase = mEncryptInterface.getPassphrase();
if (passphrase.length() == 0) {
passphrase = null;
}
data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passphrase);
} else {
mSecretKeyIdToPass = mEncryptInterface.getSignatureKey();
encryptionKeyIds = mEncryptInterface.getEncryptionKeys();
signOnly = (mEncryptInterface.getEncryptionKeys() == null
|| mEncryptInterface.getEncryptionKeys().length == 0);
}
intent.setAction(KeychainIntentService.ACTION_ENCRYPT_SIGN);
// choose default settings, target and data bundle by target
compressionId = Preferences.getPreferences(getActivity()).getDefaultMessageCompression();
data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_BYTES);
String message = mMessage.getText().toString();
if (signOnly) {
message = fixBadCharactersForGmail(message);
}
data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, message.getBytes());
data.putLong(KeychainIntentService.ENCRYPT_SECRET_KEY_ID, mSecretKeyIdToPass);
data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, true);
data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, encryptionKeyIds);
data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, compressionId);
// data.putBoolean(KeychainIntentService.ENCRYPT_GENERATE_SIGNATURE, mGenerateSignature);
data.putBoolean(KeychainIntentService.ENCRYPT_SIGN_ONLY, signOnly);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
// Message is received after encrypting is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
// get returned data bundle
Bundle data = message.getData();
String output;
if (toClipboard) {
output = data.getString(KeychainIntentService.RESULT_ENCRYPTED_STRING);
Log.d(Constants.TAG, "output: " + output);
ClipboardReflection.copyToClipboard(getActivity(), output);
AppMsg.makeText(getActivity(),
R.string.encryption_to_clipboard_successful, AppMsg.STYLE_INFO)
.show();
} else {
output = data.getString(KeychainIntentService.RESULT_ENCRYPTED_STRING);
Log.d(Constants.TAG, "output: " + output);
Intent sendIntent = new Intent(Intent.ACTION_SEND);
// Type is set to text/plain so that encrypted messages can
// be sent with Whatsapp, Hangouts, SMS etc...
sendIntent.setType("text/plain");
sendIntent.putExtra(Intent.EXTRA_TEXT, output);
startActivity(Intent.createChooser(sendIntent,
getString(R.string.title_send_email)));
}
}
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
// show progress dialog
saveHandler.showProgressDialog(getActivity());
// start service with intent
getActivity().startService(intent);
}
/**
* Shows passphrase dialog to cache a new passphrase the user enters for using it later for
* encryption
*/
private void showPassphraseDialog(final boolean toClipboard) {
// Message is received after passphrase is cached
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
encryptStart(toClipboard);
}
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(returnHandler);
try {
PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(
getActivity(), messenger, mEncryptInterface.getSignatureKey());
passphraseDialog.show(getActivity().getSupportFragmentManager(), "passphraseDialog");
} catch (PgpGeneralException e) {
Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!");
// send message to handler to start encryption directly
returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
}
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import org.sufficientlysecure.keychain.R;
public class EncryptSymmetricFragment extends Fragment {
OnSymmetricKeySelection mPassphraseUpdateListener;
private EditText mPassphrase;
private EditText mPassphraseAgain;
// Container Activity must implement this interface
public interface OnSymmetricKeySelection {
public void onPassphraseUpdate(String passphrase);
public void onPassphraseAgainUpdate(String passphrase);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mPassphraseUpdateListener = (OnSymmetricKeySelection) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnSymmetricKeySelection");
}
}
/**
* Inflate the layout for this fragment
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.encrypt_symmetric_fragment, container, false);
mPassphrase = (EditText) view.findViewById(R.id.passphrase);
mPassphraseAgain = (EditText) view.findViewById(R.id.passphraseAgain);
mPassphrase.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
mPassphraseUpdateListener.onPassphraseUpdate(s.toString());
}
});
mPassphraseAgain.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
mPassphraseUpdateListener.onPassphraseAgainUpdate(s.toString());
}
});
return view;
}
}

View File

@ -13,8 +13,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="@+id/linearLayout">
android:orientation="horizontal">
<EditText
android:id="@+id/decrypt_file_filename"

View File

@ -34,7 +34,7 @@
android:layout_gravity="right"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/label_sign_user_id"
android:text="Alice"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
@ -44,7 +44,7 @@
android:layout_gravity="right"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/label_sign_email"
android:text="alice@example.com"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
</LinearLayout>

View File

@ -1,318 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
xmlns:custom="http://schemas.android.com/apk/res-auto"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content_frame"
android:layout_marginLeft="@dimen/drawer_content_padding"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
android:orientation="vertical">
<LinearLayout
<android.support.v4.view.ViewPager
android:id="@+id/encrypt_pager_mode"
android:layout_width="match_parent"
android:layout_height="150dp">
<android.support.v4.view.PagerTabStrip
android:id="@+id/encrypt_pager_tab_strip_mode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="10dp"
android:paddingRight="10dp">
android:layout_gravity="top"
android:background="@color/emphasis"
android:textColor="#fff" />
</android.support.v4.view.ViewPager>
<LinearLayout
<android.support.v4.view.ViewPager
android:id="@+id/encrypt_pager_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.PagerTabStrip
android:id="@+id/encrypt_pager_tab_strip_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="4dp">
<ImageView
android:id="@+id/modePrevious"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_previous"/>
<TextView
android:id="@+id/modeLabel"
style="@style/SectionHeader"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_horizontal|center_vertical"
android:text="@string/label_asymmetric"/>
<ImageView
android:id="@+id/modeNext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_next"/>
</LinearLayout>
<ViewFlipper
android:id="@+id/mode"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/modeAsymmetric"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:id="@+id/sign"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/label_sign"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="4dip">
<TextView
android:id="@+id/mainUserId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/label_sign_user_id"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<TextView
android:id="@+id/mainUserIdRest"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/label_sign_email"
android:textAppearance="?android:attr/textAppearanceSmall"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="3dip">
<TextView
android:id="@+id/label_selectPublicKeys"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:text="@string/label_select_public_keys"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/btn_selectEncryptKeys"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="4dp"
android:text="@string/btn_select_encrypt_keys"
bootstrapbutton:bb_icon_left="fa-user"
bootstrapbutton:bb_size="default"
bootstrapbutton:bb_type="default"/>
</LinearLayout>
</LinearLayout>
<TableLayout
android:id="@+id/modeSymmetric"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="4dp"
android:stretchColumns="1">
<TableRow>
<TextView
android:id="@+id/label_passphrase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"
android:text="@string/label_passphrase"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<EditText
android:id="@+id/passphrase"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"/>
</TableRow>
<TableRow>
<TextView
android:id="@+id/label_passphraseAgain"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"
android:text="@string/label_passphrase_again"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<EditText
android:id="@+id/passphraseAgain"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"/>
</TableRow>
</TableLayout>
</ViewFlipper>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="4dp">
<ImageView
android:id="@+id/sourcePrevious"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_previous"/>
<TextView
android:id="@+id/sourceLabel"
style="@style/SectionHeader"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_horizontal|center_vertical"
android:text="@string/label_message"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<ImageView
android:id="@+id/sourceNext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_next"/>
</LinearLayout>
<ViewFlipper
android:id="@+id/source"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1">
<LinearLayout
android:id="@+id/sourceMessage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="4dp">
<EditText
android:id="@+id/message"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="top"
android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
android:hint="@string/encrypt_content_edit_text_hint"/>
</LinearLayout>
<LinearLayout
android:id="@+id/sourceFile"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/filename"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="top|left"
android:inputType="textMultiLine|textUri"
android:lines="4"
android:maxLines="10"
android:minLines="2"
android:scrollbars="vertical"/>
<com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/btn_browse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="4dp"
bootstrapbutton:bb_icon_left="fa-folder-open"
bootstrapbutton:bb_roundedCorners="true"
bootstrapbutton:bb_size="default"
bootstrapbutton:bb_type="default"/>
</LinearLayout>
<org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
custom:foldedLabel="@string/btn_encryption_advanced_settings_show"
custom:unFoldedLabel="@string/btn_encryption_advanced_settings_hide"
custom:foldedIcon="fa-chevron-right"
custom:unFoldedIcon="fa-chevron-down">
<include layout="@layout/encrypt_content_adv_settings"/>
</org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout>
android:layout_gravity="top"
android:background="@color/emphasis"
android:textColor="#fff" />
</android.support.v4.view.ViewPager>
</LinearLayout>
</ViewFlipper>
<TextView
style="@style/SectionHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="@string/section_encrypt_and_or_sign"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="4dp">
<com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/action_encrypt_share"
android:layout_width="match_parent"
android:layout_height="60dp"
android:padding="4dp"
android:layout_weight="1"
android:text="@string/btn_share"
bootstrapbutton:bb_icon_left="fa-share-square"
bootstrapbutton:bb_type="info"/>
<com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/action_encrypt_clipboard"
android:layout_width="match_parent"
android:layout_height="60dp"
android:padding="4dp"
android:layout_weight="1"
android:text="@string/btn_clipboard"
bootstrapbutton:bb_icon_left="fa-clipboard"
bootstrapbutton:bb_type="info"/>
<com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/action_encrypt_file"
android:layout_width="match_parent"
android:layout_height="60dp"
android:padding="4dp"
android:visibility="gone"
android:text="@string/btn_encrypt_file"
bootstrapbutton:bb_icon_left="fa-lock"
bootstrapbutton:bb_type="info"/>
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@ -59,7 +59,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/section_encrypt_and_or_sign"
android:layout_above="@+id/decrypt_file_action_decrypt"
android:layout_above="@+id/action_encrypt_file"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
@ -68,10 +68,12 @@
android:layout_width="match_parent"
android:layout_height="60dp"
android:padding="4dp"
android:visibility="gone"
android:text="@string/btn_encrypt_file"
bootstrapbutton:bb_icon_left="fa-lock"
bootstrapbutton:bb_type="info" />
bootstrapbutton:bb_type="info"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
</RelativeLayout>
</LinearLayout>

View File

@ -135,8 +135,6 @@
<string name="label_name">Name</string>
<string name="label_comment">Comment</string>
<string name="label_email">Email</string>
<string name="label_sign_user_id">Sign User Id</string>
<string name="label_sign_email">Sign email</string>
<string name="label_send_key">Upload key to selected keyserver after certification</string>
<string name="label_fingerprint">Fingerprint</string>
<string name="select_keys_button_default">Select</string>