Merge branch 'development' of github.com:open-keychain/open-keychain into development

This commit is contained in:
Dominik Schürmann 2015-04-22 19:00:47 +02:00
commit 702c28854b
8 changed files with 449 additions and 30 deletions

View File

@ -202,7 +202,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
} catch (IOException e) { } catch (IOException e) {
Notify.create(getActivity(), Notify.create(getActivity(),
getActivity().getString(R.string.error_file_added_already, FileHelper.getFilename(getActivity(), inputUri)), getActivity().getString(R.string.error_file_added_already, FileHelper.getFilename(getActivity(), inputUri)),
Notify.Style.ERROR).show(); Notify.Style.ERROR).show(this);
return; return;
} }
mSelectedFiles.requestFocus(); mSelectedFiles.requestFocus();
@ -230,7 +230,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
private void encryptClicked(boolean share) { private void encryptClicked(boolean share) {
if (mFilesModels.isEmpty()) { if (mFilesModels.isEmpty()) {
Notify.create(getActivity(), R.string.error_no_file_selected, Notify.create(getActivity(), R.string.error_no_file_selected,
Notify.Style.ERROR).show(); Notify.Style.ERROR).show(this);
return; return;
} }
if (share) { if (share) {
@ -247,7 +247,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
} else { } else {
if (mFilesModels.size() > 1) { if (mFilesModels.size() > 1) {
Notify.create(getActivity(), R.string.error_multi_not_supported, Notify.create(getActivity(), R.string.error_multi_not_supported,
Notify.Style.ERROR).show(); Notify.Style.ERROR).show(this);
return; return;
} }
showOutputFileDialog(); showOutputFileDialog();
@ -330,7 +330,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
if (mFilesModels.isEmpty()) { if (mFilesModels.isEmpty()) {
Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR) Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR)
.show(); .show(this);
return false; return false;
} else if (mFilesModels.size() > 1 && !mShareAfterEncrypt) { } else if (mFilesModels.size() > 1 && !mShareAfterEncrypt) {
Log.e(Constants.TAG, "Aborting: mInputUris.size() > 1 && !mShareAfterEncrypt"); Log.e(Constants.TAG, "Aborting: mInputUris.size() > 1 && !mShareAfterEncrypt");
@ -347,12 +347,12 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
if (mPassphrase == null) { if (mPassphrase == null) {
Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR) Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR)
.show(); .show(this);
return false; return false;
} }
if (mPassphrase.isEmpty()) { if (mPassphrase.isEmpty()) {
Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR) Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR)
.show(); .show(this);
return false; return false;
} }
@ -365,7 +365,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment {
// Files must be encrypted, only text can be signed-only right now // Files must be encrypted, only text can be signed-only right now
if (!gotEncryptionKeys) { if (!gotEncryptionKeys) {
Notify.create(getActivity(), R.string.select_encryption_key, Notify.Style.ERROR) Notify.create(getActivity(), R.string.select_encryption_key, Notify.Style.ERROR)
.show(); .show(this);
return false; return false;
} }
} }

View File

@ -288,9 +288,9 @@ public class EncryptTextFragment extends CryptoOperationFragment {
} }
protected boolean inputIsValid() { protected boolean inputIsValid() {
if (mMessage == null) { if (mMessage == null || mMessage.isEmpty()) {
Notify.create(getActivity(), R.string.error_message, Notify.Style.ERROR) Notify.create(getActivity(), R.string.error_empty_text, Notify.Style.ERROR)
.show(); .show(this);
return false; return false;
} }
@ -299,12 +299,12 @@ public class EncryptTextFragment extends CryptoOperationFragment {
if (mSymmetricPassphrase == null) { if (mSymmetricPassphrase == null) {
Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR) Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR)
.show(); .show(this);
return false; return false;
} }
if (mSymmetricPassphrase.isEmpty()) { if (mSymmetricPassphrase.isEmpty()) {
Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR) Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR)
.show(); .show(this);
return false; return false;
} }
@ -316,7 +316,7 @@ public class EncryptTextFragment extends CryptoOperationFragment {
if (!gotEncryptionKeys && mSigningKeyId == 0) { if (!gotEncryptionKeys && mSigningKeyId == 0) {
Notify.create(getActivity(), R.string.select_encryption_or_signature_key, Notify.Style.ERROR) Notify.create(getActivity(), R.string.select_encryption_or_signature_key, Notify.Style.ERROR)
.show(); .show(this);
return false; return false;
} }
} }

