Merge branch 'master' into ditch-appmsg

Conflicts:
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityOld.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
This commit is contained in:
Vincent Breitmoser
2014-07-26 23:06:32 +02:00
174 changed files with 5350 additions and 1267 deletions

View File

@@ -0,0 +1,189 @@
/*
* 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.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.support.v7.app.ActionBarActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Patterns;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ContactHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import java.util.regex.Matcher;
public class CreateKeyActivity extends ActionBarActivity {
AutoCompleteTextView mNameEdit;
AutoCompleteTextView mEmailEdit;
EditText mPassphraseEdit;
Button mCreateButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.create_key_activity);
mNameEdit = (AutoCompleteTextView) findViewById(R.id.name);
mEmailEdit = (AutoCompleteTextView) findViewById(R.id.email);
mPassphraseEdit = (EditText) findViewById(R.id.passphrase);
mCreateButton = (Button) findViewById(R.id.create_key_button);
mEmailEdit.setThreshold(1); // Start working from first character
mEmailEdit.setAdapter(
new ArrayAdapter<String>
(this, android.R.layout.simple_spinner_dropdown_item,
ContactHelper.getPossibleUserEmails(this)
)
);
mEmailEdit.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
@Override
public void afterTextChanged(Editable editable) {
String email = editable.toString();
if (email.length() > 0) {
Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email);
if (emailMatcher.matches()) {
mEmailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0,
R.drawable.uid_mail_ok, 0);
} else {
mEmailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0,
R.drawable.uid_mail_bad, 0);
}
} else {
// remove drawable if email is empty
mEmailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
}
});
mNameEdit.setThreshold(1); // Start working from first character
mNameEdit.setAdapter(
new ArrayAdapter<String>
(this, android.R.layout.simple_spinner_dropdown_item,
ContactHelper.getPossibleUserNames(this)
)
);
mCreateButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
createKeyCheck();
}
});
}
private void createKeyCheck() {
if (isEditTextNotEmpty(this, mNameEdit)
&& isEditTextNotEmpty(this, mEmailEdit)
&& isEditTextNotEmpty(this, mPassphraseEdit)) {
createKey();
}
}
private void createKey() {
Intent intent = new Intent(this, KeychainIntentService.class);
intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING);
// Message is received after importing is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
this,
getString(R.string.progress_importing),
ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
CreateKeyActivity.this.setResult(RESULT_OK);
CreateKeyActivity.this.finish();
}
}
};
// fill values for this action
Bundle data = new Bundle();
SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.CERTIFY_OTHER, null));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.SIGN_DATA, null));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, null));
String userId = mNameEdit.getText().toString() + " <" + mEmailEdit.getText().toString() + ">";
parcel.mAddUserIds.add(userId);
parcel.mNewPassphrase = mPassphraseEdit.getText().toString();
// get selected key entries
data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, parcel);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
saveHandler.showProgressDialog(this);
startService(intent);
}
/**
* Checks if text of given EditText is not empty. If it is empty an error is
* set and the EditText gets the focus.
*
* @param context
* @param editText
* @return true if EditText is not empty
*/
private static boolean isEditTextNotEmpty(Context context, EditText editText) {
boolean output = true;
if (editText.getText().toString().length() == 0) {
editText.setError("empty!");
editText.requestFocus();
output = false;
} else {
editText.setError(null);
}
return output;
}
}

View File