View File

@ -20,6 +20,9 @@ package org.sufficientlysecure.keychain.ui;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
@ -28,6 +31,8 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.base.BaseActivity; import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.ui.dialog.AddKeyserverDialogFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.widget.Editor; import org.sufficientlysecure.keychain.ui.widget.Editor;
import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener; import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
import org.sufficientlysecure.keychain.ui.widget.KeyServerEditor; import org.sufficientlysecure.keychain.ui.widget.KeyServerEditor;
@ -124,10 +129,63 @@ public class SettingsKeyServerActivity extends BaseActivity implements OnClickLi
} }
// button to add keyserver clicked
public void onClick(View v) { public void onClick(View v) {
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
Bundle data = message.getData();
switch (message.what) {
case AddKeyserverDialogFragment.MESSAGE_OKAY: {
boolean verified = data.getBoolean(AddKeyserverDialogFragment.MESSAGE_VERIFIED);
if (verified) {
Notify.create(SettingsKeyServerActivity.this,
R.string.add_keyserver_verified, Notify.Style.OK).show();
} else {
Notify.create(SettingsKeyServerActivity.this,
R.string.add_keyserver_without_verification,
Notify.Style.WARN).show();
}
String keyserver = data.getString(AddKeyserverDialogFragment.MESSAGE_KEYSERVER);
addKeyserver(keyserver);
break;
}
case AddKeyserverDialogFragment.MESSAGE_VERIFICATION_FAILED: {
AddKeyserverDialogFragment.FailureReason failureReason =
(AddKeyserverDialogFragment.FailureReason) data.getSerializable(
AddKeyserverDialogFragment.MESSAGE_FAILURE_REASON);
switch (failureReason) {
case CONNECTION_FAILED: {
Notify.create(SettingsKeyServerActivity.this,
R.string.add_keyserver_connection_failed,
Notify.Style.ERROR).show();
break;
}
case INVALID_URL: {
Notify.create(SettingsKeyServerActivity.this,
R.string.add_keyserver_invalid_url,
Notify.Style.ERROR).show();
break;
}
}
break;
}
}
}
};
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(returnHandler);
AddKeyserverDialogFragment dialogFragment = AddKeyserverDialogFragment
.newInstance(messenger, R.string.add_keyserver_dialog_title);
dialogFragment.show(getSupportFragmentManager(), "addKeyserverDialog");
}
public void addKeyserver(String keyserverUrl) {
KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor, KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor,
mEditors, false); mEditors, false);
view.setEditorListener(this); view.setEditorListener(this);
view.setValue(keyserverUrl);
mEditors.addView(view); mEditors.addView(view);
} }

View File

@ -0,0 +1,320 @@
/*
* Copyright (C) 2012-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.dialog;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
import android.test.suitebuilder.TestSuiteBuilder;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.TlsHelper;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
public class AddKeyserverDialogFragment extends DialogFragment implements OnEditorActionListener {
private static final String ARG_MESSENGER = "messenger";
private static final String ARG_TITLE = "title";
public static final int MESSAGE_OKAY = 1;
public static final int MESSAGE_VERIFICATION_FAILED = 2;
public static final String MESSAGE_KEYSERVER = "new_keyserver";
public static final String MESSAGE_VERIFIED = "verified";
public static final String MESSAGE_FAILURE_REASON = "failure_reason";
private Messenger mMessenger;
private EditText mKeyserverEditText;
private CheckBox mVerifyKeyserverCheckBox;
public static enum FailureReason {
INVALID_URL,
CONNECTION_FAILED
}
;
/**
* Creates new instance of this dialog fragment
*
* @param title title of dialog
* @param messenger to communicate back after setting the passphrase
* @return
*/
public static AddKeyserverDialogFragment newInstance(Messenger messenger, int title) {
AddKeyserverDialogFragment frag = new AddKeyserverDialogFragment();
Bundle args = new Bundle();
args.putInt(ARG_TITLE, title);
args.putParcelable(ARG_MESSENGER, messenger);
frag.setArguments(args);
return frag;
}
/**
* Creates dialog
*/
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity();
int title = getArguments().getInt(ARG_TITLE);
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
alert.setTitle(title);
LayoutInflater inflater = activity.getLayoutInflater();
View view = inflater.inflate(R.layout.add_keyserver_dialog, null);
alert.setView(view);
mKeyserverEditText = (EditText) view.findViewById(R.id.keyserver_url_edit_text);
mVerifyKeyserverCheckBox = (CheckBox) view.findViewById(R.id.verify_keyserver_checkbox);
// we don't want dialog to be dismissed on click, thereby requiring the hack seen below
// and in onStart
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
// we need to have an empty listener to prevent errors on some devices as mentioned
// at http://stackoverflow.com/q/13746412/3000919
// actual listener set in onStart
}
});
alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dismiss();
}
});
// Hack to open keyboard.
// This is the only method that I found to work across all Android versions
// http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/
// Notes: * onCreateView can't be used because we want to add buttons to the dialog
// * opening in onActivityCreated does not work on Android 4.4
mKeyserverEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
mKeyserverEditText.post(new Runnable() {
@Override
public void run() {
InputMethodManager imm = (InputMethodManager) getActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(mKeyserverEditText, InputMethodManager.SHOW_IMPLICIT);
}
});
}
});
mKeyserverEditText.requestFocus();
mKeyserverEditText.setImeActionLabel(getString(android.R.string.ok),
EditorInfo.IME_ACTION_DONE);
mKeyserverEditText.setOnEditorActionListener(this);
return alert.show();
}
@Override
public void onStart() {
super.onStart();
AlertDialog addKeyserverDialog = (AlertDialog) getDialog();
if (addKeyserverDialog != null) {
Button positiveButton = addKeyserverDialog.getButton(Dialog.BUTTON_POSITIVE);
positiveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String keyserverUrl = mKeyserverEditText.getText().toString();
if (mVerifyKeyserverCheckBox.isChecked()) {
verifyConnection(keyserverUrl);
} else {
dismiss();
// return unverified keyserver back to activity
addKeyserver(keyserverUrl, false);
}
}
});
}
}
public void addKeyserver(String keyserver, boolean verified) {
dismiss();
Bundle data = new Bundle();
data.putString(MESSAGE_KEYSERVER, keyserver);
data.putBoolean(MESSAGE_VERIFIED, verified);
sendMessageToHandler(MESSAGE_OKAY, data);
}
public void verificationFailed(FailureReason reason) {
Bundle data = new Bundle();
data.putSerializable(MESSAGE_FAILURE_REASON, reason);
sendMessageToHandler(MESSAGE_VERIFICATION_FAILED, data);
}
public void verifyConnection(String keyserver) {
new AsyncTask<String, Void, FailureReason>() {
ProgressDialog mProgressDialog;
String mKeyserver;
@Override
protected void onPreExecute() {
mProgressDialog = new ProgressDialog(getActivity());
mProgressDialog.setMessage(getString(R.string.progress_verifying_keyserver_url));
mProgressDialog.setCancelable(false);
mProgressDialog.show();
}
@Override
protected FailureReason doInBackground(String... keyservers) {
mKeyserver = keyservers[0];
FailureReason reason = null;
try {
// replace hkps/hkp scheme and reconstruct Uri
Uri keyserverUri = Uri.parse(mKeyserver);
String scheme = keyserverUri.getScheme();
String schemeSpecificPart = keyserverUri.getSchemeSpecificPart();
String fragment = keyserverUri.getFragment();
if (scheme == null) throw new MalformedURLException();
if (scheme.equalsIgnoreCase("hkps")) scheme = "https";
else if (scheme.equalsIgnoreCase("hkp")) scheme = "http";
URI newKeyserver = new URI(scheme, schemeSpecificPart, fragment);
Log.d("Converted URL", newKeyserver.toString());
TlsHelper.openConnection(newKeyserver.toURL()).getInputStream();
} catch (TlsHelper.TlsHelperException e) {
reason = FailureReason.CONNECTION_FAILED;
} catch (MalformedURLException e) {
Log.w(Constants.TAG, "Invalid keyserver URL entered by user.");
reason = FailureReason.INVALID_URL;
} catch (URISyntaxException e) {
Log.w(Constants.TAG, "Invalid keyserver URL entered by user.");
reason = FailureReason.INVALID_URL;
} catch (IOException e) {
Log.w(Constants.TAG, "Could not connect to entered keyserver url");
reason = FailureReason.CONNECTION_FAILED;
}
return reason;
}
@Override
protected void onPostExecute(FailureReason failureReason) {
mProgressDialog.dismiss();
if (failureReason == null) {
addKeyserver(mKeyserver, true);
} else {
verificationFailed(failureReason);
}
}
}.execute(keyserver);
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
// hide keyboard on dismiss
hideKeyboard();
}
private void hideKeyboard() {
if (getActivity() == null) {
return;
}
InputMethodManager inputManager = (InputMethodManager) getActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE);
//check if no view has focus:
View v = getActivity().getCurrentFocus();
if (v == null)
return;
inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
/**
* Associate the "done" button on the soft keyboard with the okay button in the view
*/
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (EditorInfo.IME_ACTION_DONE == actionId) {
AlertDialog dialog = ((AlertDialog) getDialog());
Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
bt.performClick();
return true;
}
return false;
}
/**
* Send message back to handler which is initialized in a activity
*
* @param what Message integer you want to send
*/
private void sendMessageToHandler(Integer what, Bundle data) {
Message msg = Message.obtain();
msg.what = what;
if (data != null) {
msg.setData(data);
}
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
} catch (NullPointerException e) {
Log.w(Constants.TAG, "Messenger is null!", e);
}
}
}

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="16dp"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:paddingTop="16dp">
<EditText
android:id="@+id/keyserver_url_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="8dp"
android:ems="10"
android:hint="@string/label_enter_keyserver_url"
android:imeOptions="actionDone" />
<CheckBox
android:id="@+id/verify_keyserver_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_verify_keyserver"
android:checked="true"/>
</LinearLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
@ -12,4 +12,4 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
</LinearLayout> </RelativeLayout>