@@ -54,12 +54,15 @@ import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAddedAdapter;
import org.sufficientlysecure.keychain.ui.dialog.ChangeExpiryDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
import java.util.Date;
public class EditKeyFragment extends LoaderFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
@@ -214,10 +217,18 @@ public class EditKeyFragment extends LoaderFragment implements
mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mUserIdsAddedData);
mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter);
mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0);
mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0, mSaveKeyringParcel);
mSubkeysList.setAdapter(mSubkeysAdapter);
mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSaveKeyringParcel.addSubKeys);
mSubkeysList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
long keyId = mSubkeysAdapter.getKeyId(position);
editSubkey(keyId);
}
});
mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSaveKeyringParcel.mAddSubKeys);
mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter);
// Prepare the loaders. Either re-connect with an existing ones,
@@ -287,7 +298,7 @@ public class EditKeyFragment extends LoaderFragment implements
Bundle data = message.getData();
// cache new returned passphrase!
mSaveKeyringParcel.newPassphrase = data
mSaveKeyringParcel.mNewPassphrase = data
.getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE);
}
}
@@ -309,19 +320,19 @@ public class EditKeyFragment extends LoaderFragment implements
switch (message.what) {
case EditUserIdDialogFragment.MESSAGE_CHANGE_PRIMARY_USER_ID:
// toggle
if (mSaveKeyringParcel.changePrimaryUserId != null
&& mSaveKeyringParcel.changePrimaryUserId.equals(userId)) {
mSaveKeyringParcel.changePrimaryUserId = null;
if (mSaveKeyringParcel.mChangePrimaryUserId != null
&& mSaveKeyringParcel.mChangePrimaryUserId.equals(userId)) {
mSaveKeyringParcel.mChangePrimaryUserId = null;
} else {
mSaveKeyringParcel.changePrimaryUserId = userId;
mSaveKeyringParcel.mChangePrimaryUserId = userId;
}
break;
case EditUserIdDialogFragment.MESSAGE_REVOKE:
// toggle
if (mSaveKeyringParcel.revokeUserIds.contains(userId)) {
mSaveKeyringParcel.revokeUserIds.remove(userId);
if (mSaveKeyringParcel.mRevokeUserIds.contains(userId)) {
mSaveKeyringParcel.mRevokeUserIds.remove(userId);
} else {
mSaveKeyringParcel.revokeUserIds.add(userId);
mSaveKeyringParcel.mRevokeUserIds.add(userId);
}
break;
}
@@ -342,6 +353,72 @@ public class EditKeyFragment extends LoaderFragment implements
});
}
private void editSubkey(final long keyId) {
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case EditSubkeyDialogFragment.MESSAGE_CHANGE_EXPIRY:
editSubkeyExpiry(keyId);
break;
case EditSubkeyDialogFragment.MESSAGE_REVOKE:
// toggle
if (mSaveKeyringParcel.mRevokeSubKeys.contains(keyId)) {
mSaveKeyringParcel.mRevokeSubKeys.remove(keyId);
} else {
mSaveKeyringParcel.mRevokeSubKeys.add(keyId);
}
break;
}
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
}
};
// Create a new Messenger for the communication back
final Messenger messenger = new Messenger(returnHandler);
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
public void run() {
EditSubkeyDialogFragment dialogFragment =
EditSubkeyDialogFragment.newInstance(messenger);
dialogFragment.show(getActivity().getSupportFragmentManager(), "editSubkeyDialog");
}
});
}
private void editSubkeyExpiry(final long keyId) {
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case ChangeExpiryDialogFragment.MESSAGE_NEW_EXPIRY_DATE:
// toggle
// if (mSaveKeyringParcel.changePrimaryUserId != null
// && mSaveKeyringParcel.changePrimaryUserId.equals(userId)) {
// mSaveKeyringParcel.changePrimaryUserId = null;
// } else {
// mSaveKeyringParcel.changePrimaryUserId = userId;
// }
break;
}
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
}
};
// Create a new Messenger for the communication back
final Messenger messenger = new Messenger(returnHandler);
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
public void run() {
ChangeExpiryDialogFragment dialogFragment =
ChangeExpiryDialogFragment.newInstance(messenger, new Date(), new Date());
dialogFragment.show(getActivity().getSupportFragmentManager(), "editSubkeyExpiryDialog");
}
});
}
private void addUserId() {
mUserIdsAddedAdapter.add(new UserIdsAddedAdapter.UserIdModel());
}
@@ -373,10 +450,11 @@ public class EditKeyFragment extends LoaderFragment implements
}
private void save(String passphrase) {
Log.d(Constants.TAG, "add userids to parcel: " + mUserIdsAddedAdapter.getDataAsStringList());
Log.d(Constants.TAG, "mSaveKeyringParcel.newPassphrase: " + mSaveKeyringParcel.newPassphrase);
mSaveKeyringParcel.mAddUserIds = mUserIdsAddedAdapter.getDataAsStringList();
mSaveKeyringParcel.addUserIds = mUserIdsAddedAdapter.getDataAsStringList();
Log.d(Constants.TAG, "mSaveKeyringParcel.mAddUserIds: " + mSaveKeyringParcel.mAddUserIds);
Log.d(Constants.TAG, "mSaveKeyringParcel.mNewPassphrase: " + mSaveKeyringParcel.mNewPassphrase);
Log.d(Constants.TAG, "mSaveKeyringParcel.mRevokeUserIds: " + mSaveKeyringParcel.mRevokeUserIds);
// Message is received after importing is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
@@ -432,4 +510,4 @@ public class EditKeyFragment extends LoaderFragment implements
// start service with intent
getActivity().startService(intent);
}
}
}

View File

@@ -198,7 +198,7 @@ public class EncryptAsymmetricFragment extends Fragment {
String[] userId;
try {
userId = mProviderHelper.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(mSecretKeyId)).getSplitPrimaryUserId();
KeyRings.buildUnifiedKeyRingUri(mSecretKeyId)).getSplitPrimaryUserIdWithFallback();
} catch (PgpGeneralException e) {
userId = null;
}

View File