View File

@ -1,25 +1,24 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fillViewport="true"> android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="4dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:orientation="vertical">
<EditText <EditText
android:id="@+id/encrypt_text_text" android:id="@+id/encrypt_text_text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dip" android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:gravity="top" android:gravity="top"
android:inputType="text|textCapSentences|textMultiLine|textLongMessage" android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
android:hint="@string/encrypt_content_edit_text_hint" android:hint="@string/encrypt_content_edit_text_hint" />
android:layout_weight="1" />
</LinearLayout> </ScrollView>
</ScrollView>
</RelativeLayout>

View File

@ -86,6 +86,7 @@
<string name="btn_encrypt_text">"Encrypt text"</string> <string name="btn_encrypt_text">"Encrypt text"</string>
<string name="btn_add_email">"Add additional email address"</string> <string name="btn_add_email">"Add additional email address"</string>
<string name="btn_unlock">"Unlock"</string> <string name="btn_unlock">"Unlock"</string>
<string name="btn_add_keyserver">"Add"</string>
<!-- menu --> <!-- menu -->
<string name="menu_preferences">"Settings"</string> <string name="menu_preferences">"Settings"</string>
@ -152,6 +153,8 @@
<string name="label_enable_compression">"Enable compression"</string> <string name="label_enable_compression">"Enable compression"</string>
<string name="label_encrypt_filenames">"Encrypt filenames"</string> <string name="label_encrypt_filenames">"Encrypt filenames"</string>
<string name="label_hidden_recipients">"Hide recipients"</string> <string name="label_hidden_recipients">"Hide recipients"</string>
<string name="label_verify_keyserver">"Verify Keyserver"</string>
<string name="label_enter_keyserver_url">"Enter Keyserver URL"</string>
<string name="pref_keyserver">"Search Keyserver"</string> <string name="pref_keyserver">"Search Keyserver"</string>
<string name="pref_keyserver_summary">"Search HKP keyserver"</string> <string name="pref_keyserver_summary">"Search HKP keyserver"</string>
@ -353,6 +356,8 @@
<string name="progress_con_saving">"consolidate: saving to cache…"</string> <string name="progress_con_saving">"consolidate: saving to cache…"</string>
<string name="progress_con_reimport">"consolidate: reimporting…"</string> <string name="progress_con_reimport">"consolidate: reimporting…"</string>
<string name="progress_verifying_keyserver_url">"verifying keyserver…"</string>
<!-- action strings --> <!-- action strings -->
<string name="hint_cloud_search_hint">"Search via Name, Email…"</string> <string name="hint_cloud_search_hint">"Search via Name, Email…"</string>
@ -645,6 +650,13 @@
<string name="view_key_fragment_no_system_contact">"&lt;none&gt;"</string> <string name="view_key_fragment_no_system_contact">"&lt;none&gt;"</string>
<!-- Add keyserver -->
<string name="add_keyserver_dialog_title">"Add Keyserver"</string>
<string name="add_keyserver_verified">"Keyserver verified!"</string>
<string name="add_keyserver_without_verification">"Keyserver added without verification."</string>
<string name="add_keyserver_invalid_url">"Invalid URL!"</string>
<string name="add_keyserver_connection_failed">"Failed to connect to keyserver. Please check the URL and your internet connection."</string>
<!-- Navigation Drawer --> <!-- Navigation Drawer -->
<string name="nav_keys">"Keys"</string> <string name="nav_keys">"Keys"</string>
<string name="nav_encrypt_decrypt">"Encrypt/Decrypt"</string> <string name="nav_encrypt_decrypt">"Encrypt/Decrypt"</string>
@ -1226,6 +1238,7 @@
<string name="swipe_to_update">"Swipe down to update from keyserver"</string> <string name="swipe_to_update">"Swipe down to update from keyserver"</string>
<string name="error_no_file_selected">"Select at least one file to encrypt!"</string> <string name="error_no_file_selected">"Select at least one file to encrypt!"</string>
<string name="error_multi_not_supported">"Saving of multiple files not supported. This is a limitation on current Android."</string> <string name="error_multi_not_supported">"Saving of multiple files not supported. This is a limitation on current Android."</string>
<string name="error_empty_text">"Type some text to encrypt!"</string>
<string name="key_colon">"Key:"</string> <string name="key_colon">"Key:"</string>
<string name="exchange_description">"To start a key exchange, choose the number of participants on the right side, then hit the “Start exchange” button.\n\nYou will be asked two more questions to make sure only the right participants are in the exchange and their fingerprints are correct."</string> <string name="exchange_description">"To start a key exchange, choose the number of participants on the right side, then hit the “Start exchange” button.\n\nYou will be asked two more questions to make sure only the right participants are in the exchange and their fingerprints are correct."</string>
<string name="btn_start_exchange">"Start exchange"</string> <string name="btn_start_exchange">"Start exchange"</string>