@@ -0,0 +1,90 @@
/*
* 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.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
public class FirstTimeActivity extends ActionBarActivity {
Button mCreateKey;
Button mImportKey;
Button mSkipSetup;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.first_time_activity);
mCreateKey = (Button) findViewById(R.id.first_time_create_key);
mImportKey = (Button) findViewById(R.id.first_time_import_key);
mSkipSetup = (Button) findViewById(R.id.first_time_cancel);
mSkipSetup.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finishSetup();
}
});
mImportKey.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstTimeActivity.this, ImportKeysActivity.class);
intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN);
startActivityForResult(intent, 1);
}
});
mCreateKey.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstTimeActivity.this, CreateKeyActivity.class);
startActivityForResult(intent, 1);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
finishSetup();
}
}
private void finishSetup() {
Preferences prefs = Preferences.getPreferences(this);
prefs.setFirstTime(false);
Intent intent = new Intent(FirstTimeActivity.this, KeyListActivity.class);
startActivity(intent);
finish();
}
}

View File

@@ -17,25 +17,17 @@
package org.sufficientlysecure.keychain.ui;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.view.Menu;
import android.view.MenuItem;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Constants.choice.algorithm;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Notify;
@@ -49,6 +41,14 @@ public class KeyListActivity extends DrawerActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// if this is the first time show first time activity
Preferences prefs = Preferences.getPreferences(this);
if (prefs.isFirstTime()) {
startActivity(new Intent(this, FirstTimeActivity.class));
finish();
return;
}
mExportHelper = new ExportHelper(this);
setContentView(R.layout.key_list_activity);
@@ -62,9 +62,10 @@ public class KeyListActivity extends DrawerActivity {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.key_list, menu);
if(Constants.DEBUG) {
if (Constants.DEBUG) {
menu.findItem(R.id.menu_key_list_debug_read).setVisible(true);
menu.findItem(R.id.menu_key_list_debug_write).setVisible(true);
menu.findItem(R.id.menu_key_list_debug_first_time).setVisible(true);
}
return true;
@@ -81,10 +82,6 @@ public class KeyListActivity extends DrawerActivity {
createKey();
return true;
case R.id.menu_key_list_create_expert:
createKeyExpert();
return true;
case R.id.menu_key_list_export:
mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true);
return true;
@@ -94,7 +91,7 @@ public class KeyListActivity extends DrawerActivity {
KeychainDatabase.debugRead(this);
Notify.showNotify(this, "Restored Notify.Style backup", Notify.Style.INFO);
getContentResolver().notifyChange(KeychainContract.KeyRings.CONTENT_URI, null);
} catch(IOException e) {
} catch (IOException e) {
Log.e(Constants.TAG, "IO Error", e);
Notify.showNotify(this, "IO Notify.Style: " + e.getMessage(), Notify.Style.ERROR);
}
@@ -110,6 +107,12 @@ public class KeyListActivity extends DrawerActivity {
}
return true;
case R.id.menu_key_list_debug_first_time:
Intent intent = new Intent(this, FirstTimeActivity.class);
startActivity(intent);
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
@@ -121,50 +124,8 @@ public class KeyListActivity extends DrawerActivity {
}
private void createKey() {
Intent intent = new Intent(this, WizardActivity.class);
// intent.setAction(EditKeyActivity.ACTION_CREATE_KEY);
// intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true);
// intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, ""); // show user id view
startActivityForResult(intent, 0);
Intent intent = new Intent(this, CreateKeyActivity.class);
startActivity(intent);
}
private void createKeyExpert() {
Intent intent = new Intent(this, KeychainIntentService.class);
intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING);
// Message is received after importing is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
this,
getString(R.string.progress_importing),
ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
Bundle data = message.getData();
// OtherHelper.logDebugBundle(data, "message reply");
}
};
// fill values for this action
Bundle data = new Bundle();
SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.addSubKeys.add(new SubkeyAdd(algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
parcel.addSubKeys.add(new SubkeyAdd(algorithm.rsa, 1024, KeyFlags.SIGN_DATA, null));
parcel.addUserIds.add("swagerinho");
parcel.newPassphrase = "swag";
// get selected key entries
data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, parcel);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
saveHandler.showProgressDialog(this);
startService(intent);
}
}

View File

@@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class LogDisplayFragment extends ListFragment implements OnTouchListener {
@@ -135,7 +136,7 @@ public class LogDisplayFragment extends ListFragment implements OnTouchListener
private LayoutInflater mInflater;
private int dipFactor;
public LogAdapter(Context context, ArrayList<LogEntryParcel> log, LogLevel level) {
public LogAdapter(Context context, OperationResultParcel.OperationLog log, LogLevel level) {
super(context, R.layout.log_display_item);
mInflater = LayoutInflater.from(getContext());
dipFactor = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,

View File

@@ -121,6 +121,9 @@ public class PreferencesActivity extends PreferenceActivity {
initializeForceV3Signatures(
(CheckBoxPreference) findPreference(Constants.Pref.FORCE_V3_SIGNATURES));
initializeConcealPgpApplication(
(CheckBoxPreference) findPreference(Constants.Pref.CONCEAL_PGP_APPLICATION));
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
// Load the legacy preferences headers
addPreferencesFromResource(R.xml.preference_headers_legacy);
@@ -264,6 +267,9 @@ public class PreferencesActivity extends PreferenceActivity {
initializeForceV3Signatures(
(CheckBoxPreference) findPreference(Constants.Pref.FORCE_V3_SIGNATURES));
initializeConcealPgpApplication(
(CheckBoxPreference) findPreference(Constants.Pref.CONCEAL_PGP_APPLICATION));
}
}
@@ -396,4 +402,15 @@ public class PreferencesActivity extends PreferenceActivity {
}
});
}
private static void initializeConcealPgpApplication(final CheckBoxPreference mConcealPgpApplication) {
mConcealPgpApplication.setChecked(sPreferences.getConcealPgpApplication());
mConcealPgpApplication.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
mConcealPgpApplication.setChecked((Boolean) newValue);
sPreferences.setConcealPgpApplication((Boolean) newValue);
return false;
}
});
}
}

View File

@@ -149,8 +149,8 @@ public class ViewCertActivity extends ActionBarActivity
providerHelper.getWrappedPublicKeyRing(sig.getKeyId());
try {
sig.init(signerRing.getSubkey());
if (sig.verifySignature(signeeRing.getSubkey(), signeeUid)) {
sig.init(signerRing.getPublicKey());
if (sig.verifySignature(signeeRing.getPublicKey(), signeeUid)) {
mStatus.setText(R.string.cert_verify_ok);
mStatus.setTextColor(getResources().getColor(R.color.result_green));
} else {

View File

@@ -1,466 +0,0 @@
/*
* 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.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBarActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Patterns;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RadioGroup;
import android.widget.TextView;
import org.sufficientlysecure.htmltextview.HtmlTextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ContactHelper;
import org.sufficientlysecure.keychain.util.Log;
import java.util.regex.Matcher;
public class WizardActivity extends ActionBarActivity {
private State mCurrentState;
// values for mCurrentScreen
private enum State {
START, CREATE_KEY, IMPORT_KEY, K9
}
public static final int REQUEST_CODE_IMPORT = 0x00007703;
Button mBackButton;
Button mNextButton;
StartFragment mStartFragment;
CreateKeyFragment mCreateKeyFragment;
K9Fragment mK9Fragment;
private static final String K9_PACKAGE = "com.fsck.k9";
// private static final String K9_MARKET_INTENT_URI_BASE = "market://details?id=%s";
// private static final Intent K9_MARKET_INTENT = new Intent(Intent.ACTION_VIEW, Uri.parse(
// String.format(K9_MARKET_INTENT_URI_BASE, K9_PACKAGE)));
private static final Intent K9_MARKET_INTENT = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/k9mail/k-9/releases/tag/4.904"));
LinearLayout mProgressLayout;
View mProgressLine;
ProgressBar mProgressBar;
ImageView mProgressImage;
TextView mProgressText;
/**
* Checks if text of given EditText is not empty. If it is empty an error is
* set and the EditText gets the focus.
*
* @param context
* @param editText
* @return true if EditText is not empty
*/
private static boolean isEditTextNotEmpty(Context context, EditText editText) {
boolean output = true;
if (editText.getText().toString().length() == 0) {
editText.setError("empty!");
editText.requestFocus();
output = false;
} else {
editText.setError(null);
}
return output;
}
public static class StartFragment extends Fragment {
public static StartFragment newInstance() {
StartFragment myFragment = new StartFragment();
Bundle args = new Bundle();
myFragment.setArguments(args);
return myFragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.wizard_start_fragment,
container, false);
}
}
public static class CreateKeyFragment extends Fragment {
public static CreateKeyFragment newInstance() {
CreateKeyFragment myFragment = new CreateKeyFragment();
Bundle args = new Bundle();
myFragment.setArguments(args);
return myFragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.wizard_create_key_fragment,
container, false);
final AutoCompleteTextView emailView = (AutoCompleteTextView) view.findViewById(R.id.email);
emailView.setThreshold(1); // Start working from first character
emailView.setAdapter(
new ArrayAdapter<String>
(getActivity(), android.R.layout.simple_spinner_dropdown_item,
ContactHelper.getPossibleUserEmails(getActivity())
)
);
emailView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
@Override
public void afterTextChanged(Editable editable) {
String email = editable.toString();
if (email.length() > 0) {
Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email);
if (emailMatcher.matches()) {
emailView.setCompoundDrawablesWithIntrinsicBounds(0, 0,
R.drawable.uid_mail_ok, 0);
} else {
emailView.setCompoundDrawablesWithIntrinsicBounds(0, 0,
R.drawable.uid_mail_bad, 0);
}
} else {
// remove drawable if email is empty
emailView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
}
});
final AutoCompleteTextView nameView = (AutoCompleteTextView) view.findViewById(R.id.name);
nameView.setThreshold(1); // Start working from first character
nameView.setAdapter(
new ArrayAdapter<String>
(getActivity(), android.R.layout.simple_spinner_dropdown_item,
ContactHelper.getPossibleUserNames(getActivity())
)
);
return view;
}
}
public static class K9Fragment extends Fragment {
public static K9Fragment newInstance() {
K9Fragment myFragment = new K9Fragment();
Bundle args = new Bundle();
myFragment.setArguments(args);
return myFragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.wizard_k9_fragment,
container, false);
HtmlTextView text = (HtmlTextView) v
.findViewById(R.id.wizard_k9_text);
text.setHtmlFromString("Install K9. It's good for you! Here is a screenhot how to enable OK in K9: (TODO)", true);
return v;
}
}
/**
* Loads new fragment
*
* @param fragment
*/
private void loadFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
fragmentTransaction.replace(R.id.wizard_container,
fragment);
fragmentTransaction.commit();
}
/**
* Instantiate View and initialize fragments for this Activity
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.wizard_activity);
mBackButton = (Button) findViewById(R.id.wizard_back);
mNextButton = (Button) findViewById(R.id.wizard_next);
// progress layout
mProgressLayout = (LinearLayout) findViewById(R.id.wizard_progress);
mProgressLine = findViewById(R.id.wizard_progress_line);
mProgressBar = (ProgressBar) findViewById(R.id.wizard_progress_progressbar);
mProgressImage = (ImageView) findViewById(R.id.wizard_progress_image);
mProgressText = (TextView) findViewById(R.id.wizard_progress_text);
changeToState(State.START);
}
private enum ProgressState {
WORKING, ENABLED, DISABLED, ERROR
}
private void showProgress(ProgressState state, String text) {
switch (state) {
case WORKING:
mProgressBar.setVisibility(View.VISIBLE);
mProgressImage.setVisibility(View.GONE);
break;
case ENABLED:
mProgressBar.setVisibility(View.GONE);
mProgressImage.setVisibility(View.VISIBLE);
// mProgressImage.setImageDrawable(getResources().getDrawable(
// R.drawable.status_enabled));
break;
case DISABLED:
mProgressBar.setVisibility(View.GONE);
mProgressImage.setVisibility(View.VISIBLE);
// mProgressImage.setImageDrawable(getResources().getDrawable(
// R.drawable.status_disabled));
break;
case ERROR:
mProgressBar.setVisibility(View.GONE);
mProgressImage.setVisibility(View.VISIBLE);
// mProgressImage.setImageDrawable(getResources().getDrawable(
// R.drawable.status_fail));
break;
default:
break;
}
mProgressText.setText(text);
mProgressLine.setVisibility(View.VISIBLE);
mProgressLayout.setVisibility(View.VISIBLE);
}
private void hideProgress() {
mProgressLine.setVisibility(View.GONE);
mProgressLayout.setVisibility(View.GONE);
}
public void nextOnClick(View view) {
// close keyboard
if (getCurrentFocus() != null) {
InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.hideSoftInputFromWindow(getCurrentFocus()
.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
switch (mCurrentState) {
case START: {
RadioGroup radioGroup = (RadioGroup) findViewById(R.id.wizard_start_radio_group);
int selectedId = radioGroup.getCheckedRadioButtonId();
switch (selectedId) {
case R.id.wizard_start_new_key: {
changeToState(State.CREATE_KEY);
break;
}
case R.id.wizard_start_import: {
changeToState(State.IMPORT_KEY);
break;
}
case R.id.wizard_start_skip: {
finish();
break;
}
}
mBackButton.setText(R.string.btn_back);
break;
}
case CREATE_KEY:
EditText nameEdit = (EditText) findViewById(R.id.name);
EditText emailEdit = (EditText) findViewById(R.id.email);
EditText passphraseEdit = (EditText) findViewById(R.id.passphrase);
if (isEditTextNotEmpty(this, nameEdit)
&& isEditTextNotEmpty(this, emailEdit)
&& isEditTextNotEmpty(this, passphraseEdit)) {
// SaveKeyringParcel newKey = new SaveKeyringParcel();
// newKey.addUserIds.add(nameEdit.getText().toString() + " <"
// + emailEdit.getText().toString() + ">");
AsyncTask<String, Boolean, Boolean> generateTask = new AsyncTask<String, Boolean, Boolean>() {
@Override
protected void onPreExecute() {
super.onPreExecute();
showProgress(ProgressState.WORKING, "generating key...");
}
@Override
protected Boolean doInBackground(String... params) {
return true;
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (result) {
showProgress(ProgressState.ENABLED, "key generated successfully!");
changeToState(State.K9);
} else {
showProgress(ProgressState.ERROR, "error in key gen");
}
}
};
generateTask.execute("");
}
break;
case K9: {
RadioGroup radioGroup = (RadioGroup) findViewById(R.id.wizard_k9_radio_group);
int selectedId = radioGroup.getCheckedRadioButtonId();
switch (selectedId) {
case R.id.wizard_k9_install: {
try {
startActivity(K9_MARKET_INTENT);
} catch (ActivityNotFoundException e) {
Log.e(Constants.TAG, "Activity not found for: " + K9_MARKET_INTENT);
}
break;
}
case R.id.wizard_k9_skip: {
finish();
break;
}
}
finish();
break;
}
default:
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_CODE_IMPORT: {
if (resultCode == Activity.RESULT_OK) {
// imported now...
changeToState(State.K9);
} else {
// back to start
changeToState(State.START);
}
break;
}
default: {
super.onActivityResult(requestCode, resultCode, data);
break;
}
}
}
public void backOnClick(View view) {
switch (mCurrentState) {
case START:
finish();
break;
case CREATE_KEY:
changeToState(State.START);
break;
case IMPORT_KEY:
changeToState(State.START);
break;
default:
changeToState(State.START);
break;
}
}
private void changeToState(State state) {
switch (state) {
case START: {
mCurrentState = State.START;
mStartFragment = StartFragment.newInstance();
loadFragment(mStartFragment);
mBackButton.setText(android.R.string.cancel);
mNextButton.setText(R.string.btn_next);
break;
}
case CREATE_KEY: {
mCurrentState = State.CREATE_KEY;
mCreateKeyFragment = CreateKeyFragment.newInstance();
loadFragment(mCreateKeyFragment);
break;
}
case IMPORT_KEY: {
mCurrentState = State.IMPORT_KEY;
Intent intent = new Intent(this, ImportKeysActivity.class);
intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN);
startActivityForResult(intent, REQUEST_CODE_IMPORT);
break;
}
case K9: {
mCurrentState = State.K9;
mBackButton.setEnabled(false); // don't go back to import/create key
mK9Fragment = K9Fragment.newInstance();
loadFragment(mK9Fragment);
break;
}
}
}
}

View File

@@ -32,14 +32,15 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import java.util.Date;
public class SubkeysAdapter extends CursorAdapter {
private LayoutInflater mInflater;
private SaveKeyringParcel mSaveKeyringParcel;
private boolean hasAnySecret;
private ColorStateList mDefaultTextColor;
public static final String[] SUBKEYS_PROJECTION = new String[]{
@@ -71,10 +72,21 @@ public class SubkeysAdapter extends CursorAdapter {
private static final int INDEX_EXPIRY = 11;
private static final int INDEX_FINGERPRINT = 12;
public SubkeysAdapter(Context context, Cursor c, int flags) {
public SubkeysAdapter(Context context, Cursor c, int flags,
SaveKeyringParcel saveKeyringParcel) {
super(context, c, flags);
mInflater = LayoutInflater.from(context);
mSaveKeyringParcel = saveKeyringParcel;
}
public SubkeysAdapter(Context context, Cursor c, int flags) {
this(context, c, flags, null);
}
public long getKeyId(int position) {
mCursor.moveToPosition(position);
return mCursor.getLong(INDEX_KEY_ID);
}
@Override
@@ -94,79 +106,94 @@ public class SubkeysAdapter extends CursorAdapter {
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView keyId = (TextView) view.findViewById(R.id.keyId);
TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
TextView keyExpiry = (TextView) view.findViewById(R.id.keyExpiry);
ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey);
ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
ImageView revokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey);
TextView vKeyId = (TextView) view.findViewById(R.id.keyId);
TextView vKeyDetails = (TextView) view.findViewById(R.id.keyDetails);
TextView vKeyExpiry = (TextView) view.findViewById(R.id.keyExpiry);
ImageView vMasterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey);
ImageView vCertifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
ImageView vEncryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
ImageView vSignIcon = (ImageView) view.findViewById(R.id.ic_signKey);
ImageView vRevokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey);
ImageView vEditImage = (ImageView) view.findViewById(R.id.edit_image);
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(INDEX_KEY_ID));
long keyId = cursor.getLong(INDEX_KEY_ID);
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId);
String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
context,
cursor.getInt(INDEX_ALGORITHM),
cursor.getInt(INDEX_KEY_SIZE)
);
keyId.setText(keyIdStr);
vKeyId.setText(keyIdStr);
// may be set with additional "stripped" later on
if (hasAnySecret && cursor.getInt(INDEX_HAS_SECRET) == 0) {
keyDetails.setText(algorithmStr + ", " +
vKeyDetails.setText(algorithmStr + ", " +
context.getString(R.string.key_stripped));
} else {
keyDetails.setText(algorithmStr);
vKeyDetails.setText(algorithmStr);
}
// Set icons according to properties
masterKeyIcon.setVisibility(cursor.getInt(INDEX_RANK) == 0 ? View.VISIBLE : View.INVISIBLE);
certifyIcon.setVisibility(cursor.getInt(INDEX_CAN_CERTIFY) != 0 ? View.VISIBLE : View.GONE);
encryptIcon.setVisibility(cursor.getInt(INDEX_CAN_ENCRYPT) != 0 ? View.VISIBLE : View.GONE);
signIcon.setVisibility(cursor.getInt(INDEX_CAN_SIGN) != 0 ? View.VISIBLE : View.GONE);
vMasterKeyIcon.setVisibility(cursor.getInt(INDEX_RANK) == 0 ? View.VISIBLE : View.INVISIBLE);
vCertifyIcon.setVisibility(cursor.getInt(INDEX_CAN_CERTIFY) != 0 ? View.VISIBLE : View.GONE);
vEncryptIcon.setVisibility(cursor.getInt(INDEX_CAN_ENCRYPT) != 0 ? View.VISIBLE : View.GONE);
vSignIcon.setVisibility(cursor.getInt(INDEX_CAN_SIGN) != 0 ? View.VISIBLE : View.GONE);
boolean valid = true;
if (cursor.getInt(INDEX_IS_REVOKED) > 0) {
revokedKeyIcon.setVisibility(View.VISIBLE);
boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
valid = false;
// for edit key
if (mSaveKeyringParcel != null) {
boolean revokeThisSubkey = (mSaveKeyringParcel.mRevokeSubKeys.contains(keyId));
if (revokeThisSubkey) {
if (!isRevoked) {
isRevoked = true;
}
}
vEditImage.setVisibility(View.VISIBLE);
} else {
keyId.setTextColor(mDefaultTextColor);
keyDetails.setTextColor(mDefaultTextColor);
keyExpiry.setTextColor(mDefaultTextColor);
revokedKeyIcon.setVisibility(View.GONE);
vEditImage.setVisibility(View.GONE);
}
if (isRevoked) {
vRevokedKeyIcon.setVisibility(View.VISIBLE);
} else {
vKeyId.setTextColor(mDefaultTextColor);
vKeyDetails.setTextColor(mDefaultTextColor);
vKeyExpiry.setTextColor(mDefaultTextColor);
vRevokedKeyIcon.setVisibility(View.GONE);
}
boolean isExpired;
if (!cursor.isNull(INDEX_EXPIRY)) {
Date expiryDate = new Date(cursor.getLong(INDEX_EXPIRY) * 1000);
isExpired = expiryDate.before(new Date());
valid = valid && expiryDate.after(new Date());
keyExpiry.setText(
context.getString(R.string.label_expiry) + ": " +
DateFormat.getDateFormat(context).format(expiryDate)
);
vKeyExpiry.setText(context.getString(R.string.label_expiry) + ": "
+ DateFormat.getDateFormat(context).format(expiryDate));
} else {
keyExpiry.setText(
context.getString(R.string.label_expiry) + ": " +
context.getString(R.string.none)
);
isExpired = false;
vKeyExpiry.setText(context.getString(R.string.label_expiry) + ": " + context.getString(R.string.none));
}
// if key is expired or revoked, strike through text
if (!valid) {
keyId.setText(OtherHelper.strikeOutText(keyId.getText()));
keyDetails.setText(OtherHelper.strikeOutText(keyDetails.getText()));
keyExpiry.setText(OtherHelper.strikeOutText(keyExpiry.getText()));
boolean isInvalid = isRevoked || isExpired;
if (isInvalid) {
vKeyId.setText(OtherHelper.strikeOutText(vKeyId.getText()));
vKeyDetails.setText(OtherHelper.strikeOutText(vKeyDetails.getText()));
vKeyExpiry.setText(OtherHelper.strikeOutText(vKeyExpiry.getText()));
}
keyId.setEnabled(valid);
keyDetails.setEnabled(valid);
keyExpiry.setEnabled(valid);
vKeyId.setEnabled(!isInvalid);
vKeyDetails.setEnabled(!isInvalid);
vKeyExpiry.setEnabled(!isInvalid);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = mInflater.inflate(R.layout.view_key_keys_item, null);
View view = mInflater.inflate(R.layout.view_key_subkey_item, null);
if (mDefaultTextColor == null) {
TextView keyId = (TextView) view.findViewById(R.id.keyId);
mDefaultTextColor = keyId.getTextColors();
@@ -177,13 +204,21 @@ public class SubkeysAdapter extends CursorAdapter {
// Disable selection of items, http://stackoverflow.com/a/4075045
@Override
public boolean areAllItemsEnabled() {
return false;
if (mSaveKeyringParcel == null) {
return false;
} else {
return super.areAllItemsEnabled();
}
}
// Disable selection of items, http://stackoverflow.com/a/4075045
@Override
public boolean isEnabled(int position) {
return false;
if (mSaveKeyringParcel == null) {
return false;
} else {
return super.isEnabled(position);
}
}
}

View File

@@ -40,9 +40,7 @@ import java.util.ArrayList;
public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemClickListener {
private LayoutInflater mInflater;
private final ArrayList<Boolean> mCheckStates;
private SaveKeyringParcel mSaveKeyringParcel;
public static final String[] USER_IDS_PROJECTION = new String[]{
@@ -60,7 +58,6 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
private static final int INDEX_IS_PRIMARY = 4;
private static final int INDEX_IS_REVOKED = 5;
public UserIdsAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes,
SaveKeyringParcel saveKeyringParcel) {
super(context, c, flags);
@@ -134,15 +131,18 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
// for edit key
if (mSaveKeyringParcel != null) {
boolean changeAnyPrimaryUserId = (mSaveKeyringParcel.changePrimaryUserId != null);
boolean changeThisPrimaryUserId = (mSaveKeyringParcel.changePrimaryUserId != null
&& mSaveKeyringParcel.changePrimaryUserId.equals(userId));
boolean revokeThisUserId = (mSaveKeyringParcel.revokeUserIds.contains(userId));
boolean changeAnyPrimaryUserId = (mSaveKeyringParcel.mChangePrimaryUserId != null);
boolean changeThisPrimaryUserId = (mSaveKeyringParcel.mChangePrimaryUserId != null
&& mSaveKeyringParcel.mChangePrimaryUserId.equals(userId));
boolean revokeThisUserId = (mSaveKeyringParcel.mRevokeUserIds.contains(userId));
// only if primary user id will be changed
// (this is not triggered if the user id is currently the primary one)
if (changeAnyPrimaryUserId) {
// change all user ids, only this one should be primary
// change _all_ primary user ids and set new one to true
isPrimary = changeThisPrimaryUserId;
}
if (revokeThisUserId) {
if (!isRevoked) {
isRevoked = true;
@@ -233,7 +233,7 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = mInflater.inflate(R.layout.view_key_userids_item, null);
View view = mInflater.inflate(R.layout.view_key_user_id_item, null);
// only need to do this once ever, since mShowCheckBoxes is final
view.findViewById(R.id.checkBox).setVisibility(mCheckStates != null ? View.VISIBLE : View.GONE);
return view;

View File

@@ -0,0 +1,187 @@
/*
* 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.dialog;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
import android.text.format.DateUtils;
import android.widget.DatePicker;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Log;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
public class ChangeExpiryDialogFragment extends DialogFragment {
private static final String ARG_MESSENGER = "messenger";
private static final String ARG_CREATION_DATE = "creation_date";
private static final String ARG_EXPIRY_DATE = "expiry_date";
public static final int MESSAGE_NEW_EXPIRY_DATE = 1;
public static final String MESSAGE_DATA_EXPIRY_DATE = "expiry_date";
private Messenger mMessenger;
private Calendar mCreationCal;
private Calendar mExpiryCal;
private int mDatePickerResultCount = 0;
private DatePickerDialog.OnDateSetListener mExpiryDateSetListener =
new DatePickerDialog.OnDateSetListener() {
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
// Note: Ignore results after the first one - android sends multiples.
if (mDatePickerResultCount++ == 0) {
Calendar selectedCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
selectedCal.set(year, monthOfYear, dayOfMonth);
if (mExpiryCal != null) {
long numDays = (selectedCal.getTimeInMillis() / 86400000)
- (mExpiryCal.getTimeInMillis() / 86400000);
if (numDays > 0) {
Bundle data = new Bundle();
data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, selectedCal.getTime());
sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data);
}
} else {
Bundle data = new Bundle();
data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, selectedCal.getTime());
sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data);
}
}
}
};
public class ExpiryDatePickerDialog extends DatePickerDialog {
public ExpiryDatePickerDialog(Context context, OnDateSetListener callBack,
int year, int monthOfYear, int dayOfMonth) {
super(context, callBack, year, monthOfYear, dayOfMonth);
}
// set permanent title
public void setTitle(CharSequence title) {
super.setTitle(getContext().getString(R.string.expiry_date_dialog_title));
}
}
/**
* Creates new instance of this dialog fragment
*/
public static ChangeExpiryDialogFragment newInstance(Messenger messenger,
Date creationDate, Date expiryDate) {
ChangeExpiryDialogFragment frag = new ChangeExpiryDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger);
args.putSerializable(ARG_CREATION_DATE, creationDate);
args.putSerializable(ARG_EXPIRY_DATE, expiryDate);
frag.setArguments(args);
return frag;
}
/**
* Creates dialog
*/
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
Date creationDate = (Date) getArguments().getSerializable(ARG_CREATION_DATE);
Date expiryDate = (Date) getArguments().getSerializable(ARG_EXPIRY_DATE);
mCreationCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
mCreationCal.setTime(creationDate);
mExpiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
mExpiryCal.setTime(expiryDate);
/*
* Using custom DatePickerDialog which overrides the setTitle because
* the DatePickerDialog title is buggy (unix warparound bug).
* See: https://code.google.com/p/android/issues/detail?id=49066
*/
DatePickerDialog dialog = new ExpiryDatePickerDialog(getActivity(),
mExpiryDateSetListener, mExpiryCal.get(Calendar.YEAR), mExpiryCal.get(Calendar.MONTH),
mExpiryCal.get(Calendar.DAY_OF_MONTH));
mDatePickerResultCount = 0;
dialog.setCancelable(true);
dialog.setButton(Dialog.BUTTON_NEGATIVE,
getActivity().getString(R.string.btn_no_date),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Note: Ignore results after the first one - android sends multiples.
if (mDatePickerResultCount++ == 0) {
// none expiry dates corresponds to a null message
Bundle data = new Bundle();
data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, null);
sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data);
}
}
}
);
// setCalendarViewShown() is supported from API 11 onwards.
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
// Hide calendarView in tablets because of the unix warparound bug.
dialog.getDatePicker().setCalendarViewShown(false);
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
// will crash with IllegalArgumentException if we set a min date
// that is not before expiry
if (mCreationCal != null && mCreationCal.before(mExpiryCal)) {
dialog.getDatePicker().setMinDate(mCreationCal.getTime().getTime()
+ DateUtils.DAY_IN_MILLIS);
} else {
// When created date isn't available
dialog.getDatePicker().setMinDate(mExpiryCal.getTime().getTime()
+ DateUtils.DAY_IN_MILLIS);
}
}
return dialog;
}
/**
* 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,110 @@
/*
* 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.dialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Log;
public class EditSubkeyDialogFragment extends DialogFragment {
private static final String ARG_MESSENGER = "messenger";
public static final int MESSAGE_CHANGE_EXPIRY = 1;
public static final int MESSAGE_REVOKE = 2;
private Messenger mMessenger;
/**
* Creates new instance of this dialog fragment
*/
public static EditSubkeyDialogFragment newInstance(Messenger messenger) {
EditSubkeyDialogFragment frag = new EditSubkeyDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger);
frag.setArguments(args);
return frag;
}
/**
* Creates dialog
*/
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(getActivity());
CharSequence[] array = getResources().getStringArray(R.array.edit_key_edit_subkey);
builder.setTitle(R.string.edit_key_edit_subkey_title);
builder.setItems(array, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0:
sendMessageToHandler(MESSAGE_CHANGE_EXPIRY, null);
break;
case 1:
sendMessageToHandler(MESSAGE_REVOKE, null);
break;
default:
break;
}
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dismiss();
}
});
return builder.show();
}
/**
* 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

@@ -152,7 +152,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
// above can't be statically verified to have been set in all cases because
// the catch clause doesn't return.
try {
userId = secretRing.getPrimaryUserId();
userId = secretRing.getPrimaryUserIdWithFallback();
} catch (PgpGeneralException e) {
userId = null;
}
@@ -189,7 +189,8 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
// Early breakout if we are dealing with a symmetric key
if (secretRing == null) {
PassphraseCacheService.addCachedPassphrase(activity, Constants.key.symmetric, passphrase);
PassphraseCacheService.addCachedPassphrase(activity, Constants.key.symmetric,
passphrase, getString(R.string.passp_cache_notif_pwd));
// also return passphrase back to activity
Bundle data = new Bundle();
data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
@@ -228,10 +229,18 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
// cache the new passphrase
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
PassphraseCacheService.addCachedPassphrase(activity, masterKeyId, passphrase);
try {
PassphraseCacheService.addCachedPassphrase(activity, masterKeyId, passphrase,
secretRing.getPrimaryUserIdWithFallback());
} catch(PgpGeneralException e) {
Log.e(Constants.TAG, "adding of a passhrase failed", e);
}
if (unlockedSecretKey.getKeyId() != masterKeyId) {
PassphraseCacheService.addCachedPassphrase(
activity, unlockedSecretKey.getKeyId(), passphrase);
activity, unlockedSecretKey.getKeyId(), passphrase,
unlockedSecretKey.getPrimaryUserIdWithFallback());
}
// also return passphrase back to activity