aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'OpenKeychain/src/main/java')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/AppCompatPreferenceActivity.java127
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ConsolidateOperation.java46
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java343
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java)436
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java158
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/KeybaseVerificationResult.java78
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java14
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ConsolidateInputParcel.java58
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DeleteKeyringParcel.java63
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java99
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java74
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeybaseVerificationParcel.java62
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainNewService.java188
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java679
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java66
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java102
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java99
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java37
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java270
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyBlankFragment.java90
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyImportFragment.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java)112
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyPinFragment.java128
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyPinRepeatFragment.java152
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyWaitFragment.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyWaitFragment.java)4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java92
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java14
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java18
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java24
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java126
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java116
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java164
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java132
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java80
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java194
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java367
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java94
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java91
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java235
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java65
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java215
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java326
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddKeyserverDialogFragment.java)89
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java113
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/DividerItemDecoration.java106
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperAdapter.java41
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperDragCallback.java92
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperViewHolder.java39
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/RecyclerItemClickListener.java70
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java28
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java84
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java55
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java85
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java6
73 files changed, 4187 insertions, 2610 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/AppCompatPreferenceActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/AppCompatPreferenceActivity.java
new file mode 100644
index 000000000..5200b8ced
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/compatibility/AppCompatPreferenceActivity.java
@@ -0,0 +1,127 @@
+
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sufficientlysecure.keychain.compatibility;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatDelegate;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
+ * to be used with AppCompat.
+ * <p/>
+ * This technique can be used with an {@link android.app.Activity} class, not just
+ * {@link android.preference.PreferenceActivity}.
+ */
+public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
+ private AppCompatDelegate mDelegate;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ getDelegate().installViewFactory();
+ getDelegate().onCreate(savedInstanceState);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ getDelegate().onPostCreate(savedInstanceState);
+ }
+
+ public ActionBar getSupportActionBar() {
+ return getDelegate().getSupportActionBar();
+ }
+
+ public void setSupportActionBar(@Nullable Toolbar toolbar) {
+ getDelegate().setSupportActionBar(toolbar);
+ }
+
+ @Override
+ public MenuInflater getMenuInflater() {
+ return getDelegate().getMenuInflater();
+ }
+
+ @Override
+ public void setContentView(@LayoutRes int layoutResID) {
+ getDelegate().setContentView(layoutResID);
+ }
+
+ @Override
+ public void setContentView(View view) {
+ getDelegate().setContentView(view);
+ }
+
+ @Override
+ public void setContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().setContentView(view, params);
+ }
+
+ @Override
+ public void addContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().addContentView(view, params);
+ }
+
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ getDelegate().onPostResume();
+ }
+
+ @Override
+ protected void onTitleChanged(CharSequence title, int color) {
+ super.onTitleChanged(title, color);
+ getDelegate().setTitle(title);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ getDelegate().onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ getDelegate().onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ getDelegate().onDestroy();
+ }
+
+ public void invalidateOptionsMenu() {
+ getDelegate().invalidateOptionsMenu();
+ }
+
+ private AppCompatDelegate getDelegate() {
+ if (mDelegate == null) {
+ mDelegate = AppCompatDelegate.create(this, null);
+ }
+ return mDelegate;
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java
index c0221fad3..649cede10 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java
@@ -45,6 +45,7 @@ public class CloudSearch {
}
final ImportKeysList results = new ImportKeysList(servers.size());
+ ArrayList<Thread> searchThreads = new ArrayList<>();
for (final Keyserver keyserver : servers) {
Runnable r = new Runnable() {
@Override
@@ -57,19 +58,25 @@ public class CloudSearch {
results.finishedAdding(); // notifies if all searchers done
}
};
- new Thread(r).start();
+ Thread searchThread = new Thread(r);
+ searchThreads.add(searchThread);
+ searchThread.start();
}
// wait for either all the searches to come back, or 10 seconds
- synchronized(results) {
+ synchronized (results) {
try {
results.wait(10 * SECONDS);
+ for (Thread thread : searchThreads) {
+ // kill threads that haven't returned yet
+ thread.interrupt();
+ }
} catch (InterruptedException e) {
}
}
if (results.outstandingSuppliers() > 0) {
- String message = "Launched " + servers.size() + " cloud searchers, but" +
+ String message = "Launched " + servers.size() + " cloud searchers, but" +
results.outstandingSuppliers() + "failed to complete.";
problems.add(new Keyserver.QueryFailedException(message));
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java
index 439260b74..0806e6a16 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java
@@ -58,12 +58,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
* @see CertifyActionsParcel
*
*/
-public class CertifyOperation extends BaseOperation {
+public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
public CertifyOperation(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean cancelled) {
super(context, providerHelper, progressable, cancelled);
}
+ @Override
public CertifyResult execute(CertifyActionsParcel parcel, CryptoInputParcel cryptoInput) {
OperationLog log = new OperationLog();
@@ -86,8 +87,10 @@ public class CertifyOperation extends BaseOperation {
case PATTERN:
case PASSPHRASE:
if (!cryptoInput.hasPassphrase()) {
- return new CertifyResult(log, RequiredInputParcel.createRequiredSignPassphrase(
- certificationKey.getKeyId(), certificationKey.getKeyId(), null));
+ return new CertifyResult(log,
+ RequiredInputParcel.createRequiredSignPassphrase(
+ certificationKey.getKeyId(), certificationKey.getKeyId(), null)
+ );
}
// certification is always with the master key id, so use that one
passphrase = cryptoInput.getPassphrase();
@@ -185,10 +188,10 @@ public class CertifyOperation extends BaseOperation {
}
HkpKeyserver keyServer = null;
- ImportExportOperation importExportOperation = null;
+ ExportOperation exportOperation = null;
if (parcel.keyServerUri != null) {
keyServer = new HkpKeyserver(parcel.keyServerUri);
- importExportOperation = new ImportExportOperation(mContext, mProviderHelper, mProgressable);
+ exportOperation = new ExportOperation(mContext, mProviderHelper, mProgressable);
}
// Write all certified keys into the database
@@ -206,10 +209,10 @@ public class CertifyOperation extends BaseOperation {
mProviderHelper.clearLog();
SaveKeyringResult result = mProviderHelper.savePublicKeyRing(certifiedKey);
- if (importExportOperation != null) {
+ if (exportOperation != null) {
// TODO use subresult, get rid of try/catch!
try {
- importExportOperation.uploadKeyRingToServer(keyServer, certifiedKey);
+ exportOperation.uploadKeyRingToServer(keyServer, certifiedKey);
uploadOk += 1;
} catch (AddKeyException e) {
Log.e(Constants.TAG, "error uploading key", e);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ConsolidateOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ConsolidateOperation.java
new file mode 100644
index 000000000..bda574e0a
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ConsolidateOperation.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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.operations;
+
+import android.content.Context;
+
+import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
+import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.ConsolidateInputParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+
+public class ConsolidateOperation extends BaseOperation<ConsolidateInputParcel> {
+
+ public ConsolidateOperation(Context context, ProviderHelper providerHelper, Progressable
+ progressable) {
+ super(context, providerHelper, progressable);
+ }
+
+ @Override
+ public ConsolidateResult execute(ConsolidateInputParcel consolidateInputParcel,
+ CryptoInputParcel cryptoInputParcel) {
+ if (consolidateInputParcel.mConsolidateRecovery) {
+ return mProviderHelper.consolidateDatabaseStep2(mProgressable);
+ } else {
+ return mProviderHelper.consolidateDatabaseStep1(mProgressable);
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java
index 5ef04ab05..50b2ef69b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java
@@ -27,6 +27,8 @@ import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
+import org.sufficientlysecure.keychain.service.DeleteKeyringParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
/** An operation which implements a high level keyring delete operation.
@@ -37,13 +39,18 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
* a list.
*
*/
-public class DeleteOperation extends BaseOperation {
+public class DeleteOperation extends BaseOperation<DeleteKeyringParcel> {
public DeleteOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
super(context, providerHelper, progressable);
}
- public DeleteResult execute(long[] masterKeyIds, boolean isSecret) {
+ @Override
+ public DeleteResult execute(DeleteKeyringParcel deleteKeyringParcel,
+ CryptoInputParcel cryptoInputParcel) {
+
+ long[] masterKeyIds = deleteKeyringParcel.mMasterKeyIds;
+ boolean isSecret = deleteKeyringParcel.mIsSecret;
OperationLog log = new OperationLog();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java
new file mode 100644
index 000000000..01a45bc79
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
+ *
+ * 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.operations;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+
+import org.spongycastle.bcpg.ArmoredOutputStream;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
+import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException;
+import org.sufficientlysecure.keychain.operations.results.ExportResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.FileHelper;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * An operation class which implements high level export
+ * operations.
+ * This class receives a source and/or destination of keys as input and performs
+ * all steps for this export.
+ *
+ * @see org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter#getSelectedEntries()
+ * For the export operation, the input consists of a set of key ids and
+ * either the name of a file or an output uri to write to.
+ * TODO rework uploadKeyRingToServer
+ */
+public class ExportOperation extends BaseOperation<ExportKeyringParcel> {
+
+ public ExportOperation(Context context, ProviderHelper providerHelper, Progressable
+ progressable) {
+ super(context, providerHelper, progressable);
+ }
+
+ public ExportOperation(Context context, ProviderHelper providerHelper,
+ Progressable progressable, AtomicBoolean cancelled) {
+ super(context, providerHelper, progressable, cancelled);
+ }
+
+ public void uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring)
+ throws AddKeyException {
+ uploadKeyRingToServer(server, keyring.getUncachedKeyRing());
+ }
+
+ public void uploadKeyRingToServer(HkpKeyserver server, UncachedKeyRing keyring) throws
+ AddKeyException {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ArmoredOutputStream aos = null;
+ try {
+ aos = new ArmoredOutputStream(bos);
+ keyring.encode(aos);
+ aos.close();
+
+ String armoredKey = bos.toString("UTF-8");
+ server.add(armoredKey);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException", e);
+ throw new AddKeyException();
+ } finally {
+ try {
+ if (aos != null) {
+ aos.close();
+ }
+ bos.close();
+ } catch (IOException e) {
+ // this is just a finally thing, no matter if it doesn't work out.
+ }
+ }
+ }
+
+ public ExportResult exportToFile(long[] masterKeyIds, boolean exportSecret, String outputFile) {
+
+ OperationLog log = new OperationLog();
+ if (masterKeyIds != null) {
+ log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length);
+ } else {
+ log.add(LogType.MSG_EXPORT_ALL, 0);
+ }
+
+ // do we have a file name?
+ if (outputFile == null) {
+ log.add(LogType.MSG_EXPORT_ERROR_NO_FILE, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log);
+ }
+
+ // check if storage is ready
+ if (!FileHelper.isStorageMounted(outputFile)) {
+ log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log);
+ }
+
+ try {
+ OutputStream outStream = new FileOutputStream(outputFile);
+ try {
+ ExportResult result = exportKeyRings(log, masterKeyIds, exportSecret, outStream);
+ if (result.cancelled()) {
+ //noinspection ResultOfMethodCallIgnored
+ new File(outputFile).delete();
+ }
+ return result;
+ } finally {
+ outStream.close();
+ }
+ } catch (IOException e) {
+ log.add(LogType.MSG_EXPORT_ERROR_FOPEN, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log);
+ }
+
+ }
+
+ public ExportResult exportToUri(long[] masterKeyIds, boolean exportSecret, Uri outputUri) {
+
+ OperationLog log = new OperationLog();
+ if (masterKeyIds != null) {
+ log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length);
+ } else {
+ log.add(LogType.MSG_EXPORT_ALL, 0);
+ }
+
+ // do we have a file name?
+ if (outputUri == null) {
+ log.add(LogType.MSG_EXPORT_ERROR_NO_URI, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log);
+ }
+
+ try {
+ OutputStream outStream = mProviderHelper.getContentResolver().openOutputStream
+ (outputUri);
+ return exportKeyRings(log, masterKeyIds, exportSecret, outStream);
+ } catch (FileNotFoundException e) {
+ log.add(LogType.MSG_EXPORT_ERROR_URI_OPEN, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log);
+ }
+
+ }
+
+ ExportResult exportKeyRings(OperationLog log, long[] masterKeyIds, boolean exportSecret,
+ OutputStream outStream) {
+
+ /* TODO isn't this checked above, with the isStorageMounted call?
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log);
+ }
+ */
+
+ if (!BufferedOutputStream.class.isInstance(outStream)) {
+ outStream = new BufferedOutputStream(outStream);
+ }
+
+ int okSecret = 0, okPublic = 0, progress = 0;
+
+ Cursor cursor = null;
+ try {
+
+ String selection = null, ids[] = null;
+
+ if (masterKeyIds != null) {
+ // generate placeholders and string selection args
+ ids = new String[masterKeyIds.length];
+ StringBuilder placeholders = new StringBuilder("?");
+ for (int i = 0; i < masterKeyIds.length; i++) {
+ ids[i] = Long.toString(masterKeyIds[i]);
+ if (i != 0) {
+ placeholders.append(",?");
+ }
+ }
+
+ // put together selection string
+ selection = Tables.KEY_RINGS_PUBLIC + "." + KeyRings.MASTER_KEY_ID
+ + " IN (" + placeholders + ")";
+ }
+
+ cursor = mProviderHelper.getContentResolver().query(
+ KeyRings.buildUnifiedKeyRingsUri(), new String[]{
+ KeyRings.MASTER_KEY_ID, KeyRings.PUBKEY_DATA,
+ KeyRings.PRIVKEY_DATA, KeyRings.HAS_ANY_SECRET
+ }, selection, ids, Tables.KEYS + "." + KeyRings.MASTER_KEY_ID
+ );
+
+ if (cursor == null || !cursor.moveToFirst()) {
+ log.add(LogType.MSG_EXPORT_ERROR_DB, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log, okPublic, okSecret);
+ }
+
+ int numKeys = cursor.getCount();
+
+ updateProgress(
+ mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
+ numKeys), 0, numKeys);
+
+ // For each public masterKey id
+ while (!cursor.isAfterLast()) {
+
+ long keyId = cursor.getLong(0);
+ ArmoredOutputStream arOutStream = null;
+
+ // Create an output stream
+ try {
+ arOutStream = new ArmoredOutputStream(outStream);
+
+ log.add(LogType.MSG_EXPORT_PUBLIC, 1, KeyFormattingUtils.beautifyKeyId(keyId));
+
+ byte[] data = cursor.getBlob(1);
+ CanonicalizedKeyRing ring =
+ UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true);
+ ring.encode(arOutStream);
+
+ okPublic += 1;
+ } catch (PgpGeneralException e) {
+ log.add(LogType.MSG_EXPORT_ERROR_KEY, 2);
+ updateProgress(progress++, numKeys);
+ continue;
+ } finally {
+ // make sure this is closed
+ if (arOutStream != null) {
+ arOutStream.close();
+ }
+ arOutStream = null;
+ }
+
+ if (exportSecret && cursor.getInt(3) > 0) {
+ try {
+ arOutStream = new ArmoredOutputStream(outStream);
+
+ // export secret key part
+ log.add(LogType.MSG_EXPORT_SECRET, 2, KeyFormattingUtils.beautifyKeyId
+ (keyId));
+ byte[] data = cursor.getBlob(2);
+ CanonicalizedKeyRing ring =
+ UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true);
+ ring.encode(arOutStream);
+
+ okSecret += 1;
+ } catch (PgpGeneralException e) {
+ log.add(LogType.MSG_EXPORT_ERROR_KEY, 2);
+ updateProgress(progress++, numKeys);
+ continue;
+ } finally {
+ // make sure this is closed
+ if (arOutStream != null) {
+ arOutStream.close();
+ }
+ }
+ }
+
+ updateProgress(progress++, numKeys);
+
+ cursor.moveToNext();
+ }
+
+ updateProgress(R.string.progress_done, numKeys, numKeys);
+
+ } catch (IOException e) {
+ log.add(LogType.MSG_EXPORT_ERROR_IO, 1);
+ return new ExportResult(ExportResult.RESULT_ERROR, log, okPublic, okSecret);
+ } finally {
+ // Make sure the stream is closed
+ if (outStream != null) try {
+ outStream.close();
+ } catch (Exception e) {
+ Log.e(Constants.TAG, "error closing stream", e);
+ }
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+
+ log.add(LogType.MSG_EXPORT_SUCCESS, 1);
+ return new ExportResult(ExportResult.RESULT_OK, log, okPublic, okSecret);
+
+ }
+
+ public ExportResult execute(ExportKeyringParcel exportInput, CryptoInputParcel cryptoInput) {
+ switch (exportInput.mExportType) {
+ case UPLOAD_KEYSERVER: {
+ HkpKeyserver hkpKeyserver = new HkpKeyserver(exportInput.mKeyserver);
+ try {
+ CanonicalizedPublicKeyRing keyring
+ = mProviderHelper.getCanonicalizedPublicKeyRing(
+ exportInput.mCanonicalizedPublicKeyringUri);
+ uploadKeyRingToServer(hkpKeyserver, keyring);
+ // TODO: replace with proper log
+ return new ExportResult(ExportResult.RESULT_OK, new OperationLog());
+ } catch (Exception e) {
+ return new ExportResult(ExportResult.RESULT_ERROR, new OperationLog());
+ // TODO: Implement better exception handling, replace with log
+ }
+ }
+ case EXPORT_FILE: {
+ return exportToFile(exportInput.mMasterKeyIds, exportInput.mExportSecret,
+ exportInput.mOutputFile);
+ }
+ case EXPORT_URI: {
+ return exportToUri(exportInput.mMasterKeyIds, exportInput.mExportSecret,
+ exportInput.mOutputUri);
+ }
+ default: { // can't happen
+ return null;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java
index cdda42dae..ace059dac 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java
@@ -19,57 +19,50 @@
package org.sufficientlysecure.keychain.operations;
import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import org.spongycastle.bcpg.ArmoredOutputStream;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver;
import org.sufficientlysecure.keychain.keyimport.Keyserver;
-import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
-import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-import org.sufficientlysecure.keychain.util.FileHelper;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
import org.sufficientlysecure.keychain.util.ProgressScaler;
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
-/** An operation class which implements high level import and export
+/**
+ * An operation class which implements high level import
* operations.
- *
* This class receives a source and/or destination of keys as input and performs
- * all steps for this import or export.
- *
+ * all steps for this import.
* For the import operation, the only valid source is an Iterator of
* ParcelableKeyRing, each of which must contain either a single
* keyring encoded as bytes, or a unique reference to a keyring
@@ -78,72 +71,57 @@ import java.util.concurrent.atomic.AtomicBoolean;
* secret keys, because some implementations (notably Symantec PGP Desktop) do
* not include self certificates for user ids in the secret keyring. The import
* method here will generally import keyrings in the order given by the
- * iterator. so this should be ensured beforehand.
- * @see org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter#getSelectedEntries()
- *
- * For the export operation, the input consists of a set of key ids and
- * either the name of a file or an output uri to write to.
- *
- * TODO rework uploadKeyRingToServer
+ * iterator, so this should be ensured beforehand.
*
+ * @see org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter#getSelectedEntries()
*/
-public class ImportExportOperation extends BaseOperation {
+public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
- public ImportExportOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
+ public ImportOperation(Context context, ProviderHelper providerHelper, Progressable
+ progressable) {
super(context, providerHelper, progressable);
}
- public ImportExportOperation(Context context, ProviderHelper providerHelper,
- Progressable progressable, AtomicBoolean cancelled) {
+ public ImportOperation(Context context, ProviderHelper providerHelper,
+ Progressable progressable, AtomicBoolean cancelled) {
super(context, providerHelper, progressable, cancelled);
}
- public void uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring) throws AddKeyException {
- uploadKeyRingToServer(server, keyring.getUncachedKeyRing());
+ // Overloaded functions for using progressable supplied in constructor during import
+ public ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num,
+ String keyServerUri) {
+ return serialKeyRingImport(entries, num, keyServerUri, mProgressable);
}
- public void uploadKeyRingToServer(HkpKeyserver server, UncachedKeyRing keyring) throws AddKeyException {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- ArmoredOutputStream aos = null;
- try {
- aos = new ArmoredOutputStream(bos);
- keyring.encode(aos);
- aos.close();
+ public ImportKeyResult serialKeyRingImport(List<ParcelableKeyRing> entries,
+ String keyServerUri) {
+
+ Iterator<ParcelableKeyRing> it = entries.iterator();
+ int numEntries = entries.size();
+
+ return serialKeyRingImport(it, numEntries, keyServerUri, mProgressable);
- String armoredKey = bos.toString("UTF-8");
- server.add(armoredKey);
- } catch (IOException e) {
- Log.e(Constants.TAG, "IOException", e);
- throw new AddKeyException();
- } finally {
- try {
- if (aos != null) {
- aos.close();
- }
- bos.close();
- } catch (IOException e) {
- // this is just a finally thing, no matter if it doesn't work out.
- }
- }
}
- public ImportKeyResult importKeyRings(List<ParcelableKeyRing> entries, String keyServerUri) {
+ public ImportKeyResult serialKeyRingImport(List<ParcelableKeyRing> entries, String keyServerUri,
+ Progressable progressable) {
Iterator<ParcelableKeyRing> it = entries.iterator();
int numEntries = entries.size();
- return importKeyRings(it, numEntries, keyServerUri);
+ return serialKeyRingImport(it, numEntries, keyServerUri, progressable);
}
- public ImportKeyResult importKeyRings(ParcelableFileCache<ParcelableKeyRing> cache, String keyServerUri) {
+ public ImportKeyResult serialKeyRingImport(ParcelableFileCache<ParcelableKeyRing> cache,
+ String keyServerUri) {
// get entries from cached file
try {
IteratorWithSize<ParcelableKeyRing> it = cache.readCache();
int numEntries = it.getSize();
- return importKeyRings(it, numEntries, keyServerUri);
+ return serialKeyRingImport(it, numEntries, keyServerUri, mProgressable);
} catch (IOException e) {
// Special treatment here, we need a lot
@@ -157,15 +135,18 @@ public class ImportExportOperation extends BaseOperation {
}
/**
- * Since the introduction of multithreaded import, we expect calling functions to handle the key sync i,e
- * ContactSyncAdapterService.requestSync()
+ * Since the introduction of multithreaded import, we expect calling functions to handle the
+ * key sync i,eContactSyncAdapterService.requestSync()
*
- * @param entries keys to import
- * @param num number of keys to import
+ * @param entries keys to import
+ * @param num number of keys to import
* @param keyServerUri contains uri of keyserver to import from, if it is an import from cloud
+ * @param progressable Allows multi-threaded import to supply a progressable that ignores the
+ * progress of a single key being imported
* @return
*/
- public ImportKeyResult importKeyRings(Iterator<ParcelableKeyRing> entries, int num, String keyServerUri) {
+ public ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num,
+ String keyServerUri, Progressable progressable) {
updateProgress(R.string.progress_importing, 0, 100);
OperationLog log = new OperationLog();
@@ -208,7 +189,8 @@ public class ImportExportOperation extends BaseOperation {
else {
// We fetch from keyservers first, because we tend to get more certificates
- // from there, so the number of certificates which are merged in later is smaller.
+ // from there, so the number of certificates which are merged in later is
+ // smaller.
// If we have a keyServerUri and a fingerprint or at least a keyId,
// download from HKP
@@ -224,7 +206,8 @@ public class ImportExportOperation extends BaseOperation {
byte[] data;
// Download by fingerprint, or keyId - whichever is available
if (entry.mExpectedFingerprint != null) {
- log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, "0x" + entry.mExpectedFingerprint.substring(24));
+ log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, "0x" +
+ entry.mExpectedFingerprint.substring(24));
data = keyServer.get("0x" + entry.mExpectedFingerprint).getBytes();
} else {
log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, entry.mKeyIdHex);
@@ -302,10 +285,12 @@ public class ImportExportOperation extends BaseOperation {
mProviderHelper.clearLog();
if (key.isSecret()) {
result = mProviderHelper.saveSecretKeyRing(key,
- new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100));
+ new ProgressScaler(progressable, (int) (position * progSteps),
+ (int) ((position + 1) * progSteps), 100));
} else {
result = mProviderHelper.savePublicKeyRing(key,
- new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100));
+ new ProgressScaler(progressable, (int) (position * progSteps),
+ (int) ((position + 1) * progSteps), 100));
}
if (!result.success()) {
badKeys += 1;
@@ -333,7 +318,7 @@ public class ImportExportOperation extends BaseOperation {
// Special: consolidate on secret key import (cannot be cancelled!)
if (secret > 0) {
setPreventCancel();
- ConsolidateResult result = mProviderHelper.consolidateDatabaseStep1(mProgressable);
+ ConsolidateResult result = mProviderHelper.consolidateDatabaseStep1(progressable);
log.add(result, 1);
}
@@ -376,7 +361,7 @@ public class ImportExportOperation extends BaseOperation {
}
// Final log entry, it's easier to do this individually
- if ( (newKeys > 0 || updatedKeys > 0) && badKeys > 0) {
+ if ((newKeys > 0 || updatedKeys > 0) && badKeys > 0) {
log.add(LogType.MSG_IMPORT_PARTIAL, 1);
} else if (newKeys > 0 || updatedKeys > 0) {
log.add(LogType.MSG_IMPORT_SUCCESS, 1);
@@ -388,206 +373,193 @@ public class ImportExportOperation extends BaseOperation {
importedMasterKeyIdsArray);
}
- public ExportResult exportToFile(long[] masterKeyIds, boolean exportSecret, String outputFile) {
+ @Override
+ public ImportKeyResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) {
+ return importKeys(importInput.mKeyList, importInput.mKeyserver);
+ }
- OperationLog log = new OperationLog();
- if (masterKeyIds != null) {
- log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length);
- } else {
- log.add(LogType.MSG_EXPORT_ALL, 0);
- }
+ public ImportKeyResult importKeys(ArrayList<ParcelableKeyRing> keyList, String keyServer) {
- // do we have a file name?
- if (outputFile == null) {
- log.add(LogType.MSG_EXPORT_ERROR_NO_FILE, 1);
- return new ExportResult(ExportResult.RESULT_ERROR, log);
- }
+ ImportKeyResult result;
- // check if storage is ready
- if (!FileHelper.isStorageMounted(outputFile)) {
- log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1);
- return new ExportResult(ExportResult.RESULT_ERROR, log);
- }
+ if (keyList == null) {// import from file, do serially
+ ParcelableFileCache<ParcelableKeyRing> cache = new ParcelableFileCache<>(mContext,
+ "key_import.pcl");
- try {
- OutputStream outStream = new FileOutputStream(outputFile);
- try {
- ExportResult result = exportKeyRings(log, masterKeyIds, exportSecret, outStream);
- if (result.cancelled()) {
- //noinspection ResultOfMethodCallIgnored
- new File(outputFile).delete();
- }
- return result;
- } finally {
- outStream.close();
+ result = serialKeyRingImport(cache, keyServer);
+ } else {
+ // if there is more than one key with the same fingerprint, we do a serial import to
+ // prevent
+ // https://github.com/open-keychain/open-keychain/issues/1221
+ HashSet<String> keyFingerprintSet = new HashSet<>();
+ for (int i = 0; i < keyList.size(); i++) {
+ keyFingerprintSet.add(keyList.get(i).mExpectedFingerprint);
+ }
+ if (keyFingerprintSet.size() == keyList.size()) {
+ // all keys have unique fingerprints
+ result = multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer);
+ } else {
+ result = serialKeyRingImport(keyList, keyServer);
}
- } catch (IOException e) {
- log.add(LogType.MSG_EXPORT_ERROR_FOPEN, 1);
- return new ExportResult(ExportResult.RESULT_ERROR, log);
}
+ ContactSyncAdapterService.requestSync();
+ return result;
}
- public ExportResult exportToUri(long[] masterKeyIds, boolean exportSecret, Uri outputUri) {
+ private ImportKeyResult multiThreadedKeyImport(Iterator<ParcelableKeyRing> keyListIterator,
+ int totKeys, final String keyServer) {
+ Log.d(Constants.TAG, "Multi-threaded key import starting");
+ if (keyListIterator != null) {
+ KeyImportAccumulator accumulator = new KeyImportAccumulator(totKeys, mProgressable);
- OperationLog log = new OperationLog();
- if (masterKeyIds != null) {
- log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length);
- } else {
- log.add(LogType.MSG_EXPORT_ALL, 0);
- }
+ final ProgressScaler ignoreProgressable = new ProgressScaler();
- // do we have a file name?
- if (outputUri == null) {
- log.add(LogType.MSG_EXPORT_ERROR_NO_URI, 1);
- return new ExportResult(ExportResult.RESULT_ERROR, log);
- }
+ final int maxThreads = 200;
+ ExecutorService importExecutor = new ThreadPoolExecutor(0, maxThreads,
+ 30L, TimeUnit.SECONDS,
+ new SynchronousQueue<Runnable>());
- try {
- OutputStream outStream = mProviderHelper.getContentResolver().openOutputStream(outputUri);
- return exportKeyRings(log, masterKeyIds, exportSecret, outStream);
- } catch (FileNotFoundException e) {
- log.add(LogType.MSG_EXPORT_ERROR_URI_OPEN, 1);
- return new ExportResult(ExportResult.RESULT_ERROR, log);
- }
+ ExecutorCompletionService<ImportKeyResult> importCompletionService =
+ new ExecutorCompletionService(importExecutor);
- }
+ while (keyListIterator.hasNext()) { // submit all key rings to be imported
- ExportResult exportKeyRings(OperationLog log, long[] masterKeyIds, boolean exportSecret,
- OutputStream outStream) {
+ final ParcelableKeyRing pkRing = keyListIterator.next();
- /* TODO isn't this checked above, with the isStorageMounted call?
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1);
- return new ExportResult(ExportResult.RESULT_ERROR, log);
- }
- */
+ Callable<ImportKeyResult> importOperationCallable = new Callable<ImportKeyResult>
+ () {
- if ( ! BufferedOutputStream.class.isInstance(outStream)) {
- outStream = new BufferedOutputStream(outStream);
- }
+ @Override
+ public ImportKeyResult call() {
- int okSecret = 0, okPublic = 0, progress = 0;
+ ArrayList<ParcelableKeyRing> list = new ArrayList<>();
+ list.add(pkRing);
- Cursor cursor = null;
- try {
+ return serialKeyRingImport(list, keyServer, ignoreProgressable);
+ }
+ };
- String selection = null, ids[] = null;
+ importCompletionService.submit(importOperationCallable);
+ }
- if (masterKeyIds != null) {
- // generate placeholders and string selection args
- ids = new String[masterKeyIds.length];
- StringBuilder placeholders = new StringBuilder("?");
- for (int i = 0; i < masterKeyIds.length; i++) {
- ids[i] = Long.toString(masterKeyIds[i]);
- if (i != 0) {
- placeholders.append(",?");
+ while (!accumulator.isImportFinished()) { // accumulate the results of each import
+ try {
+ accumulator.accumulateKeyImport(importCompletionService.take().get());
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(Constants.TAG, "A key could not be imported during multi-threaded " +
+ "import", e);
+ // do nothing?
+ if (e instanceof ExecutionException) {
+ // Since serialKeyRingImport does not throw any exceptions, this is what
+ // would have happened if
+ // we were importing the key on this thread
+ throw new RuntimeException();
}
}
-
- // put together selection string
- selection = Tables.KEY_RINGS_PUBLIC + "." + KeyRings.MASTER_KEY_ID
- + " IN (" + placeholders + ")";
}
+ return accumulator.getConsolidatedResult();
+ }
+ return null; // TODO: Decide if we should just crash instead of returning null
+ }
- cursor = mProviderHelper.getContentResolver().query(
- KeyRings.buildUnifiedKeyRingsUri(), new String[]{
- KeyRings.MASTER_KEY_ID, KeyRings.PUBKEY_DATA,
- KeyRings.PRIVKEY_DATA, KeyRings.HAS_ANY_SECRET
- }, selection, ids, Tables.KEYS + "." + KeyRings.MASTER_KEY_ID
- );
-
- if (cursor == null || !cursor.moveToFirst()) {
- log.add(LogType.MSG_EXPORT_ERROR_DB, 1);
- return new ExportResult(ExportResult.RESULT_ERROR, log, okPublic, okSecret);
- }
+ /**
+ * Used to accumulate the results of individual key imports
+ */
+ private class KeyImportAccumulator {
+ private OperationResult.OperationLog mImportLog = new OperationResult.OperationLog();
+ Progressable mProgressable;
+ private int mTotalKeys;
+ private int mImportedKeys = 0;
+ ArrayList<Long> mImportedMasterKeyIds = new ArrayList<Long>();
+ private int mBadKeys = 0;
+ private int mNewKeys = 0;
+ private int mUpdatedKeys = 0;
+ private int mSecret = 0;
+ private int mResultType = 0;
+
+ /**
+ * Accumulates keyring imports and updates the progressable whenever a new key is imported.
+ * Also sets the progress to 0 on instantiation.
+ *
+ * @param totalKeys total number of keys to be imported
+ * @param externalProgressable the external progressable to be updated every time a key
+ * is imported
+ */
+ public KeyImportAccumulator(int totalKeys, Progressable externalProgressable) {
+ mTotalKeys = totalKeys;
+ mProgressable = externalProgressable;
+ mProgressable.setProgress(0, totalKeys);
+ }
- int numKeys = cursor.getCount();
+ public int getTotalKeys() {
+ return mTotalKeys;
+ }
- updateProgress(
- mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
- numKeys), 0, numKeys);
+ public int getImportedKeys() {
+ return mImportedKeys;
+ }
- // For each public masterKey id
- while (!cursor.isAfterLast()) {
+ public synchronized void accumulateKeyImport(ImportKeyResult result) {
+ mImportedKeys++;
- long keyId = cursor.getLong(0);
- ArmoredOutputStream arOutStream = null;
+ mProgressable.setProgress(mImportedKeys, mTotalKeys);
- // Create an output stream
- try {
- arOutStream = new ArmoredOutputStream(outStream);
+ mImportLog.addAll(result.getLog().toList());//accumulates log
+ mBadKeys += result.mBadKeys;
+ mNewKeys += result.mNewKeys;
+ mUpdatedKeys += result.mUpdatedKeys;
+ mSecret += result.mSecret;
- log.add(LogType.MSG_EXPORT_PUBLIC, 1, KeyFormattingUtils.beautifyKeyId(keyId));
+ long[] masterKeyIds = result.getImportedMasterKeyIds();
+ for (long masterKeyId : masterKeyIds) {
+ mImportedMasterKeyIds.add(masterKeyId);
+ }
- byte[] data = cursor.getBlob(1);
- CanonicalizedKeyRing ring =
- UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true);
- ring.encode(arOutStream);
+ // if any key import has been cancelled, set result type to cancelled
+ // resultType is added to in getConsolidatedKayImport to account for remaining factors
+ mResultType |= result.getResult() & ImportKeyResult.RESULT_CANCELLED;
+ }
- okPublic += 1;
- } catch (PgpGeneralException e) {
- log.add(LogType.MSG_EXPORT_ERROR_KEY, 2);
- updateProgress(progress++, numKeys);
- continue;
- } finally {
- // make sure this is closed
- if (arOutStream != null) {
- arOutStream.close();
- }
- arOutStream = null;
+ /**
+ * returns accumulated result of all imports so far
+ */
+ public ImportKeyResult getConsolidatedResult() {
+
+ // adding required information to mResultType
+ // special case,no keys requested for import
+ if (mBadKeys == 0 && mNewKeys == 0 && mUpdatedKeys == 0) {
+ mResultType = ImportKeyResult.RESULT_FAIL_NOTHING;
+ } else {
+ if (mNewKeys > 0) {
+ mResultType |= ImportKeyResult.RESULT_OK_NEWKEYS;
}
-
- if (exportSecret && cursor.getInt(3) > 0) {
- try {
- arOutStream = new ArmoredOutputStream(outStream);
-
- // export secret key part
- log.add(LogType.MSG_EXPORT_SECRET, 2, KeyFormattingUtils.beautifyKeyId(keyId));
- byte[] data = cursor.getBlob(2);
- CanonicalizedKeyRing ring =
- UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true);
- ring.encode(arOutStream);
-
- okSecret += 1;
- } catch (PgpGeneralException e) {
- log.add(LogType.MSG_EXPORT_ERROR_KEY, 2);
- updateProgress(progress++, numKeys);
- continue;
- } finally {
- // make sure this is closed
- if (arOutStream != null) {
- arOutStream.close();
- }
+ if (mUpdatedKeys > 0) {
+ mResultType |= ImportKeyResult.RESULT_OK_UPDATED;
+ }
+ if (mBadKeys > 0) {
+ mResultType |= ImportKeyResult.RESULT_WITH_ERRORS;
+ if (mNewKeys == 0 && mUpdatedKeys == 0) {
+ mResultType |= ImportKeyResult.RESULT_ERROR;
}
}
-
- updateProgress(progress++, numKeys);
-
- cursor.moveToNext();
+ if (mImportLog.containsWarnings()) {
+ mResultType |= ImportKeyResult.RESULT_WARNINGS;
+ }
}
- updateProgress(R.string.progress_done, numKeys, numKeys);
-
- } catch (IOException e) {
- log.add(LogType.MSG_EXPORT_ERROR_IO, 1);
- return new ExportResult(ExportResult.RESULT_ERROR, log, okPublic, okSecret);
- } finally {
- // Make sure the stream is closed
- if (outStream != null) try {
- outStream.close();
- } catch (Exception e) {
- Log.e(Constants.TAG, "error closing stream", e);
- }
- if (cursor != null) {
- cursor.close();
+ long masterKeyIds[] = new long[mImportedMasterKeyIds.size()];
+ for (int i = 0; i < masterKeyIds.length; i++) {
+ masterKeyIds[i] = mImportedMasterKeyIds.get(i);
}
- }
-
- log.add(LogType.MSG_EXPORT_SUCCESS, 1);
- return new ExportResult(ExportResult.RESULT_OK, log, okPublic, okSecret);
+ return new ImportKeyResult(mResultType, mImportLog, mNewKeys, mUpdatedKeys, mBadKeys,
+ mSecret, masterKeyIds);
+ }
+ public boolean isImportFinished() {
+ return mTotalKeys == mImportedKeys;
+ }
}
-}
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java
new file mode 100644
index 000000000..57b99951d
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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.operations;
+
+import android.content.Context;
+
+import com.textuality.keybase.lib.Proof;
+import com.textuality.keybase.lib.prover.Prover;
+import de.measite.minidns.Client;
+import de.measite.minidns.DNSMessage;
+import de.measite.minidns.Question;
+import de.measite.minidns.Record;
+import de.measite.minidns.record.Data;
+import de.measite.minidns.record.TXT;
+import org.json.JSONObject;
+import org.spongycastle.openpgp.PGPUtil;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
+import org.sufficientlysecure.keychain.operations.results.KeybaseVerificationResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
+import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class KeybaseVerificationOperation extends BaseOperation<KeybaseVerificationParcel> {
+
+ public KeybaseVerificationOperation(Context context, ProviderHelper providerHelper,
+ Progressable progressable) {
+ super(context, providerHelper, progressable);
+ }
+
+ @Override
+ public KeybaseVerificationResult execute(KeybaseVerificationParcel keybaseInput,
+ CryptoInputParcel cryptoInput) {
+
+ String requiredFingerprint = keybaseInput.mRequiredFingerprint;
+
+ OperationResult.OperationLog log = new OperationResult.OperationLog();
+ log.add(OperationResult.LogType.MSG_KEYBASE_VERIFICATION, 0, requiredFingerprint);
+
+ try {
+ String keybaseProof = keybaseInput.mKeybaseProof;
+ Proof proof = new Proof(new JSONObject(keybaseProof));
+ mProgressable.setProgress(R.string.keybase_message_fetching_data, 0, 100);
+
+ Prover prover = Prover.findProverFor(proof);
+
+ if (prover == null) {
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_NO_PROVER, 1,
+ proof.getPrettyName());
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+
+ if (!prover.fetchProofData()) {
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_FETCH_PROOF, 1);
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+
+ if (!prover.checkFingerprint(requiredFingerprint)) {
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_FINGERPRINT_MISMATCH, 1);
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+
+ String domain = prover.dnsTxtCheckRequired();
+ if (domain != null) {
+ DNSMessage dnsQuery = new Client().query(new Question(domain, Record.TYPE.TXT));
+ if (dnsQuery == null) {
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_DNS_FAIL, 1);
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 2,
+ getFlattenedProverLog(prover));
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+ Record[] records = dnsQuery.getAnswers();
+ List<List<byte[]>> extents = new ArrayList<List<byte[]>>();
+ for (Record r : records) {
+ Data d = r.getPayload();
+ if (d instanceof TXT) {
+ extents.add(((TXT) d).getExtents());
+ }
+ }
+ if (!prover.checkDnsTxt(extents)) {
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 1,
+ getFlattenedProverLog(prover));
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+ }
+
+ byte[] messageBytes = prover.getPgpMessage().getBytes();
+ if (prover.rawMessageCheckRequired()) {
+ InputStream messageByteStream = PGPUtil.getDecoderStream(new
+ ByteArrayInputStream
+ (messageBytes));
+ if (!prover.checkRawMessageBytes(messageByteStream)) {
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 1,
+ getFlattenedProverLog(prover));
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+ }
+
+ PgpDecryptVerify op = new PgpDecryptVerify(mContext, mProviderHelper, mProgressable);
+
+ PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(messageBytes)
+ .setSignedLiteralData(true)
+ .setRequiredSignerFingerprint(requiredFingerprint);
+
+ DecryptVerifyResult decryptVerifyResult = op.execute(input, new CryptoInputParcel());
+
+ if (!decryptVerifyResult.success()) {
+ log.add(decryptVerifyResult, 1);
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+
+ if (!prover.validate(new String(decryptVerifyResult.getOutputBytes()))) {
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_PAYLOAD_MISMATCH, 1);
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+
+ return new KeybaseVerificationResult(OperationResult.RESULT_OK, log, prover);
+ } catch (Exception e) {
+ // just adds the passed parameter, in this case e.getMessage()
+ log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 1, e.getMessage());
+ return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
+ }
+ }
+
+ private String getFlattenedProverLog(Prover prover) {
+ String log = "";
+ for (String line : prover.getLog()) {
+ log += line + "\n";
+ }
+ return log;
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java
index 558756378..6291f14a3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java
@@ -29,13 +29,13 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
-import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
+import org.sufficientlysecure.keychain.service.PromoteKeyringParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ProgressScaler;
-import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
/** An operation which promotes a public key ring to a secret one.
@@ -45,13 +45,24 @@ import java.util.concurrent.atomic.AtomicBoolean;
* without secret key material, using a GNU_DUMMY s2k type.
*
*/
-public class PromoteKeyOperation extends BaseOperation {
+public class PromoteKeyOperation extends BaseOperation<PromoteKeyringParcel> {
public PromoteKeyOperation(Context context, ProviderHelper providerHelper,
Progressable progressable, AtomicBoolean cancelled) {
super(context, providerHelper, progressable, cancelled);
}
+ @Override
+ public PromoteKeyResult execute(PromoteKeyringParcel promoteKeyringParcel,
+ CryptoInputParcel cryptoInputParcel) {
+ // Input
+ long masterKeyId = promoteKeyringParcel.mKeyRingId;
+ byte[] cardAid = promoteKeyringParcel.mCardAid;
+ long[] subKeyIds = promoteKeyringParcel.mSubKeyIds;
+
+ return execute(masterKeyId, cardAid, subKeyIds);
+ }
+
public PromoteKeyResult execute(long masterKeyId, byte[] cardAid, long[] subKeyIds) {
OperationLog log = new OperationLog();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java
index 7c58d62f8..8fe5b86c5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/SignEncryptOperation.java
@@ -25,7 +25,6 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/KeybaseVerificationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/KeybaseVerificationResult.java
new file mode 100644
index 000000000..420cbbf01
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/KeybaseVerificationResult.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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.operations.results;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import com.textuality.keybase.lib.KeybaseException;
+import com.textuality.keybase.lib.prover.Prover;
+
+public class KeybaseVerificationResult extends OperationResult implements Parcelable {
+ public final String mProofUrl;
+ public final String mPresenceUrl;
+ public final String mPresenceLabel;
+
+ public KeybaseVerificationResult(int result, OperationLog log) {
+ super(result, log);
+ mProofUrl = null;
+ mPresenceLabel = null;
+ mPresenceUrl = null;
+ }
+
+ public KeybaseVerificationResult(int result, OperationLog log, Prover prover)
+ throws KeybaseException {
+ super(result, log);
+ mProofUrl = prover.getProofUrl();
+ mPresenceUrl = prover.getPresenceUrl();
+ mPresenceLabel = prover.getPresenceLabel();
+ }
+
+ protected KeybaseVerificationResult(Parcel in) {
+ super(in);
+ mProofUrl = in.readString();
+ mPresenceUrl = in.readString();
+ mPresenceLabel = in.readString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mProofUrl);
+ dest.writeString(mPresenceUrl);
+ dest.writeString(mPresenceLabel);
+ }
+
+ public static final Parcelable.Creator<KeybaseVerificationResult> CREATOR = new Parcelable.Creator<KeybaseVerificationResult>() {
+ @Override
+ public KeybaseVerificationResult createFromParcel(Parcel in) {
+ return new KeybaseVerificationResult(in);
+ }
+
+ @Override
+ public KeybaseVerificationResult[] newArray(int size) {
+ return new KeybaseVerificationResult[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
index c9e427462..f0561bef2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
@@ -750,7 +750,19 @@ public abstract class OperationResult implements Parcelable {
MSG_DEL_OK (LogLevel.OK, R.plurals.msg_del_ok),
MSG_DEL_FAIL (LogLevel.WARN, R.plurals.msg_del_fail),
- //export log
+ // keybase verification
+ MSG_KEYBASE_VERIFICATION(LogLevel.START, R.string.msg_keybase_verification),
+
+ MSG_KEYBASE_ERROR_NO_PROVER(LogLevel.ERROR, R.string.msg_keybase_error_no_prover),
+ MSG_KEYBASE_ERROR_FETCH_PROOF(LogLevel.ERROR, R.string.msg_keybase_error_fetching_evidence),
+ MSG_KEYBASE_ERROR_FINGERPRINT_MISMATCH(LogLevel.ERROR,
+ R.string.msg_keybase_error_key_mismatch),
+ MSG_KEYBASE_ERROR_DNS_FAIL(LogLevel.ERROR, R.string.msg_keybase_error_dns_fail),
+ MSG_KEYBASE_ERROR_SPECIFIC(LogLevel.ERROR, R.string.msg_keybase_error_specific),
+ MSG_KEYBASE_ERROR_PAYLOAD_MISMATCH(LogLevel.ERROR,
+ R.string.msg_keybase_error_msg_payload_mismatch),
+
+ // export log
MSG_EXPORT_LOG(LogLevel.START,R.string.msg_export_log_start),
MSG_EXPORT_LOG_EXPORT_ERROR_NO_FILE(LogLevel.ERROR,R.string.msg_export_log_error_no_file),
MSG_EXPORT_LOG_EXPORT_ERROR_FOPEN(LogLevel.ERROR,R.string.msg_export_log_error_fopen),
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index 7a4c85178..a6d260d22 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -938,7 +938,14 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
log.add(LogType.MSG_DC_OK, indent);
+ OpenPgpMetadata metadata = new OpenPgpMetadata(
+ "",
+ "text/plain",
+ -1,
+ clearText.length);
+
DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
+ result.setDecryptMetadata(metadata);
result.setSignatureResult(signatureResultBuilder.build());
return result;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index bf7014853..590c58f97 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -32,7 +32,7 @@ import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.operations.ImportExportOperation;
+import org.sufficientlysecure.keychain.operations.ImportOperation;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
@@ -1248,9 +1248,9 @@ public class ProviderHelper {
// 3. Re-Import secret keyrings from cache
if (numSecrets > 0) {
- ImportKeyResult result = new ImportExportOperation(mContext, this,
+ ImportKeyResult result = new ImportOperation(mContext, this,
new ProgressFixedScaler(progress, 10, 25, 100, R.string.progress_con_reimport))
- .importKeyRings(itSecrets, numSecrets, null);
+ .serialKeyRingImport(itSecrets, numSecrets, null);
log.add(result, indent);
} else {
log.add(LogType.MSG_CON_REIMPORT_SECRET_SKIP, indent);
@@ -1276,9 +1276,9 @@ public class ProviderHelper {
// 4. Re-Import public keyrings from cache
if (numPublics > 0) {
- ImportKeyResult result = new ImportExportOperation(mContext, this,
+ ImportKeyResult result = new ImportOperation(mContext, this,
new ProgressFixedScaler(progress, 25, 99, 100, R.string.progress_con_reimport))
- .importKeyRings(itPublics, numPublics, null);
+ .serialKeyRingImport(itPublics, numPublics, null);
log.add(result, indent);
} else {
log.add(LogType.MSG_CON_REIMPORT_PUBLIC_SKIP, indent);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index 0d2d3256d..ac66bd097 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -167,7 +167,7 @@ public class OpenPgpService extends RemoteService {
Intent data, RequiredInputParcel requiredInput) {
switch (requiredInput.mType) {
- case NFC_KEYTOCARD:
+ case NFC_MOVE_KEY_TO_CARD:
case NFC_DECRYPT:
case NFC_SIGN: {
// build PendingIntent for YubiKey NFC operations
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ConsolidateInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ConsolidateInputParcel.java
new file mode 100644
index 000000000..15d109814
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ConsolidateInputParcel.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class ConsolidateInputParcel implements Parcelable {
+
+ public boolean mConsolidateRecovery;
+
+ public ConsolidateInputParcel(boolean consolidateRecovery) {
+ mConsolidateRecovery = consolidateRecovery;
+ }
+
+ protected ConsolidateInputParcel(Parcel in) {
+ mConsolidateRecovery = in.readByte() != 0x00;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte((byte) (mConsolidateRecovery ? 0x01 : 0x00));
+ }
+
+ public static final Parcelable.Creator<ConsolidateInputParcel> CREATOR = new Parcelable.Creator<ConsolidateInputParcel>() {
+ @Override
+ public ConsolidateInputParcel createFromParcel(Parcel in) {
+ return new ConsolidateInputParcel(in);
+ }
+
+ @Override
+ public ConsolidateInputParcel[] newArray(int size) {
+ return new ConsolidateInputParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DeleteKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DeleteKeyringParcel.java
new file mode 100644
index 000000000..b412a6e2b
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DeleteKeyringParcel.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class DeleteKeyringParcel implements Parcelable {
+
+ public long[] mMasterKeyIds;
+ public boolean mIsSecret;
+
+ public DeleteKeyringParcel(long[] masterKeyIds, boolean isSecret) {
+ mMasterKeyIds = masterKeyIds;
+ mIsSecret = isSecret;
+ }
+
+ protected DeleteKeyringParcel(Parcel in) {
+ mIsSecret = in.readByte() != 0x00;
+ mMasterKeyIds = in.createLongArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte((byte) (mIsSecret ? 0x01 : 0x00));
+ dest.writeLongArray(mMasterKeyIds);
+ }
+
+ public static final Parcelable.Creator<DeleteKeyringParcel> CREATOR = new Parcelable.Creator<DeleteKeyringParcel>() {
+ @Override
+ public DeleteKeyringParcel createFromParcel(Parcel in) {
+ return new DeleteKeyringParcel(in);
+ }
+
+ @Override
+ public DeleteKeyringParcel[] newArray(int size) {
+ return new DeleteKeyringParcel[size];
+ }
+ };
+}
+
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java
new file mode 100644
index 000000000..ef5b48df3
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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.service;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class ExportKeyringParcel implements Parcelable {
+ public String mKeyserver;
+ public Uri mCanonicalizedPublicKeyringUri;
+
+ public boolean mExportSecret;
+ public long mMasterKeyIds[];
+ public String mOutputFile;
+ public Uri mOutputUri;
+ public ExportType mExportType;
+
+ public enum ExportType {
+ UPLOAD_KEYSERVER,
+ EXPORT_FILE,
+ EXPORT_URI
+ }
+
+ public ExportKeyringParcel(String keyserver, Uri keyringUri) {
+ mExportType = ExportType.UPLOAD_KEYSERVER;
+ mKeyserver = keyserver;
+ mCanonicalizedPublicKeyringUri = keyringUri;
+ }
+
+ public ExportKeyringParcel(long[] masterKeyIds, boolean exportSecret, String outputFile) {
+ mExportType = ExportType.EXPORT_FILE;
+ mMasterKeyIds = masterKeyIds;
+ mExportSecret = exportSecret;
+ mOutputFile = outputFile;
+ }
+
+ public ExportKeyringParcel(long[] masterKeyIds, boolean exportSecret, Uri outputUri) {
+ mExportType = ExportType.EXPORT_URI;
+ mMasterKeyIds = masterKeyIds;
+ mExportSecret = exportSecret;
+ mOutputUri = outputUri;
+ }
+
+ protected ExportKeyringParcel(Parcel in) {
+ mKeyserver = in.readString();
+ mCanonicalizedPublicKeyringUri = (Uri) in.readValue(Uri.class.getClassLoader());
+ mExportSecret = in.readByte() != 0x00;
+ mOutputFile = in.readString();
+ mOutputUri = (Uri) in.readValue(Uri.class.getClassLoader());
+ mExportType = (ExportType) in.readValue(ExportType.class.getClassLoader());
+ mMasterKeyIds = in.createLongArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mKeyserver);
+ dest.writeValue(mCanonicalizedPublicKeyringUri);
+ dest.writeByte((byte) (mExportSecret ? 0x01 : 0x00));
+ dest.writeString(mOutputFile);
+ dest.writeValue(mOutputUri);
+ dest.writeValue(mExportType);
+ dest.writeLongArray(mMasterKeyIds);
+ }
+
+ public static final Parcelable.Creator<ExportKeyringParcel> CREATOR = new Parcelable.Creator<ExportKeyringParcel>() {
+ @Override
+ public ExportKeyringParcel createFromParcel(Parcel in) {
+ return new ExportKeyringParcel(in);
+ }
+
+ @Override
+ public ExportKeyringParcel[] newArray(int size) {
+ return new ExportKeyringParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java
new file mode 100644
index 000000000..a41dd71cb
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+
+import java.util.ArrayList;
+
+public class ImportKeyringParcel implements Parcelable {
+ // if null, keys are expected to be read from a cache file in ImportExportOperations
+ public ArrayList<ParcelableKeyRing> mKeyList;
+ public String mKeyserver; // must be set if keys are to be imported from a keyserver
+
+ public ImportKeyringParcel (ArrayList<ParcelableKeyRing> keyList, String keyserver) {
+ mKeyList = keyList;
+ mKeyserver = keyserver;
+ }
+
+ protected ImportKeyringParcel(Parcel in) {
+ if (in.readByte() == 0x01) {
+ mKeyList = new ArrayList<>();
+ in.readList(mKeyList, ParcelableKeyRing.class.getClassLoader());
+ } else {
+ mKeyList = null;
+ }
+ mKeyserver = in.readString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (mKeyList == null) {
+ dest.writeByte((byte) (0x00));
+ } else {
+ dest.writeByte((byte) (0x01));
+ dest.writeList(mKeyList);
+ }
+ dest.writeString(mKeyserver);
+ }
+
+ public static final Parcelable.Creator<ImportKeyringParcel> CREATOR = new Parcelable.Creator<ImportKeyringParcel>() {
+ @Override
+ public ImportKeyringParcel createFromParcel(Parcel in) {
+ return new ImportKeyringParcel(in);
+ }
+
+ @Override
+ public ImportKeyringParcel[] newArray(int size) {
+ return new ImportKeyringParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeybaseVerificationParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeybaseVerificationParcel.java
new file mode 100644
index 000000000..1872191af
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeybaseVerificationParcel.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class KeybaseVerificationParcel implements Parcelable {
+
+ public String mKeybaseProof;
+ public String mRequiredFingerprint;
+
+ public KeybaseVerificationParcel(String keybaseProof, String requiredFingerprint) {
+ mKeybaseProof = keybaseProof;
+ mRequiredFingerprint = requiredFingerprint;
+ }
+
+ protected KeybaseVerificationParcel(Parcel in) {
+ mKeybaseProof = in.readString();
+ mRequiredFingerprint = in.readString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mKeybaseProof);
+ dest.writeString(mRequiredFingerprint);
+ }
+
+ public static final Parcelable.Creator<KeybaseVerificationParcel> CREATOR = new Parcelable.Creator<KeybaseVerificationParcel>() {
+ @Override
+ public KeybaseVerificationParcel createFromParcel(Parcel in) {
+ return new KeybaseVerificationParcel(in);
+ }
+
+ @Override
+ public KeybaseVerificationParcel[] newArray(int size) {
+ return new KeybaseVerificationParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainNewService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainNewService.java
deleted file mode 100644
index 9e33a1421..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainNewService.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 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.service;
-
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.Parcelable;
-import android.os.RemoteException;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.operations.BaseOperation;
-import org.sufficientlysecure.keychain.operations.CertifyOperation;
-import org.sufficientlysecure.keychain.operations.EditKeyOperation;
-import org.sufficientlysecure.keychain.operations.SignEncryptOperation;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
-import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
-import org.sufficientlysecure.keychain.pgp.Progressable;
-import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
-import org.sufficientlysecure.keychain.util.Log;
-
-/**
- * This Service contains all important long lasting operations for OpenKeychain. It receives Intents with
- * data from the activities or other apps, executes them, and stops itself after doing them.
- */
-public class KeychainNewService extends Service implements Progressable {
-
- // messenger for communication (hack)
- public static final String EXTRA_MESSENGER = "messenger";
-
- // extras for operation
- public static final String EXTRA_OPERATION_INPUT = "op_input";
- public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
-
- // this attribute can possibly merged with the one above? not sure...
- private AtomicBoolean mActionCanceled = new AtomicBoolean(false);
-
- ThreadLocal<Messenger> mMessenger = new ThreadLocal<>();
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- /**
- * This is run on the main thread, we need to spawn a runnable which runs on another thread for the actual operation
- */
- @Override
- public int onStartCommand(final Intent intent, int flags, int startId) {
-
- Runnable actionRunnable = new Runnable() {
- @Override
- public void run() {
- // We have not been cancelled! (yet)
- mActionCanceled.set(false);
-
- Bundle extras = intent.getExtras();
-
- // Set messenger for communication (for this particular thread)
- mMessenger.set(extras.<Messenger>getParcelable(EXTRA_MESSENGER));
-
- // Input
- Parcelable inputParcel = extras.getParcelable(EXTRA_OPERATION_INPUT);
- CryptoInputParcel cryptoInput = extras.getParcelable(EXTRA_CRYPTO_INPUT);
-
- // Operation
- BaseOperation op;
-
- // just for brevity
- KeychainNewService outerThis = KeychainNewService.this;
- if (inputParcel instanceof SignEncryptParcel) {
- op = new SignEncryptOperation(outerThis, new ProviderHelper(outerThis), outerThis, mActionCanceled);
- } else if (inputParcel instanceof PgpDecryptVerifyInputParcel) {
- op = new PgpDecryptVerify(outerThis, new ProviderHelper(outerThis), outerThis);
- } else if (inputParcel instanceof SaveKeyringParcel) {
- op = new EditKeyOperation(outerThis, new ProviderHelper(outerThis), outerThis, mActionCanceled);
- } else if (inputParcel instanceof CertifyAction) {
- op = new CertifyOperation(outerThis, new ProviderHelper(outerThis), outerThis, mActionCanceled);
- } else {
- return;
- }
-
- @SuppressWarnings("unchecked") // this is unchecked, we make sure it's the correct op above!
- OperationResult result = op.execute(inputParcel, cryptoInput);
-
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- }
- };
-
- Thread actionThread = new Thread(actionRunnable);
- actionThread.start();
-
- return START_NOT_STICKY;
- }
-
- private void sendMessageToHandler(MessageStatus status, Integer arg2, Bundle data) {
-
- Message msg = Message.obtain();
- assert msg != null;
- msg.arg1 = status.ordinal();
- if (arg2 != null) {
- msg.arg2 = arg2;
- }
- if (data != null) {
- msg.setData(data);
- }
-
- try {
- mMessenger.get().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);
- }
- }
-
- private void sendMessageToHandler(MessageStatus status, OperationResult data) {
- Bundle bundle = new Bundle();
- bundle.putParcelable(OperationResult.EXTRA_RESULT, data);
- sendMessageToHandler(status, null, bundle);
- }
-
- private void sendMessageToHandler(MessageStatus status) {
- sendMessageToHandler(status, null, null);
- }
-
- /**
- * Set progress of ProgressDialog by sending message to handler on UI thread
- */
- @Override
- public void setProgress(String message, int progress, int max) {
- Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max="
- + max);
-
- Bundle data = new Bundle();
- if (message != null) {
- data.putString(ServiceProgressHandler.DATA_MESSAGE, message);
- }
- data.putInt(ServiceProgressHandler.DATA_PROGRESS, progress);
- data.putInt(ServiceProgressHandler.DATA_PROGRESS_MAX, max);
-
- sendMessageToHandler(MessageStatus.UPDATE_PROGRESS, null, data);
- }
-
- @Override
- public void setProgress(int resourceId, int progress, int max) {
- setProgress(getString(resourceId), progress, max);
- }
-
- @Override
- public void setProgress(int progress, int max) {
- setProgress(null, progress, max);
- }
-
- @Override
- public void setPreventCancel() {
- sendMessageToHandler(MessageStatus.PREVENT_CANCEL);
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java
index ba877c2a2..1cd76b462 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java
@@ -20,70 +20,35 @@ package org.sufficientlysecure.keychain.service;
import android.app.Service;
import android.content.Intent;
-import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
+import android.os.Parcelable;
import android.os.RemoteException;
-import com.textuality.keybase.lib.Proof;
-import com.textuality.keybase.lib.prover.Prover;
-
-import org.json.JSONObject;
-import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
-import org.sufficientlysecure.keychain.keyimport.Keyserver;
-import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.operations.BaseOperation;
import org.sufficientlysecure.keychain.operations.CertifyOperation;
+import org.sufficientlysecure.keychain.operations.ConsolidateOperation;
import org.sufficientlysecure.keychain.operations.DeleteOperation;
import org.sufficientlysecure.keychain.operations.EditKeyOperation;
-import org.sufficientlysecure.keychain.operations.ImportExportOperation;
+import org.sufficientlysecure.keychain.operations.ExportOperation;
+import org.sufficientlysecure.keychain.operations.ImportOperation;
+import org.sufficientlysecure.keychain.operations.KeybaseVerificationOperation;
import org.sufficientlysecure.keychain.operations.PromoteKeyOperation;
import org.sufficientlysecure.keychain.operations.SignEncryptOperation;
-import org.sufficientlysecure.keychain.operations.results.CertifyResult;
-import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
-import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
-import org.sufficientlysecure.keychain.operations.results.DeleteResult;
-import org.sufficientlysecure.keychain.operations.results.ExportResult;
-import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
-import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
-import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.ParcelableFileCache;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import de.measite.minidns.Client;
-import de.measite.minidns.DNSMessage;
-import de.measite.minidns.Question;
-import de.measite.minidns.Record;
-import de.measite.minidns.record.Data;
-import de.measite.minidns.record.TXT;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* This Service contains all important long lasting operations for OpenKeychain. It receives Intents with
@@ -91,75 +56,19 @@ import de.measite.minidns.record.TXT;
*/
public class KeychainService extends Service implements Progressable {
- /* extras that can be given by intent */
+ // messenger for communication (hack)
public static final String EXTRA_MESSENGER = "messenger";
- public static final String EXTRA_DATA = "data";
-
- /* possible actions */
-
- public static final String ACTION_VERIFY_KEYBASE_PROOF = Constants.INTENT_PREFIX + "VERIFY_KEYBASE_PROOF";
-
- public static final String ACTION_EDIT_KEYRING = Constants.INTENT_PREFIX + "EDIT_KEYRING";
-
- public static final String ACTION_PROMOTE_KEYRING = Constants.INTENT_PREFIX + "PROMOTE_KEYRING";
-
- public static final String ACTION_IMPORT_KEYRING = Constants.INTENT_PREFIX + "IMPORT_KEYRING";
- public static final String ACTION_EXPORT_KEYRING = Constants.INTENT_PREFIX + "EXPORT_KEYRING";
-
- public static final String ACTION_UPLOAD_KEYRING = Constants.INTENT_PREFIX + "UPLOAD_KEYRING";
-
- public static final String ACTION_DELETE = Constants.INTENT_PREFIX + "DELETE";
- public static final String ACTION_CONSOLIDATE = Constants.INTENT_PREFIX + "CONSOLIDATE";
-
- public static final String ACTION_CANCEL = Constants.INTENT_PREFIX + "CANCEL";
-
- /* keys for data bundle */
-
- // keybase proof
- public static final String KEYBASE_REQUIRED_FINGERPRINT = "keybase_required_fingerprint";
- public static final String KEYBASE_PROOF = "keybase_proof";
-
- // save keyring
- public static final String EDIT_KEYRING_PARCEL = "save_parcel";
- public static final String EDIT_KEYRING_PASSPHRASE = "passphrase";
+ // extras for operation
+ public static final String EXTRA_OPERATION_INPUT = "op_input";
public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
- // delete keyring(s)
- public static final String DELETE_KEY_LIST = "delete_list";
- public static final String DELETE_IS_SECRET = "delete_is_secret";
-
- // import key
- public static final String IMPORT_KEY_LIST = "import_key_list";
- public static final String IMPORT_KEY_SERVER = "import_key_server";
-
- // export key
- public static final String EXPORT_FILENAME = "export_filename";
- public static final String EXPORT_URI = "export_uri";
- public static final String EXPORT_SECRET = "export_secret";
- public static final String EXPORT_ALL = "export_all";
- public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "export_key_ring_id";
-
- // upload key
- public static final String UPLOAD_KEY_SERVER = "upload_key_server";
-
- // promote key
- public static final String PROMOTE_MASTER_KEY_ID = "promote_master_key_id";
- public static final String PROMOTE_CARD_AID = "promote_card_aid";
- public static final String PROMOTE_SUBKEY_IDS = "promote_fingerprints";
-
- // consolidate
- public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery";
-
- Messenger mMessenger;
+ public static final String ACTION_CANCEL = "action_cancel";
// this attribute can possibly merged with the one above? not sure...
private AtomicBoolean mActionCanceled = new AtomicBoolean(false);
-
- private KeyImportAccumulator mKeyImportAccumulator;
-
- private KeychainService mKeychainService;
+ ThreadLocal<Messenger> mMessenger = new ThreadLocal<>();
@Override
public IBinder onBind(Intent intent) {
@@ -171,9 +80,8 @@ public class KeychainService extends Service implements Progressable {
*/
@Override
public int onStartCommand(final Intent intent, int flags, int startId) {
- mKeychainService = this;
- if (ACTION_CANCEL.equals(intent.getAction())) {
+ if (intent.getAction() != null && intent.getAction().equals(ACTION_CANCEL)) {
mActionCanceled.set(true);
return START_NOT_STICKY;
}
@@ -185,315 +93,62 @@ public class KeychainService extends Service implements Progressable {
mActionCanceled.set(false);
Bundle extras = intent.getExtras();
- if (extras == null) {
- Log.e(Constants.TAG, "Extras bundle is null!");
- return;
- }
- if (!(extras.containsKey(EXTRA_MESSENGER) || extras.containsKey(EXTRA_DATA) || (intent
- .getAction() == null))) {
- Log.e(Constants.TAG,
- "Extra bundle must contain a messenger, a data bundle, and an action!");
+ // Set messenger for communication (for this particular thread)
+ mMessenger.set(extras.<Messenger>getParcelable(EXTRA_MESSENGER));
+
+ // Input
+ Parcelable inputParcel = extras.getParcelable(EXTRA_OPERATION_INPUT);
+ CryptoInputParcel cryptoInput = extras.getParcelable(EXTRA_CRYPTO_INPUT);
+
+ // Operation
+ BaseOperation op;
+
+ // just for brevity
+ KeychainService outerThis = KeychainService.this;
+ if (inputParcel instanceof SignEncryptParcel) {
+ op = new SignEncryptOperation(outerThis, new ProviderHelper(outerThis),
+ outerThis, mActionCanceled);
+ } else if (inputParcel instanceof PgpDecryptVerifyInputParcel) {
+ op = new PgpDecryptVerify(outerThis, new ProviderHelper(outerThis), outerThis);
+ } else if (inputParcel instanceof SaveKeyringParcel) {
+ op = new EditKeyOperation(outerThis, new ProviderHelper(outerThis), outerThis,
+ mActionCanceled);
+ } else if (inputParcel instanceof CertifyActionsParcel) {
+ op = new CertifyOperation(outerThis, new ProviderHelper(outerThis), outerThis,
+ mActionCanceled);
+ } else if (inputParcel instanceof DeleteKeyringParcel) {
+ op = new DeleteOperation(outerThis, new ProviderHelper(outerThis), outerThis);
+ } else if (inputParcel instanceof PromoteKeyringParcel) {
+ op = new PromoteKeyOperation(outerThis, new ProviderHelper(outerThis),
+ outerThis, mActionCanceled);
+ } else if (inputParcel instanceof ImportKeyringParcel) {
+ op = new ImportOperation(outerThis, new ProviderHelper(outerThis), outerThis,
+ mActionCanceled);
+ } else if (inputParcel instanceof ExportKeyringParcel) {
+ op = new ExportOperation(outerThis, new ProviderHelper(outerThis), outerThis,
+ mActionCanceled);
+ } else if (inputParcel instanceof ConsolidateInputParcel) {
+ op = new ConsolidateOperation(outerThis, new ProviderHelper(outerThis),
+ outerThis);
+ } else if (inputParcel instanceof KeybaseVerificationParcel) {
+ op = new KeybaseVerificationOperation(outerThis, new ProviderHelper(outerThis),
+ outerThis);
+ } else {
return;
}
- Uri dataUri = intent.getData();
-
- mMessenger = (Messenger) extras.get(EXTRA_MESSENGER);
- Bundle data = extras.getBundle(EXTRA_DATA);
- if (data == null) {
- Log.e(Constants.TAG, "data extra is null!");
- return;
- }
+ @SuppressWarnings("unchecked") // this is unchecked, we make sure it's the correct op above!
+ OperationResult result = op.execute(inputParcel, cryptoInput);
+ sendMessageToHandler(MessageStatus.OKAY, result);
- Log.logDebugBundle(data, "EXTRA_DATA");
-
- ProviderHelper providerHelper = new ProviderHelper(mKeychainService);
-
- String action = intent.getAction();
-
- // executeServiceMethod action from extra bundle
- switch (action) {
- case ACTION_CONSOLIDATE: {
-
- // Operation
- ConsolidateResult result;
- if (data.containsKey(CONSOLIDATE_RECOVERY) && data.getBoolean(CONSOLIDATE_RECOVERY)) {
- result = providerHelper.consolidateDatabaseStep2(mKeychainService);
- } else {
- result = providerHelper.consolidateDatabaseStep1(mKeychainService);
- }
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_VERIFY_KEYBASE_PROOF: {
-
- try {
- Proof proof = new Proof(new JSONObject(data.getString(KEYBASE_PROOF)));
- setProgress(R.string.keybase_message_fetching_data, 0, 100);
-
- Prover prover = Prover.findProverFor(proof);
-
- if (prover == null) {
- sendProofError(getString(R.string.keybase_no_prover_found) + ": " + proof
- .getPrettyName());
- return;
- }
-
- if (!prover.fetchProofData()) {
- sendProofError(prover.getLog(), getString(R.string.keybase_problem_fetching_evidence));
- return;
- }
- String requiredFingerprint = data.getString(KEYBASE_REQUIRED_FINGERPRINT);
- if (!prover.checkFingerprint(requiredFingerprint)) {
- sendProofError(getString(R.string.keybase_key_mismatch));
- return;
- }
-
- String domain = prover.dnsTxtCheckRequired();
- if (domain != null) {
- DNSMessage dnsQuery = new Client().query(new Question(domain, Record.TYPE.TXT));
- if (dnsQuery == null) {
- sendProofError(prover.getLog(), getString(R.string.keybase_dns_query_failure));
- return;
- }
- Record[] records = dnsQuery.getAnswers();
- List<List<byte[]>> extents = new ArrayList<List<byte[]>>();
- for (Record r : records) {
- Data d = r.getPayload();
- if (d instanceof TXT) {
- extents.add(((TXT) d).getExtents());
- }
- }
- if (!prover.checkDnsTxt(extents)) {
- sendProofError(prover.getLog(), null);
- return;
- }
- }
-
- byte[] messageBytes = prover.getPgpMessage().getBytes();
- if (prover.rawMessageCheckRequired()) {
- InputStream messageByteStream = PGPUtil.getDecoderStream(new ByteArrayInputStream
- (messageBytes));
- if (!prover.checkRawMessageBytes(messageByteStream)) {
- sendProofError(prover.getLog(), null);
- return;
- }
- }
-
- PgpDecryptVerify op = new PgpDecryptVerify(mKeychainService, providerHelper,
- mKeychainService);
-
- PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(messageBytes)
- .setSignedLiteralData(true)
- .setRequiredSignerFingerprint(requiredFingerprint);
-
- DecryptVerifyResult decryptVerifyResult = op.execute(input, new CryptoInputParcel());
-
- if (!decryptVerifyResult.success()) {
- OperationLog log = decryptVerifyResult.getLog();
- OperationResult.LogEntryParcel lastEntry = null;
- for (OperationResult.LogEntryParcel entry : log) {
- lastEntry = entry;
- }
- sendProofError(getString(lastEntry.mType.getMsgId()));
- return;
- }
-
- if (!prover.validate(new String(decryptVerifyResult.getOutputBytes()))) {
- sendProofError(getString(R.string.keybase_message_payload_mismatch));
- return;
- }
-
- Bundle resultData = new Bundle();
- resultData.putString(ServiceProgressHandler.DATA_MESSAGE, "OK");
-
- // these help the handler construct a useful human-readable message
- resultData.putString(ServiceProgressHandler.KEYBASE_PROOF_URL, prover.getProofUrl());
- resultData.putString(ServiceProgressHandler.KEYBASE_PRESENCE_URL, prover.getPresenceUrl());
- resultData.putString(ServiceProgressHandler.KEYBASE_PRESENCE_LABEL, prover
- .getPresenceLabel());
- sendMessageToHandler(MessageStatus.OKAY, resultData);
- } catch (Exception e) {
- sendErrorToHandler(e);
- }
-
- break;
- }
- case ACTION_DELETE: {
-
- // Input
- long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST);
- boolean isSecret = data.getBoolean(DELETE_IS_SECRET);
-
- // Operation
- DeleteOperation op = new DeleteOperation(mKeychainService, providerHelper, mKeychainService);
- DeleteResult result = op.execute(masterKeyIds, isSecret);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_EDIT_KEYRING: {
-
- // Input
- SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL);
- CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
-
- // Operation
- EditKeyOperation op = new EditKeyOperation(mKeychainService, providerHelper,
- mKeychainService, mActionCanceled);
- OperationResult result = op.execute(saveParcel, cryptoInput);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_PROMOTE_KEYRING: {
-
- // Input
- long keyRingId = data.getLong(PROMOTE_MASTER_KEY_ID);
- byte[] cardAid = data.getByteArray(PROMOTE_CARD_AID);
- long[] subKeyIds = data.getLongArray(PROMOTE_SUBKEY_IDS);
-
- // Operation
- PromoteKeyOperation op = new PromoteKeyOperation(
- mKeychainService, providerHelper, mKeychainService,
- mActionCanceled);
- PromoteKeyResult result = op.execute(keyRingId, cardAid, subKeyIds);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_EXPORT_KEYRING: {
-
- // Input
- boolean exportSecret = data.getBoolean(EXPORT_SECRET, false);
- String outputFile = data.getString(EXPORT_FILENAME);
- Uri outputUri = data.getParcelable(EXPORT_URI);
-
- boolean exportAll = data.getBoolean(EXPORT_ALL);
- long[] masterKeyIds = exportAll ? null : data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);
-
- // Operation
- ImportExportOperation importExportOperation = new ImportExportOperation(
- mKeychainService, providerHelper, mKeychainService);
- ExportResult result;
- if (outputFile != null) {
- result = importExportOperation.exportToFile(masterKeyIds, exportSecret, outputFile);
- } else {
- result = importExportOperation.exportToUri(masterKeyIds, exportSecret, outputUri);
- }
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_IMPORT_KEYRING: {
-
- // Input
- String keyServer = data.getString(IMPORT_KEY_SERVER);
- ArrayList<ParcelableKeyRing> keyList = data.getParcelableArrayList(IMPORT_KEY_LIST);
-
- // either keyList or cache must be null, no guarantees otherwise
- if (keyList == null) {// import from file, do serially
- serialKeyImport(null, keyServer, providerHelper);
- } else {
- // if there is more than one key with the same fingerprint, we do a serial import to prevent
- // https://github.com/open-keychain/open-keychain/issues/1221
- HashSet<String> keyFingerprintSet = new HashSet<>();
- for (int i = 0; i < keyList.size(); i++) {
- keyFingerprintSet.add(keyList.get(i).mExpectedFingerprint);
- }
- if (keyFingerprintSet.size() == keyList.size()) {
- // all keys have unique fingerprints
- multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer);
- } else {
- serialKeyImport(keyList, keyServer, providerHelper);
- }
- }
-
- break;
- }
- case ACTION_UPLOAD_KEYRING: {
- try {
-
- // Input
- String keyServer = data.getString(UPLOAD_KEY_SERVER);
- // and dataUri!
-
- // Operation
- HkpKeyserver server = new HkpKeyserver(keyServer);
-
- CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri);
- ImportExportOperation importExportOperation = new ImportExportOperation(mKeychainService,
- providerHelper, mKeychainService);
-
- try {
- importExportOperation.uploadKeyRingToServer(server, keyring);
- } catch (Keyserver.AddKeyException e) {
- throw new PgpGeneralException("Unable to export key to selected server");
- }
-
- sendMessageToHandler(MessageStatus.OKAY);
- } catch (Exception e) {
- sendErrorToHandler(e);
- }
- break;
- }
- }
- if (!intent.getAction().equals(ACTION_IMPORT_KEYRING)) {
- // import keyring handles stopping service on its own
- stopSelf();
- }
}
};
Thread actionThread = new Thread(actionRunnable);
actionThread.start();
-
- return START_NOT_STICKY;
- }
-
- private void sendProofError(List<String> log, String label) {
- String msg = null;
- label = (label == null) ? "" : label + ": ";
- for (String m : log) {
- Log.e(Constants.TAG, label + m);
- msg = m;
- }
- sendProofError(label + msg);
- }
- private void sendProofError(String msg) {
- Bundle bundle = new Bundle();
- bundle.putString(ServiceProgressHandler.DATA_ERROR, msg);
- sendMessageToHandler(MessageStatus.OKAY, bundle);
- }
-
- private void sendErrorToHandler(Exception e) {
- // TODO: Implement a better exception handling here
- // contextualize the exception, if necessary
- String message;
- if (e instanceof PgpGeneralMsgIdException) {
- e = ((PgpGeneralMsgIdException) e).getContextualized(mKeychainService);
- message = e.getMessage();
- } else {
- message = e.getMessage();
- }
- Log.d(Constants.TAG, "KeychainService Exception: ", e);
-
- Bundle data = new Bundle();
- data.putString(ServiceProgressHandler.DATA_ERROR, message);
- sendMessageToHandler(MessageStatus.EXCEPTION, null, data);
+ return START_NOT_STICKY;
}
private void sendMessageToHandler(MessageStatus status, Integer arg2, Bundle data) {
@@ -509,7 +164,7 @@ public class KeychainService extends Service implements Progressable {
}
try {
- mMessenger.send(msg);
+ mMessenger.get().send(msg);
} catch (RemoteException e) {
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
} catch (NullPointerException e) {
@@ -523,10 +178,6 @@ public class KeychainService extends Service implements Progressable {
sendMessageToHandler(status, null, bundle);
}
- private void sendMessageToHandler(MessageStatus status, Bundle data) {
- sendMessageToHandler(status, null, data);
- }
-
private void sendMessageToHandler(MessageStatus status) {
sendMessageToHandler(status, null, null);
}
@@ -564,214 +215,4 @@ public class KeychainService extends Service implements Progressable {
sendMessageToHandler(MessageStatus.PREVENT_CANCEL);
}
- public void serialKeyImport(ArrayList<ParcelableKeyRing> keyList, final String keyServer,
- ProviderHelper providerHelper) {
- Log.d(Constants.TAG, "serial key import starting");
- ParcelableFileCache<ParcelableKeyRing> cache =
- new ParcelableFileCache<>(mKeychainService, "key_import.pcl");
-
- // Operation
- ImportExportOperation importExportOperation = new ImportExportOperation(
- mKeychainService, providerHelper, mKeychainService,
- mActionCanceled);
- // Either list or cache must be null, no guarantees otherwise.
- ImportKeyResult result = keyList != null
- ? importExportOperation.importKeyRings(keyList, keyServer)
- : importExportOperation.importKeyRings(cache, keyServer);
-
- ContactSyncAdapterService.requestSync();
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- stopSelf();
- }
-
- public void multiThreadedKeyImport(Iterator<ParcelableKeyRing> keyListIterator, int totKeys, final String
- keyServer) {
- Log.d(Constants.TAG, "Multi-threaded key import starting");
- if (keyListIterator != null) {
- mKeyImportAccumulator = new KeyImportAccumulator(totKeys, mKeychainService);
- setProgress(0, totKeys);
-
- final int maxThreads = 200;
- ExecutorService importExecutor = new ThreadPoolExecutor(0, maxThreads,
- 30L, TimeUnit.SECONDS,
- new SynchronousQueue<Runnable>());
-
- while (keyListIterator.hasNext()) {
-
- final ParcelableKeyRing pkRing = keyListIterator.next();
-
- Runnable importOperationRunnable = new Runnable() {
-
- @Override
- public void run() {
- ImportKeyResult result = null;
- try {
- ImportExportOperation importExportOperation = new ImportExportOperation(
- mKeychainService,
- new ProviderHelper(mKeychainService),
- mKeyImportAccumulator.getImportProgressable(),
- mActionCanceled);
-
- ArrayList<ParcelableKeyRing> list = new ArrayList<>();
- list.add(pkRing);
-
- result = importExportOperation.importKeyRings(list,
- keyServer);
- } finally {
- // in the off-chance that importKeyRings does something to crash the
- // thread before it can call singleKeyRingImportCompleted, our imported
- // key count will go wrong. This will cause the service to never die,
- // and the progress dialog to stay displayed. The finally block was
- // originally meant to ensure singleKeyRingImportCompleted was called,
- // and checks for null were to be introduced, but in such a scenario,
- // knowing an uncaught error exists in importKeyRings is more important.
-
- // if a null gets passed, something wrong is happening. We want a crash.
-
- mKeyImportAccumulator.singleKeyRingImportCompleted(result);
- }
- }
- };
-
- importExecutor.execute(importOperationRunnable);
- }
- }
- }
-
- /**
- * Used to accumulate the results of individual key imports
- */
- private class KeyImportAccumulator {
- private OperationResult.OperationLog mImportLog = new OperationResult.OperationLog();
- private int mTotalKeys;
- private int mImportedKeys = 0;
- private Progressable mInternalProgressable;
- ArrayList<Long> mImportedMasterKeyIds = new ArrayList<Long>();
- private int mBadKeys = 0;
- private int mNewKeys = 0;
- private int mUpdatedKeys = 0;
- private int mSecret = 0;
- private int mResultType = 0;
-
- /**
- * meant to be used with a service due to stopSelf() in singleKeyRingImportCompleted. Remove this if
- * generalising.
- *
- * @param totalKeys total number of keys to be imported
- * @param externalProgressable the external progressable to be updated every time a key is imported
- */
- public KeyImportAccumulator(int totalKeys, Progressable externalProgressable) {
- mTotalKeys = totalKeys;
- // ignore updates from ImportExportOperation for now
- mInternalProgressable = new Progressable() {
- @Override
- public void setProgress(String message, int current, int total) {
-
- }
-
- @Override
- public void setProgress(int resourceId, int current, int total) {
-
- }
-
- @Override
- public void setProgress(int current, int total) {
-
- }
-
- @Override
- public void setPreventCancel() {
-
- }
- };
- }
-
- private synchronized void singleKeyRingImportCompleted(ImportKeyResult result) {
- // increase imported key count and accumulate log and bad, new etc. key counts from result
- mKeyImportAccumulator.accumulateKeyImport(result);
-
- setProgress(mKeyImportAccumulator.getImportedKeys(), mKeyImportAccumulator.getTotalKeys());
-
- if (mKeyImportAccumulator.isImportFinished()) {
- ContactSyncAdapterService.requestSync();
-
- sendMessageToHandler(ServiceProgressHandler.MessageStatus.OKAY,
- mKeyImportAccumulator.getConsolidatedImportKeyResult());
-
- stopSelf();//we're done here
- }
- }
-
- public Progressable getImportProgressable() {
- return mInternalProgressable;
- }
-
- public int getTotalKeys() {
- return mTotalKeys;
- }
-
- public int getImportedKeys() {
- return mImportedKeys;
- }
-
- public synchronized void accumulateKeyImport(ImportKeyResult result) {
- mImportedKeys++;
- mImportLog.addAll(result.getLog().toList());//accumulates log
- mBadKeys += result.mBadKeys;
- mNewKeys += result.mNewKeys;
- mUpdatedKeys += result.mUpdatedKeys;
- mSecret += result.mSecret;
-
- long[] masterKeyIds = result.getImportedMasterKeyIds();
- for (long masterKeyId : masterKeyIds) {
- mImportedMasterKeyIds.add(masterKeyId);
- }
-
- // if any key import has been cancelled, set result type to cancelled
- // resultType is added to in getConsolidatedKayImport to account for remaining factors
- mResultType |= result.getResult() & ImportKeyResult.RESULT_CANCELLED;
- }
-
- /**
- * returns accumulated result of all imports so far
- */
- public ImportKeyResult getConsolidatedImportKeyResult() {
-
- // adding required information to mResultType
- // special case,no keys requested for import
- if (mBadKeys == 0 && mNewKeys == 0 && mUpdatedKeys == 0) {
- mResultType = ImportKeyResult.RESULT_FAIL_NOTHING;
- } else {
- if (mNewKeys > 0) {
- mResultType |= ImportKeyResult.RESULT_OK_NEWKEYS;
- }
- if (mUpdatedKeys > 0) {
- mResultType |= ImportKeyResult.RESULT_OK_UPDATED;
- }
- if (mBadKeys > 0) {
- mResultType |= ImportKeyResult.RESULT_WITH_ERRORS;
- if (mNewKeys == 0 && mUpdatedKeys == 0) {
- mResultType |= ImportKeyResult.RESULT_ERROR;
- }
- }
- if (mImportLog.containsWarnings()) {
- mResultType |= ImportKeyResult.RESULT_WARNINGS;
- }
- }
-
- long masterKeyIds[] = new long[mImportedMasterKeyIds.size()];
- for (int i = 0; i < masterKeyIds.length; i++) {
- masterKeyIds[i] = mImportedMasterKeyIds.get(i);
- }
-
- return new ImportKeyResult(mResultType, mImportLog, mNewKeys, mUpdatedKeys, mBadKeys,
- mSecret, masterKeyIds);
- }
-
- public boolean isImportFinished() {
- return mTotalKeys == mImportedKeys;
- }
- }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
index a0b470add..7c0b7eaef 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -459,11 +459,16 @@ public class PassphraseCacheService extends Service {
* Called when one specific passphrase for keyId timed out
*/
private void timeout(long keyId) {
+
CachedPassphrase cPass = mPassphraseCache.get(keyId);
- // clean internal char[] from memory!
- cPass.getPassphrase().removeFromMemory();
- // remove passphrase object
- mPassphraseCache.remove(keyId);
+ if (cPass != null) {
+ if (cPass.getPassphrase() != null) {
+ // clean internal char[] from memory!
+ cPass.getPassphrase().removeFromMemory();
+ }
+ // remove passphrase object
+ mPassphraseCache.remove(keyId);
+ }
Log.d(Constants.TAG, "PassphraseCacheService Timeout of keyId " + keyId + ", removed from memory!");
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java
new file mode 100644
index 000000000..d268c3694
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class PromoteKeyringParcel implements Parcelable {
+
+ public long mKeyRingId;
+ public byte[] mCardAid;
+ public long[] mSubKeyIds;
+
+ public PromoteKeyringParcel(long keyRingId, byte[] cardAid, long[] subKeyIds) {
+ mKeyRingId = keyRingId;
+ mCardAid = cardAid;
+ mSubKeyIds = subKeyIds;
+ }
+
+ protected PromoteKeyringParcel(Parcel in) {
+ mKeyRingId = in.readLong();
+ mCardAid = in.createByteArray();
+ mSubKeyIds = in.createLongArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mKeyRingId);
+ dest.writeByteArray(mCardAid);
+ dest.writeLongArray(mSubKeyIds);
+ }
+
+ public static final Parcelable.Creator<PromoteKeyringParcel> CREATOR = new Parcelable.Creator<PromoteKeyringParcel>() {
+ @Override
+ public PromoteKeyringParcel createFromParcel(Parcel in) {
+ return new PromoteKeyringParcel(in);
+ }
+
+ @Override
+ public PromoteKeyringParcel[] newArray(int size) {
+ return new PromoteKeyringParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java
index 989b0c4bd..d294e5057 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java
@@ -64,6 +64,8 @@ public class ServiceProgressHandler extends Handler {
public static final String KEYBASE_PRESENCE_URL = "keybase_presence_url";
public static final String KEYBASE_PRESENCE_LABEL = "keybase_presence_label";
+ public static final String TAG_PROGRESS_DIALOG = "progressDialog";
+
FragmentActivity mActivity;
public ServiceProgressHandler(FragmentActivity activity) {
@@ -88,7 +90,7 @@ public class ServiceProgressHandler extends Handler {
Handler handler = new Handler();
handler.post(new Runnable() {
public void run() {
- frag.show(manager, "progressDialog");
+ frag.show(manager, TAG_PROGRESS_DIALOG);
}
});
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java
index 930c2ee4f..efe844145 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java
@@ -12,7 +12,7 @@ import android.os.Parcelable;
public class RequiredInputParcel implements Parcelable {
public enum RequiredInputType {
- PASSPHRASE, PASSPHRASE_SYMMETRIC, NFC_SIGN, NFC_DECRYPT, NFC_KEYTOCARD
+ PASSPHRASE, PASSPHRASE_SYMMETRIC, NFC_SIGN, NFC_DECRYPT, NFC_MOVE_KEY_TO_CARD
}
public Date mSignatureTime;
@@ -226,7 +226,7 @@ public class RequiredInputParcel implements Parcelable {
ByteBuffer buf = ByteBuffer.wrap(mSubkeysToExport.get(0));
// We need to pass in a subkey here...
- return new RequiredInputParcel(RequiredInputType.NFC_KEYTOCARD,
+ return new RequiredInputParcel(RequiredInputType.NFC_MOVE_KEY_TO_CARD,
inputHashes, null, null, mMasterKeyId, buf.getLong());
}
@@ -241,7 +241,7 @@ public class RequiredInputParcel implements Parcelable {
if (!mMasterKeyId.equals(input.mMasterKeyId)) {
throw new AssertionError("Master keys must match, this is a programming error!");
}
- if (input.mType != RequiredInputType.NFC_KEYTOCARD) {
+ if (input.mType != RequiredInputType.NFC_MOVE_KEY_TO_CARD) {
throw new AssertionError("Operation types must match, this is a programming error!");
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
index e39a3a0bf..891c2268c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
@@ -19,15 +19,12 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.os.Parcel;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
@@ -52,15 +49,11 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
-import org.sufficientlysecure.keychain.service.KeychainService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.adapter.MultiUserIdsAdapter;
import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner;
import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList;
@@ -302,7 +295,7 @@ public class CertifyKeyFragment
}
@Override
- protected CertifyActionsParcel createOperationInput() {
+ public CertifyActionsParcel createOperationInput() {
// Bail out if there is not at least one user id selected
ArrayList<CertifyAction> certifyActions = mUserIdsAdapter.getSelectedCertifyActions();
@@ -325,7 +318,7 @@ public class CertifyKeyFragment
}
@Override
- protected void onCryptoOperationSuccess(CertifyResult result) {
+ public void onCryptoOperationSuccess(CertifyResult result) {
Intent intent = new Intent();
intent.putExtra(CertifyResult.EXTRA_RESULT, result);
getActivity().setResult(Activity.RESULT_OK, intent);
@@ -333,7 +326,7 @@ public class CertifyKeyFragment
}
@Override
- protected void onCryptoOperationCancelled() {
+ public void onCryptoOperationCancelled() {
super.onCryptoOperationCancelled();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java
index 6a9bb7b11..7a473e49f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java
@@ -17,25 +17,27 @@
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.support.v4.app.FragmentActivity;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.service.KeychainService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
+import org.sufficientlysecure.keychain.service.ConsolidateInputParcel;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
/**
* We can not directly create a dialog on the application context.
* This activity encapsulates a DialogFragment to emulate a dialog.
*/
-public class ConsolidateDialogActivity extends FragmentActivity {
+public class ConsolidateDialogActivity extends FragmentActivity
+ implements CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult> {
public static final String EXTRA_CONSOLIDATE_RECOVERY = "consolidate_recovery";
+ private CryptoOperationHelper<ConsolidateInputParcel, ConsolidateResult> mConsolidateOpHelper;
+ private boolean mRecovery;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -48,55 +50,45 @@ public class ConsolidateDialogActivity extends FragmentActivity {
}
private void consolidateRecovery(boolean recovery) {
- // Message is received after importing is done in KeychainService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(this) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- /* don't care about the results (for now?)
-
- // get returned data bundle
- Bundle returnData = message.getInputData();
- if (returnData == null) {
- return;
- }
- final ConsolidateResult result =
- returnData.getParcelable(KeychainService.RESULT_CONSOLIDATE);
- if (result == null) {
- return;
- }
- result.createNotify(ConsolidateDialogActivity.this).show();
- */
-
- ConsolidateDialogActivity.this.finish();
- }
- }
- };
-
- // Send all information needed to service to import key in other thread
- Intent intent = new Intent(this, KeychainService.class);
- intent.setAction(KeychainService.ACTION_CONSOLIDATE);
-
- // fill values for this action
- Bundle data = new Bundle();
- data.putBoolean(KeychainService.CONSOLIDATE_RECOVERY, recovery);
- intent.putExtra(KeychainService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL, false
- );
-
- // start service with intent
- startService(intent);
+
+ mRecovery = recovery;
+
+ mConsolidateOpHelper = new CryptoOperationHelper<>(this, this, R.string.progress_importing);
+ mConsolidateOpHelper.cryptoOperation();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (mConsolidateOpHelper != null) {
+ mConsolidateOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
}
+ @Override
+ public ConsolidateInputParcel createOperationInput() {
+ return new ConsolidateInputParcel(mRecovery);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(ConsolidateResult result) {
+ // don't care about result (for now?)
+ ConsolidateDialogActivity.this.finish();
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+
+ }
+
+ @Override
+ public void onCryptoOperationError(ConsolidateResult result) {
+ // don't care about result (for now?)
+ ConsolidateDialogActivity.this.finish();
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
index 68a809b69..36ab62cb4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
@@ -17,9 +17,8 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
import android.content.Intent;
+import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
@@ -43,7 +42,9 @@ public class CreateKeyActivity extends BaseNfcActivity {
public static final String EXTRA_FIRST_TIME = "first_time";
public static final String EXTRA_ADDITIONAL_EMAILS = "additional_emails";
public static final String EXTRA_PASSPHRASE = "passphrase";
- public static final String EXTRA_USE_SMART_CARD_SETTINGS = "use_smart_card_settings";
+ public static final String EXTRA_CREATE_YUBI_KEY = "create_yubi_key";
+ public static final String EXTRA_YUBI_KEY_PIN = "yubi_key_pin";
+ public static final String EXTRA_YUBI_KEY_ADMIN_PIN = "yubi_key_admin_pin";
public static final String EXTRA_NFC_USER_ID = "nfc_user_id";
public static final String EXTRA_NFC_AID = "nfc_aid";
@@ -56,7 +57,9 @@ public class CreateKeyActivity extends BaseNfcActivity {
ArrayList<String> mAdditionalEmails;
Passphrase mPassphrase;
boolean mFirstTime;
- boolean mUseSmartCardSettings;
+ boolean mCreateYubiKey;
+ String mYubiKeyPin;
+ String mYubiKeyAdminPin;
Fragment mCurrentFragment;
@@ -64,6 +67,23 @@ public class CreateKeyActivity extends BaseNfcActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ // React on NDEF_DISCOVERED from Manifest
+ // NOTE: ACTION_NDEF_DISCOVERED and not ACTION_TAG_DISCOVERED like in BaseNfcActivity
+ if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
+ try {
+ handleTagDiscoveredIntent(getIntent());
+ } catch (CardException e) {
+ handleNfcError(e);
+ } catch (IOException e) {
+ handleNfcError(e);
+ }
+
+ setTitle(R.string.title_manage_my_keys);
+
+ // done
+ return;
+ }
+
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
// Restore value of members from saved state
@@ -72,7 +92,9 @@ public class CreateKeyActivity extends BaseNfcActivity {
mAdditionalEmails = savedInstanceState.getStringArrayList(EXTRA_ADDITIONAL_EMAILS);
mPassphrase = savedInstanceState.getParcelable(EXTRA_PASSPHRASE);
mFirstTime = savedInstanceState.getBoolean(EXTRA_FIRST_TIME);
- mUseSmartCardSettings = savedInstanceState.getBoolean(EXTRA_USE_SMART_CARD_SETTINGS);
+ mCreateYubiKey = savedInstanceState.getBoolean(EXTRA_CREATE_YUBI_KEY);
+ mYubiKeyPin = savedInstanceState.getString(EXTRA_YUBI_KEY_PIN);
+ mYubiKeyAdminPin = savedInstanceState.getString(EXTRA_YUBI_KEY_ADMIN_PIN);
mCurrentFragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG);
} else {
@@ -82,24 +104,32 @@ public class CreateKeyActivity extends BaseNfcActivity {
mName = intent.getStringExtra(EXTRA_NAME);
mEmail = intent.getStringExtra(EXTRA_EMAIL);
mFirstTime = intent.getBooleanExtra(EXTRA_FIRST_TIME, false);
- mUseSmartCardSettings = intent.getBooleanExtra(EXTRA_USE_SMART_CARD_SETTINGS, false);
+ mCreateYubiKey = intent.getBooleanExtra(EXTRA_CREATE_YUBI_KEY, false);
if (intent.hasExtra(EXTRA_NFC_FINGERPRINTS)) {
byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_NFC_FINGERPRINTS);
String nfcUserId = intent.getStringExtra(EXTRA_NFC_USER_ID);
byte[] nfcAid = intent.getByteArrayExtra(EXTRA_NFC_AID);
- Fragment frag2 = CreateKeyYubiKeyImportFragment.createInstance(
- nfcFingerprints, nfcAid, nfcUserId);
- loadFragment(frag2, FragAction.START);
+ if (containsKeys(nfcFingerprints)) {
+ Fragment frag = CreateYubiKeyImportFragment.newInstance(
+ nfcFingerprints, nfcAid, nfcUserId);
+ loadFragment(frag, FragAction.START);
+
+ setTitle(R.string.title_import_keys);
+ } else {
+ Fragment frag = CreateYubiKeyBlankFragment.newInstance();
+ loadFragment(frag, FragAction.START);
+ setTitle(R.string.title_manage_my_keys);
+ }
- setTitle(R.string.title_import_keys);
+ // done
return;
- } else {
- CreateKeyStartFragment frag = CreateKeyStartFragment.newInstance();
- loadFragment(frag, FragAction.START);
}
+ // normal key creation
+ CreateKeyStartFragment frag = CreateKeyStartFragment.newInstance();
+ loadFragment(frag, FragAction.START);
}
if (mFirstTime) {
@@ -122,16 +152,7 @@ public class CreateKeyActivity extends BaseNfcActivity {
byte[] nfcAid = nfcGetAid();
String userId = nfcGetUserId();
- // If all fingerprint bytes are 0, the card contains no keys.
- boolean cardContainsKeys = false;
- for (byte b : scannedFingerprints) {
- if (b != 0) {
- cardContainsKeys = true;
- break;
- }
- }
-
- if (cardContainsKeys) {
+ if (containsKeys(scannedFingerprints)) {
try {
long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(scannedFingerprints);
CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(masterKeyId);
@@ -146,25 +167,29 @@ public class CreateKeyActivity extends BaseNfcActivity {
finish();
} catch (PgpKeyNotFoundException e) {
- Fragment frag = CreateKeyYubiKeyImportFragment.createInstance(
+ Fragment frag = CreateYubiKeyImportFragment.newInstance(
scannedFingerprints, nfcAid, userId);
loadFragment(frag, FragAction.TO_RIGHT);
}
} else {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.first_time_blank_smartcard_title)
- .setMessage(R.string.first_time_blank_smartcard_message)
- .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int button) {
- CreateKeyActivity.this.mUseSmartCardSettings = true;
- }
- })
- .setNegativeButton(android.R.string.no, null).show();
+ Fragment frag = CreateYubiKeyBlankFragment.newInstance();
+ loadFragment(frag, FragAction.TO_RIGHT);
}
}
+ private boolean containsKeys(byte[] scannedFingerprints) {
+ // If all fingerprint bytes are 0, the card contains no keys.
+ boolean cardContainsKeys = false;
+ for (byte b : scannedFingerprints) {
+ if (b != 0) {
+ cardContainsKeys = true;
+ break;
+ }
+ }
+ return cardContainsKeys;
+ }
+
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@@ -174,7 +199,9 @@ public class CreateKeyActivity extends BaseNfcActivity {
outState.putStringArrayList(EXTRA_ADDITIONAL_EMAILS, mAdditionalEmails);
outState.putParcelable(EXTRA_PASSPHRASE, mPassphrase);
outState.putBoolean(EXTRA_FIRST_TIME, mFirstTime);
- outState.putBoolean(EXTRA_USE_SMART_CARD_SETTINGS, mUseSmartCardSettings);
+ outState.putBoolean(EXTRA_CREATE_YUBI_KEY, mCreateYubiKey);
+ outState.putString(EXTRA_YUBI_KEY_PIN, mYubiKeyPin);
+ outState.putString(EXTRA_YUBI_KEY_ADMIN_PIN, mYubiKeyAdminPin);
}
@Override
@@ -182,7 +209,7 @@ public class CreateKeyActivity extends BaseNfcActivity {
setContentView(R.layout.create_key_activity);
}
- public static enum FragAction {
+ public enum FragAction {
START,
TO_RIGHT,
TO_LEFT
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java
index 597f04d6b..69dd232cf 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
+import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -29,6 +30,7 @@ import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
@@ -37,9 +39,9 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.ui.dialog.AddEmailDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.widget.EmailEditText;
+import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.ArrayList;
import java.util.List;
@@ -201,7 +203,7 @@ public class CreateKeyEmailFragment extends Fragment {
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
- if (message.what == SetPassphraseDialogFragment.MESSAGE_OKAY) {
+ if (message.what == AddEmailDialogFragment.MESSAGE_OKAY) {
Bundle data = message.getData();
String email = data.getString(AddEmailDialogFragment.MESSAGE_DATA_EMAIL);
@@ -232,11 +234,38 @@ public class CreateKeyEmailFragment extends Fragment {
mCreateKeyActivity.mEmail = mEmailEdit.getText().toString();
mCreateKeyActivity.mAdditionalEmails = getAdditionalEmails();
- CreateKeyPassphraseFragment frag = CreateKeyPassphraseFragment.newInstance();
- mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
+ CreateKeyActivity createKeyActivity = ((CreateKeyActivity) getActivity());
+
+ if (createKeyActivity.mCreateYubiKey) {
+ hideKeyboard();
+
+ // set empty passphrase
+ createKeyActivity.mPassphrase = new Passphrase();
+
+ CreateYubiKeyPinFragment frag = CreateYubiKeyPinFragment.newInstance();
+ mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
+ } else {
+ CreateKeyPassphraseFragment frag = CreateKeyPassphraseFragment.newInstance();
+ mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
+ }
}
}
+ 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);
+ }
+
private ArrayList<String> getAdditionalEmails() {
ArrayList<String> emails = new ArrayList<>();
for (EmailAdapter.ViewModel holder : mAdditionalEmailModels) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
index ebbd01afe..8c7abb874 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014-2015 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
@@ -18,12 +18,10 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
-import android.app.ProgressDialog;
import android.content.Intent;
+import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
@@ -35,15 +33,19 @@ import org.spongycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
+import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
+import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
-import org.sufficientlysecure.keychain.service.KeychainService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
@@ -63,6 +65,10 @@ public class CreateKeyFinalFragment extends Fragment {
SaveKeyringParcel mSaveKeyringParcel;
+ private CryptoOperationHelper<ExportKeyringParcel, ExportResult> mUploadOpHelper;
+ private CryptoOperationHelper<SaveKeyringParcel, EditKeyResult> mCreateOpHelper;
+ private CryptoOperationHelper<SaveKeyringParcel, EditKeyResult> mMoveToCardOpHelper;
+
public static CreateKeyFinalFragment newInstance() {
CreateKeyFinalFragment frag = new CreateKeyFinalFragment();
@@ -129,11 +135,26 @@ public class CreateKeyFinalFragment extends Fragment {
}
});
+ // If this is a debug build, don't upload by default
+ if (Constants.DEBUG) {
+ mUploadCheckbox.setChecked(false);
+ }
+
return view;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mCreateOpHelper != null) {
+ mCreateOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+ if (mMoveToCardOpHelper != null) {
+ mMoveToCardOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+ if (mUploadOpHelper != null) {
+ mUploadOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+
switch (requestCode) {
case REQUEST_EDIT_KEY: {
if (resultCode == Activity.RESULT_OK) {
@@ -156,7 +177,8 @@ public class CreateKeyFinalFragment extends Fragment {
if (mSaveKeyringParcel == null) {
mSaveKeyringParcel = new SaveKeyringParcel();
- if (createKeyActivity.mUseSmartCardSettings) {
+
+ if (createKeyActivity.mCreateYubiKey) {
mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
2048, null, KeyFlags.SIGN_DATA | KeyFlags.CERTIFY_OTHER, 0L));
mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
@@ -164,6 +186,7 @@ public class CreateKeyFinalFragment extends Fragment {
mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
2048, null, KeyFlags.AUTHENTICATION, 0L));
mEditText.setText(R.string.create_key_custom);
+ mEditButton.setEnabled(false);
} else {
mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
4096, null, KeyFlags.CERTIFY_OTHER, 0L));
@@ -192,115 +215,184 @@ public class CreateKeyFinalFragment extends Fragment {
}
}
-
private void createKey() {
- Intent intent = new Intent(getActivity(), KeychainService.class);
- intent.setAction(KeychainService.ACTION_EDIT_KEYRING);
+ final CreateKeyActivity createKeyActivity = (CreateKeyActivity) getActivity();
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
+ CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult> createKeyCallback
+ = new CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult>() {
@Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
- if (returnData == null) {
- return;
- }
- final EditKeyResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
- if (result == null) {
- Log.e(Constants.TAG, "result == null");
- return;
- }
-
- if (result.mMasterKeyId != null && mUploadCheckbox.isChecked()) {
- // result will be displayed after upload
- uploadKey(result);
- } else {
- Intent data = new Intent();
- data.putExtra(OperationResult.EXTRA_RESULT, result);
- getActivity().setResult(Activity.RESULT_OK, data);
- getActivity().finish();
- }
+ public SaveKeyringParcel createOperationInput() {
+ return mSaveKeyringParcel;
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(EditKeyResult result) {
+
+ if (createKeyActivity.mCreateYubiKey) {
+ moveToCard(result);
+ return;
+ }
+
+ if (result.mMasterKeyId != null && mUploadCheckbox.isChecked()) {
+ // result will be displayed after upload
+ uploadKey(result);
+ return;
}
+
+ Intent data = new Intent();
+ data.putExtra(OperationResult.EXTRA_RESULT, result);
+ getActivity().setResult(Activity.RESULT_OK, data);
+ getActivity().finish();
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+
+ }
+
+ @Override
+ public void onCryptoOperationError(EditKeyResult result) {
+ result.createNotify(getActivity()).show();
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
}
};
- // fill values for this action
- Bundle data = new Bundle();
+ mCreateOpHelper = new CryptoOperationHelper<>(this, createKeyCallback,
+ R.string.progress_building_key);
+ mCreateOpHelper.cryptoOperation();
+ }
- // get selected key entries
- data.putParcelable(KeychainService.EDIT_KEYRING_PARCEL, mSaveKeyringParcel);
+ private void moveToCard(final EditKeyResult saveKeyResult) {
+ CachedPublicKeyRing key = (new ProviderHelper(getActivity()))
+ .getCachedPublicKeyRing(saveKeyResult.mMasterKeyId);
- intent.putExtra(KeychainService.EXTRA_DATA, data);
+ final SaveKeyringParcel changeKeyringParcel;
+ try {
+ changeKeyringParcel = new SaveKeyringParcel(key.getMasterKeyId(), key.getFingerprint());
+ } catch (PgpKeyNotFoundException e) {
+ Log.e(Constants.TAG, "Key that should be moved to YubiKey not found in database!");
+ return;
+ }
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
+ Cursor cursor = getActivity().getContentResolver().query(
+ KeychainContract.Keys.buildKeysUri(changeKeyringParcel.mMasterKeyId),
+ new String[]{KeychainContract.Keys.KEY_ID,}, null, null, null
+ );
+ try {
+ while (cursor != null && cursor.moveToNext()) {
+ long subkeyId = cursor.getLong(0);
+ changeKeyringParcel.getOrCreateSubkeyChange(subkeyId).mMoveKeyToCard = true;
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
- saveHandler.showProgressDialog(getString(R.string.progress_building_key),
- ProgressDialog.STYLE_HORIZONTAL, false);
+ CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult> callback
+ = new CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult>() {
- getActivity().startService(intent);
- }
+ @Override
+ public SaveKeyringParcel createOperationInput() {
+ return changeKeyringParcel;
+ }
- // TODO move into EditKeyOperation
- private void uploadKey(final EditKeyResult saveKeyResult) {
- // Send all information needed to service to upload key in other thread
- final Intent intent = new Intent(getActivity(), KeychainService.class);
+ @Override
+ public void onCryptoOperationSuccess(EditKeyResult result) {
+ handleResult(result);
+ }
- intent.setAction(KeychainService.ACTION_UPLOAD_KEYRING);
+ @Override
+ public void onCryptoOperationCancelled() {
+
+ }
+
+ @Override
+ public void onCryptoOperationError(EditKeyResult result) {
+ handleResult(result);
+ }
+
+ public void handleResult(EditKeyResult result) {
+ // merge logs of createKey with moveToCard
+ saveKeyResult.getLog().add(result, 0);
+
+ if (result.mMasterKeyId != null && mUploadCheckbox.isChecked()) {
+ // result will be displayed after upload
+ uploadKey(saveKeyResult);
+ return;
+ }
+
+ Intent data = new Intent();
+ data.putExtra(OperationResult.EXTRA_RESULT, saveKeyResult);
+ getActivity().setResult(Activity.RESULT_OK, data);
+ getActivity().finish();
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
+ };
+
+
+ mMoveToCardOpHelper = new CryptoOperationHelper<>(this, callback, R.string.progress_modify);
+ mMoveToCardOpHelper.cryptoOperation();
+ }
+ private void uploadKey(final EditKeyResult saveKeyResult) {
// set data uri as path to keyring
- Uri blobUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(
+ final Uri blobUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(
saveKeyResult.mMasterKeyId);
- intent.setData(blobUri);
+ // upload to favorite keyserver
+ final String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
- // fill values for this action
- Bundle data = new Bundle();
+ CryptoOperationHelper.Callback<ExportKeyringParcel, ExportResult> callback
+ = new CryptoOperationHelper.Callback<ExportKeyringParcel, ExportResult>() {
- // upload to favorite keyserver
- String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
- data.putString(KeychainService.UPLOAD_KEY_SERVER, keyserver);
+ @Override
+ public ExportKeyringParcel createOperationInput() {
+ return new ExportKeyringParcel(keyserver, blobUri);
+ }
- intent.putExtra(KeychainService.EXTRA_DATA, data);
+ @Override
+ public void onCryptoOperationSuccess(ExportResult result) {
+ handleResult(result);
+ }
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
@Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // TODO: upload operation needs a result!
- // TODO: then combine these results
- //if (result.getResult() == OperationResultParcel.RESULT_OK) {
- //Notify.create(getActivity(), R.string.key_send_success,
- //Notify.Style.OK).show();
-
- Intent data = new Intent();
- data.putExtra(OperationResult.EXTRA_RESULT, saveKeyResult);
- getActivity().setResult(Activity.RESULT_OK, data);
- getActivity().finish();
- }
+ public void onCryptoOperationCancelled() {
+
}
- };
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
+ @Override
+ public void onCryptoOperationError(ExportResult result) {
+ handleResult(result);
+ }
- // show progress dialog
- saveHandler.showProgressDialog(
- getString(R.string.progress_uploading),
- ProgressDialog.STYLE_HORIZONTAL, false);
+ public void handleResult(ExportResult result) {
+ // TODO: ExportOperation UPLOAD_KEYSERVER needs logs!
+ // TODO: then merge logs here!
+ //saveKeyResult.getLog().add(result, 0);
+
+ Intent data = new Intent();
+ data.putExtra(OperationResult.EXTRA_RESULT, saveKeyResult);
+ getActivity().setResult(Activity.RESULT_OK, data);
+ getActivity().finish();
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
+ };
- // start service with intent
- getActivity().startService(intent);
+ mUploadOpHelper = new CryptoOperationHelper<>(this, callback, R.string.progress_uploading);
+ mUploadOpHelper.cryptoOperation();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java
index 1a844e6e4..cd97ef108 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java
@@ -81,7 +81,7 @@ public class CreateKeyStartFragment extends Fragment {
mYubiKey.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- CreateKeyYubiKeyWaitFragment frag = new CreateKeyYubiKeyWaitFragment();
+ CreateYubiKeyWaitFragment frag = new CreateYubiKeyWaitFragment();
mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
}
});
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyBlankFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyBlankFragment.java
new file mode 100644
index 000000000..5b13dc88e
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyBlankFragment.java
@@ -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.app.Activity;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
+
+public class CreateYubiKeyBlankFragment extends Fragment {
+
+ CreateKeyActivity mCreateKeyActivity;
+ View mBackButton;
+ View mNextButton;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static CreateYubiKeyBlankFragment newInstance() {
+ CreateYubiKeyBlankFragment frag = new CreateYubiKeyBlankFragment();
+
+ Bundle args = new Bundle();
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.create_yubi_key_blank_fragment, container, false);
+
+ mBackButton = view.findViewById(R.id.create_key_back_button);
+ mNextButton = view.findViewById(R.id.create_key_next_button);
+
+ mBackButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (getFragmentManager().getBackStackEntryCount() == 0) {
+ getActivity().setResult(Activity.RESULT_CANCELED);
+ getActivity().finish();
+ } else {
+ mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
+ }
+ }
+ });
+ mNextButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ nextClicked();
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mCreateKeyActivity = (CreateKeyActivity) getActivity();
+ }
+
+ private void nextClicked() {
+ mCreateKeyActivity.mCreateYubiKey = true;
+
+ CreateKeyNameFragment frag = CreateKeyNameFragment.newInstance();
+ mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyImportFragment.java
index 2ab8c5967..9897fa237 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyImportFragment.java
@@ -22,11 +22,8 @@ import java.nio.ByteBuffer;
import java.util.ArrayList;
import android.app.Activity;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
@@ -37,18 +34,19 @@ import android.widget.TextView;
import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.service.KeychainService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.NfcListenerFragment;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Preferences;
-public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListenerFragment {
+public class CreateYubiKeyImportFragment
+ extends CryptoOperationFragment<ImportKeyringParcel, ImportKeyResult>
+ implements NfcListenerFragment {
private static final String ARG_FINGERPRINT = "fingerprint";
public static final String ARG_AID = "aid";
@@ -64,9 +62,13 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
private TextView vSerNo;
private TextView vUserId;
- public static Fragment createInstance(byte[] scannedFingerprints, byte[] nfcAid, String userId) {
+ // for CryptoOperationFragment key import
+ private String mKeyserver;
+ private ArrayList<ParcelableKeyRing> mKeyList;
- CreateKeyYubiKeyImportFragment frag = new CreateKeyYubiKeyImportFragment();
+ public static Fragment newInstance(byte[] scannedFingerprints, byte[] nfcAid, String userId) {
+
+ CreateYubiKeyImportFragment frag = new CreateYubiKeyImportFragment();
Bundle args = new Bundle();
args.putByteArray(ARG_FINGERPRINT, scannedFingerprints);
@@ -95,7 +97,7 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.create_yubikey_import_fragment, container, false);
+ View view = inflater.inflate(R.layout.create_yubi_key_import_fragment, container, false);
vSerNo = (TextView) view.findViewById(R.id.yubikey_serno);
vUserId = (TextView) view.findViewById(R.id.yubikey_userid);
@@ -175,77 +177,20 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
public void importKey() {
- // Message is received after decrypting is done in KeychainService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
-
- ImportKeyResult result =
- returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
-
- long[] masterKeyIds = result.getImportedMasterKeyIds();
-
- // TODO handle masterKeyIds.length != 1...? sorta outlandish scenario
-
- if (!result.success() || masterKeyIds.length == 0) {
- result.createNotify(getActivity()).show();
- return;
- }
-
- Intent intent = new Intent(getActivity(), ViewKeyActivity.class);
- // use the imported masterKeyId, not the one from the yubikey, because
- // that one might* just have been a subkey of the imported key
- intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyIds[0]));
- intent.putExtra(ViewKeyActivity.EXTRA_DISPLAY_RESULT, result);
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid);
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId);
- intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints);
- startActivity(intent);
- getActivity().finish();
-
- }
-
- }
- };
-
- // Send all information needed to service to decrypt in other thread
- Intent intent = new Intent(getActivity(), KeychainService.class);
-
- // fill values for this action
- Bundle data = new Bundle();
-
- intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
-
ArrayList<ParcelableKeyRing> keyList = new ArrayList<>();
keyList.add(new ParcelableKeyRing(mNfcFingerprint, null, null));
- data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, keyList);
+ mKeyList = keyList;
{
Preferences prefs = Preferences.getPreferences(getActivity());
Preferences.CloudSearchPrefs cloudPrefs =
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
- data.putString(KeychainService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
+ mKeyserver = cloudPrefs.keyserver;
}
- intent.putExtra(KeychainService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
+ super.setProgressMessageResource(R.string.progress_importing);
- saveHandler.showProgressDialog(
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL, false
- );
-
- // start service with intent
- getActivity().startService(intent);
+ super.cryptoOperation();
}
@@ -264,4 +209,29 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
refreshSearch();
}
+
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return new ImportKeyringParcel(mKeyList, mKeyserver);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(ImportKeyResult result) {
+ long[] masterKeyIds = result.getImportedMasterKeyIds();
+ if (masterKeyIds.length == 0) {
+ super.onCryptoOperationError(result);
+ return;
+ }
+
+ Intent intent = new Intent(getActivity(), ViewKeyActivity.class);
+ // use the imported masterKeyId, not the one from the yubikey, because
+ // that one might* just have been a subkey of the imported key
+ intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyIds[0]));
+ intent.putExtra(ViewKeyActivity.EXTRA_DISPLAY_RESULT, result);
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid);
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId);
+ intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints);
+ startActivity(intent);
+ getActivity().finish();
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyPinFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyPinFragment.java
new file mode 100644
index 000000000..8744762fe
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyPinFragment.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 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.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
+
+import java.security.SecureRandom;
+
+public class CreateYubiKeyPinFragment extends Fragment {
+
+ // view
+ CreateKeyActivity mCreateKeyActivity;
+ TextView mPin;
+ TextView mAdminPin;
+ View mBackButton;
+ View mNextButton;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static CreateYubiKeyPinFragment newInstance() {
+ CreateYubiKeyPinFragment frag = new CreateYubiKeyPinFragment();
+
+ Bundle args = new Bundle();
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.create_yubi_key_pin_fragment, container, false);
+
+ mPin = (TextView) view.findViewById(R.id.create_yubi_key_pin);
+ mAdminPin = (TextView) view.findViewById(R.id.create_yubi_key_admin_pin);
+ mBackButton = view.findViewById(R.id.create_key_back_button);
+ mNextButton = view.findViewById(R.id.create_key_next_button);
+
+ if (mCreateKeyActivity.mYubiKeyPin == null) {
+ new AsyncTask<Void, Void, Pair<String, String>>() {
+ @Override
+ protected Pair<String, String> doInBackground(Void... unused) {
+ SecureRandom secureRandom = new SecureRandom();
+ // min = 6, we choose 6
+ String pin = "" + secureRandom.nextInt(999999);
+ // min = 8, we choose 10, but 6 are equals the PIN
+ String adminPin = pin + secureRandom.nextInt(9999);
+
+ return new Pair<>(pin, adminPin);
+ }
+
+ @Override
+ protected void onPostExecute(Pair<String, String> pair) {
+ mCreateKeyActivity.mYubiKeyPin = pair.first;
+ mCreateKeyActivity.mYubiKeyAdminPin = pair.second;
+
+ mPin.setText(mCreateKeyActivity.mYubiKeyPin);
+ mAdminPin.setText(mCreateKeyActivity.mYubiKeyAdminPin);
+ }
+ }.execute();
+ } else {
+ mPin.setText(mCreateKeyActivity.mYubiKeyPin);
+ mAdminPin.setText(mCreateKeyActivity.mYubiKeyAdminPin);
+ }
+
+ mBackButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ back();
+ }
+ });
+ mNextButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ nextClicked();
+ }
+ });
+
+
+ return view;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mCreateKeyActivity = (CreateKeyActivity) getActivity();
+ }
+
+
+ private void nextClicked() {
+ // save state
+// mCreateKeyActivity.mPassphrase = new Passphrase(mPassphraseEdit);
+
+ CreateYubiKeyPinRepeatFragment frag = CreateYubiKeyPinRepeatFragment.newInstance();
+ mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
+ }
+
+ private void back() {
+ mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyPinRepeatFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyPinRepeatFragment.java
new file mode 100644
index 000000000..dc437577a
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyPinRepeatFragment.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2015 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.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
+
+public class CreateYubiKeyPinRepeatFragment extends Fragment {
+
+ // view
+ CreateKeyActivity mCreateKeyActivity;
+ EditText mPin;
+ EditText mAdminPin;
+ View mBackButton;
+ View mNextButton;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static CreateYubiKeyPinRepeatFragment newInstance() {
+ CreateYubiKeyPinRepeatFragment frag = new CreateYubiKeyPinRepeatFragment();
+
+ Bundle args = new Bundle();
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ /**
+ * 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().length() == 0) {
+ editText.setError(context.getString(R.string.create_key_empty));
+ editText.requestFocus();
+ output = false;
+ } else {
+ editText.setError(null);
+ }
+
+ return output;
+ }
+
+ private static boolean checkPin(Context context, EditText editText1, String pin) {
+ boolean output = editText1.getText().toString().equals(pin);
+
+ if (!output) {
+ editText1.setError(context.getString(R.string.create_key_yubi_key_pin_not_correct));
+ editText1.requestFocus();
+ } else {
+ editText1.setError(null);
+ }
+
+ return output;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.create_yubi_key_pin_repeat_fragment, container, false);
+
+ mPin = (EditText) view.findViewById(R.id.create_yubi_key_pin_repeat);
+ mAdminPin = (EditText) view.findViewById(R.id.create_yubi_key_admin_pin_repeat);
+ mBackButton = view.findViewById(R.id.create_key_back_button);
+ mNextButton = view.findViewById(R.id.create_key_next_button);
+
+ mPin.requestFocus();
+ mBackButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ back();
+ }
+ });
+ mNextButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ nextClicked();
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mCreateKeyActivity = (CreateKeyActivity) getActivity();
+ }
+
+ private void back() {
+ hideKeyboard();
+ mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
+ }
+
+ private void nextClicked() {
+ if (isEditTextNotEmpty(getActivity(), mPin)
+ && checkPin(getActivity(), mPin, mCreateKeyActivity.mYubiKeyPin)
+ && isEditTextNotEmpty(getActivity(), mAdminPin)
+ && checkPin(getActivity(), mAdminPin, mCreateKeyActivity.mYubiKeyAdminPin)) {
+
+ CreateKeyFinalFragment frag = CreateKeyFinalFragment.newInstance();
+ hideKeyboard();
+ mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
+ }
+ }
+
+ 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);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyWaitFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyWaitFragment.java
index 0b8586c0a..d45195512 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyWaitFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyWaitFragment.java
@@ -28,14 +28,14 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
-public class CreateKeyYubiKeyWaitFragment extends Fragment {
+public class CreateYubiKeyWaitFragment extends Fragment {
CreateKeyActivity mCreateKeyActivity;
View mBackButton;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.create_yubikey_wait_fragment, container, false);
+ View view = inflater.inflate(R.layout.create_yubi_key_wait_fragment, container, false);
mBackButton = view.findViewById(R.id.create_key_back_button);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
index 4375be740..04f54f151 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
@@ -120,7 +120,14 @@ public class DecryptActivity extends BaseActivity {
case ACTION_DECRYPT_FROM_CLIPBOARD: {
ClipboardManager clipMan = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
+ if (clipMan == null) {
+ break;
+ }
+
ClipData clip = clipMan.getPrimaryClip();
+ if (clip == null) {
+ break;
+ }
// check if data is available as uri
Uri uri = null;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
index c8ae867b2..4eb8cd5e8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
@@ -27,6 +27,7 @@ import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.Fragment;
+import android.os.Parcelable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -45,14 +46,14 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
+import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
import org.sufficientlysecure.keychain.ui.util.Notify;
@@ -78,6 +79,8 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
private DecryptVerifyResult mDecryptVerifyResult;
private ViewAnimator mOverlayAnimator;
+ private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper;
+
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
@@ -135,43 +138,15 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
private void lookupUnknownKey(long unknownKeyId) {
- // Message is received after importing is done in KeychainService
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(getActivity()) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
-
- if (returnData == null) {
- return;
- }
-
- final ImportKeyResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
-
- Activity activity = getActivity();
- if (result != null && activity != null) {
- result.createNotify(activity).show();
- }
-
- getLoaderManager().restartLoader(LOADER_ID_UNIFIED, null, DecryptFragment.this);
- }
- }
- };
-
- // fill values for this action
- Bundle data = new Bundle();
+ final ArrayList<ParcelableKeyRing> keyList;
+ final String keyserver;
// search config
{
Preferences prefs = Preferences.getPreferences(getActivity());
Preferences.CloudSearchPrefs cloudPrefs =
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
- data.putString(KeychainService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
+ keyserver = cloudPrefs.keyserver;
}
{
@@ -180,19 +155,43 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();
selectedEntries.add(keyEntry);
- data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, selectedEntries);
+ keyList = selectedEntries;
}
- // Send all information needed to service to query keys in other thread
- Intent intent = new Intent(getActivity(), KeychainService.class);
- intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
- intent.putExtra(KeychainService.EXTRA_DATA, data);
+ CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> callback
+ = new CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult>() {
+
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return new ImportKeyringParcel(keyList, keyserver);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(ImportKeyResult result) {
+ result.createNotify(getActivity()).show();
+
+ getLoaderManager().restartLoader(LOADER_ID_UNIFIED, null, DecryptFragment.this);
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+ // do nothing
+ }
+
+ @Override
+ public void onCryptoOperationError(ImportKeyResult result) {
+ result.createNotify(getActivity()).show();
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
+ };
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
+ mImportOpHelper = new CryptoOperationHelper<>(this, callback, R.string.progress_importing);
- getActivity().startService(intent);
+ mImportOpHelper.cryptoOperation();
}
private void showKey(long keyId) {
@@ -457,4 +456,11 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
protected abstract void onVerifyLoaded(boolean hideErrorOverlay);
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mImportOpHelper != null) {
+ mImportOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java
index d2bff8336..96767463e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java
@@ -254,18 +254,12 @@ public class DecryptListFragment
}
@Override
- protected boolean onCryptoSetProgress(String msg, int progress, int max) {
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
mAdapter.setProgress(mCurrentInputUri, progress, max, msg);
return true;
}
-
- @Override
- protected void dismissProgress() {
- // progress shown inline, so never mind
- }
-
@Override
- protected void onCryptoOperationError(DecryptVerifyResult result) {
+ public void onCryptoOperationError(DecryptVerifyResult result) {
final Uri uri = mCurrentInputUri;
mCurrentInputUri = null;
@@ -275,7 +269,7 @@ public class DecryptListFragment
}
@Override
- protected void onCryptoOperationSuccess(DecryptVerifyResult result) {
+ public void onCryptoOperationSuccess(DecryptVerifyResult result) {
Uri uri = mCurrentInputUri;
mCurrentInputUri = null;
@@ -439,7 +433,7 @@ public class DecryptListFragment
}
@Override
- protected PgpDecryptVerifyInputParcel createOperationInput() {
+ public PgpDecryptVerifyInputParcel createOperationInput() {
if (mCurrentInputUri == null) {
if (mPendingInputUris.isEmpty()) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
index 48aa3016d..1363d44f2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
@@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@@ -26,7 +25,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
-import android.os.Parcelable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -40,9 +38,6 @@ import android.widget.ListView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
-import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
-import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
-import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.SingletonResult;
@@ -54,8 +49,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
-import org.sufficientlysecure.keychain.service.KeychainService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
@@ -65,7 +58,12 @@ 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.base.CryptoOperationFragment;
-import org.sufficientlysecure.keychain.ui.dialog.*;
+import org.sufficientlysecure.keychain.ui.dialog.AddSubkeyDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.AddUserIdDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyExpiryDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
@@ -609,12 +607,12 @@ public class EditKeyFragment extends CryptoOperationFragment<SaveKeyringParcel,
}
@Override
- protected SaveKeyringParcel createOperationInput() {
+ public SaveKeyringParcel createOperationInput() {
return mSaveKeyringParcel;
}
@Override
- protected void onCryptoOperationSuccess(OperationResult result) {
+ public void onCryptoOperationSuccess(OperationResult result) {
// if good -> finish, return result to showkey and display there!
Intent intent = new Intent();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java
index 590d02c6f..779b22535 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java
@@ -29,6 +29,7 @@ import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -37,6 +38,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker;
import org.sufficientlysecure.keychain.util.FileHelper;
@@ -92,15 +94,31 @@ public class EncryptDecryptOverviewFragment extends Fragment {
mDecryptFromClipboard.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- Intent clipboardDecrypt = new Intent(getActivity(), DecryptActivity.class);
- clipboardDecrypt.setAction(DecryptActivity.ACTION_DECRYPT_FROM_CLIPBOARD);
- startActivityForResult(clipboardDecrypt, 0);
+ decryptFromClipboard();
}
});
return view;
}
+ private void decryptFromClipboard() {
+
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ final CharSequence clipboardText = ClipboardReflection.getClipboardText(activity);
+ if (clipboardText == null || TextUtils.isEmpty(clipboardText)) {
+ Notify.create(activity, R.string.error_clipboard_empty, Style.ERROR).show();
+ return;
+ }
+
+ Intent clipboardDecrypt = new Intent(getActivity(), DecryptActivity.class);
+ clipboardDecrypt.setAction(DecryptActivity.ACTION_DECRYPT_FROM_CLIPBOARD);
+ startActivityForResult(clipboardDecrypt, 0);
+ }
+
@Override
public void onResume() {
super.onResume();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
index d7c6b2049..215af5885 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java
@@ -386,7 +386,7 @@ public class EncryptFilesFragment
}
@Override
- protected void onCryptoOperationSuccess(final SignEncryptResult result) {
+ public void onCryptoOperationSuccess(final SignEncryptResult result) {
if (mDeleteAfterEncrypt) {
// TODO make behavior coherent here
@@ -485,7 +485,7 @@ public class EncryptFilesFragment
}
- protected SignEncryptParcel createOperationInput() {
+ public SignEncryptParcel createOperationInput() {
SignEncryptParcel actionsParcel = getCachedActionsParcel();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java
index e0629eb73..886c52651 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java
@@ -202,7 +202,7 @@ public class EncryptTextFragment
}
- protected SignEncryptParcel createOperationInput() {
+ public SignEncryptParcel createOperationInput() {
if (mMessage == null || mMessage.isEmpty()) {
Notify.create(getActivity(), R.string.error_empty_text, Notify.Style.ERROR)
@@ -316,7 +316,7 @@ public class EncryptTextFragment
}
@Override
- protected void onCryptoOperationSuccess(SignEncryptResult result) {
+ public void onCryptoOperationSuccess(SignEncryptResult result) {
if (mShareAfterEncrypt) {
// Share encrypted message/file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 07ab88b02..ba4f759e6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -17,12 +17,10 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
-import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.view.View;
import android.view.View.OnClickListener;
@@ -35,9 +33,10 @@ import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.service.KeychainService;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
@@ -47,7 +46,8 @@ import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize
import java.io.IOException;
import java.util.ArrayList;
-public class ImportKeysActivity extends BaseNfcActivity {
+public class ImportKeysActivity extends BaseNfcActivity
+ implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
public static final String ACTION_IMPORT_KEY = OpenKeychainIntents.IMPORT_KEY;
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER = OpenKeychainIntents.IMPORT_KEY_FROM_KEYSERVER;
@@ -82,6 +82,12 @@ public class ImportKeysActivity extends BaseNfcActivity {
private Fragment mTopFragment;
private View mImportButton;
+ // for CryptoOperationHelper.Callback
+ private String mKeyserver;
+ private ArrayList<ParcelableKeyRing> mKeyList;
+
+ private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -388,23 +394,9 @@ public class ImportKeysActivity extends BaseNfcActivity {
return;
}
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(this) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- ImportKeysActivity.this.handleMessage(message);
- }
- };
-
- // Send all information needed to service to import key in other thread
- Intent intent = new Intent(this, KeychainService.class);
-
- intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
-
- // fill values for this action
- Bundle data = new Bundle();
+ mOperationHelper = new CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult>(
+ this, this, R.string.progress_importing
+ );
ImportKeysListFragment.LoaderState ls = mListFragment.getLoaderState();
if (ls instanceof ImportKeysListFragment.BytesLoaderState) {
@@ -423,30 +415,18 @@ public class ImportKeysActivity extends BaseNfcActivity {
new ParcelableFileCache<>(this, "key_import.pcl");
cache.writeCache(selectedEntries);
- intent.putExtra(KeychainService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- serviceHandler.showProgressDialog(
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL,
- true
- );
+ mKeyList = null;
+ mKeyserver = null;
+ mOperationHelper.cryptoOperation();
- // start service with intent
- startService(intent);
} catch (IOException e) {
Log.e(Constants.TAG, "Problem writing cache file", e);
Notify.create(this, "Problem writing cache file!", Notify.Style.ERROR)
.show((ViewGroup) findViewById(R.id.import_snackbar));
}
} else if (ls instanceof ImportKeysListFragment.CloudLoaderState) {
- ImportKeysListFragment.CloudLoaderState sls = (ImportKeysListFragment.CloudLoaderState) ls;
-
- data.putString(KeychainService.IMPORT_KEY_SERVER, sls.mCloudPrefs.keyserver);
+ ImportKeysListFragment.CloudLoaderState sls =
+ (ImportKeysListFragment.CloudLoaderState) ls;
// get selected key entries
ArrayList<ParcelableKeyRing> keys = new ArrayList<>();
@@ -459,22 +439,11 @@ public class ImportKeysActivity extends BaseNfcActivity {
);
}
}
- data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, keys);
-
- intent.putExtra(KeychainService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
- // show progress dialog
- serviceHandler.showProgressDialog(
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL, true
- );
+ mKeyList = keys;
+ mKeyserver = sls.mCloudPrefs.keyserver;
+ mOperationHelper.cryptoOperation();
- // start service with intent
- startService(intent);
}
}
@@ -485,4 +454,57 @@ public class ImportKeysActivity extends BaseNfcActivity {
// either way, finish afterwards
finish();
}
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mOperationHelper == null ||
+ !mOperationHelper.handleActivityResult(requestCode, resultCode, data)) {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ public void handleResult (ImportKeyResult result) {
+ if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(getIntent().getAction())
+ || ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN.equals(getIntent().getAction())) {
+ Intent intent = new Intent();
+ intent.putExtra(ImportKeyResult.EXTRA_RESULT, result);
+ ImportKeysActivity.this.setResult(RESULT_OK, intent);
+ ImportKeysActivity.this.finish();
+ return;
+ }
+ if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE.equals(getIntent().getAction())) {
+ ImportKeysActivity.this.setResult(RESULT_OK, mPendingIntentData);
+ ImportKeysActivity.this.finish();
+ return;
+ }
+
+ result.createNotify(ImportKeysActivity.this)
+ .show((ViewGroup) findViewById(R.id.import_snackbar));
+ }
+ // methods from CryptoOperationHelper.Callback
+
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return new ImportKeyringParcel(mKeyList, mKeyserver);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(ImportKeyResult result) {
+ handleResult(result);
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+ // do nothing
+ }
+
+ @Override
+ public void onCryptoOperationError(ImportKeyResult result) {
+ handleResult(result);
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
index 9f3beff43..da713d0d8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
@@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.ui;
import android.annotation.TargetApi;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.net.Uri;
@@ -26,8 +25,6 @@ import android.nfc.NdefMessage;
import android.nfc.NfcAdapter;
import android.os.Build;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.os.Parcelable;
import android.support.v4.app.FragmentActivity;
import android.widget.Toast;
@@ -43,8 +40,8 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.SingletonResult;
-import org.sufficientlysecure.keychain.service.KeychainService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
@@ -55,7 +52,8 @@ import java.util.Locale;
/**
* Proxy activity (just a transparent content view) to scan QR Codes using the Barcode Scanner app
*/
-public class ImportKeysProxyActivity extends FragmentActivity {
+public class ImportKeysProxyActivity extends FragmentActivity
+ implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
public static final String ACTION_QR_CODE_API = OpenKeychainIntents.IMPORT_KEY_FROM_QR_CODE;
// implies activity returns scanned fingerprint as extra and does not import
@@ -64,6 +62,11 @@ public class ImportKeysProxyActivity extends FragmentActivity {
public static final String EXTRA_FINGERPRINT = "fingerprint";
+ // for CryptoOperationHelper
+ private String mKeyserver;
+ private ArrayList<ParcelableKeyRing> mKeyList;
+ private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -106,6 +109,10 @@ public class ImportKeysProxyActivity extends FragmentActivity {
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mImportOpHelper != null) {
+ mImportOpHelper.cryptoOperation();
+ }
+
if (requestCode == IntentIntegratorSupportV4.REQUEST_CODE) {
IntentResult scanResult = IntentIntegratorSupportV4.parseActivityResult(requestCode,
resultCode, data);
@@ -205,75 +212,56 @@ public class ImportKeysProxyActivity extends FragmentActivity {
private void startImportService(ArrayList<ParcelableKeyRing> keyRings) {
- // Message is received after importing is done in KeychainService
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(this) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
- if (returnData == null) {
- finish();
- return;
- }
- final ImportKeyResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
- if (result == null) {
- Log.e(Constants.TAG, "result == null");
- finish();
- return;
- }
-
- if (!result.success()) {
- // only return if no success...
- Intent data = new Intent();
- data.putExtras(returnData);
- returnResult(data);
- return;
- }
-
- Intent certifyIntent = new Intent(ImportKeysProxyActivity.this,
- CertifyKeyActivity.class);
- certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
- certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS,
- result.getImportedMasterKeyIds());
- startActivityForResult(certifyIntent, 0);
- }
- }
- };
-
- // fill values for this action
- Bundle data = new Bundle();
-
// search config
{
Preferences prefs = Preferences.getPreferences(this);
Preferences.CloudSearchPrefs cloudPrefs =
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
- data.putString(KeychainService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
+ mKeyserver = cloudPrefs.keyserver;
}
- data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, keyRings);
+ mKeyList = keyRings;
+
+ mImportOpHelper = new CryptoOperationHelper<>(this, this, R.string.progress_importing);
+
+ mImportOpHelper.cryptoOperation();
+ }
+
+
+ // CryptoOperationHelper.Callback methods
- // Send all information needed to service to query keys in other thread
- Intent intent = new Intent(this, KeychainService.class);
- intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
- intent.putExtra(KeychainService.EXTRA_DATA, data);
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return new ImportKeyringParcel(mKeyList, mKeyserver);
+ }
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
+ @Override
+ public void onCryptoOperationSuccess(ImportKeyResult result) {
+ Intent certifyIntent = new Intent(this, CertifyKeyActivity.class);
+ certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
+ certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS,
+ result.getImportedMasterKeyIds());
+ startActivityForResult(certifyIntent, 0);
+ }
- // show progress dialog
- serviceHandler.showProgressDialog(
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL, true);
+ @Override
+ public void onCryptoOperationCancelled() {
- // start service with intent
- startService(intent);
+ }
+
+ @Override
+ public void onCryptoOperationError(ImportKeyResult result) {
+ Bundle returnData = new Bundle();
+ returnData.putParcelable(OperationResult.EXTRA_RESULT, result);
+ Intent data = new Intent();
+ data.putExtras(returnData);
+ returnResult(data);
+ return;
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
}
/**
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
index 780558b27..e038cf948 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -21,7 +21,6 @@ package org.sufficientlysecure.keychain.ui;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.app.Activity;
-import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
@@ -64,9 +63,10 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.service.ConsolidateInputParcel;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
@@ -90,7 +90,8 @@ import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
*/
public class KeyListFragment extends LoaderFragment
implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
- LoaderManager.LoaderCallbacks<Cursor>, FabContainer {
+ LoaderManager.LoaderCallbacks<Cursor>, FabContainer,
+ CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
static final int REQUEST_REPEAT_PASSPHRASE = 1;
static final int REQUEST_ACTION = 2;
@@ -107,6 +108,14 @@ public class KeyListFragment extends LoaderFragment
private FloatingActionsMenu mFab;
+ // for CryptoOperationHelper import
+ private ArrayList<ParcelableKeyRing> mKeyList;
+ private String mKeyserver;
+ private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper;
+
+ // for ConsolidateOperation
+ private CryptoOperationHelper<ConsolidateInputParcel, ConsolidateResult> mConsolidateOpHelper;
+
// This ids for multiple key export.
private ArrayList<Long> mIdsForRepeatAskPassphrase;
// This index for remembering the number of master key.
@@ -580,112 +589,59 @@ public class KeyListFragment extends LoaderFragment
ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
keyList.add(keyEntry);
}
+ mKeyList = keyList;
} finally {
cursor.close();
}
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(getActivity()) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
- if (returnData == null) {
- return;
- }
- final ImportKeyResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
- if (result == null) {
- Log.e(Constants.TAG, "result == null");
- return;
- }
-
- result.createNotify(getActivity()).show();
- }
- }
- };
-
- // Send all information needed to service to query keys in other thread
- Intent intent = new Intent(getActivity(), KeychainService.class);
- intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
-
- // fill values for this action
- Bundle data = new Bundle();
-
// search config
{
Preferences prefs = Preferences.getPreferences(getActivity());
Preferences.CloudSearchPrefs cloudPrefs =
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
- data.putString(KeychainService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
+ mKeyserver = cloudPrefs.keyserver;
}
- data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, keyList);
-
- intent.putExtra(KeychainService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- serviceHandler.showProgressDialog(
- getString(R.string.progress_updating),
- ProgressDialog.STYLE_HORIZONTAL, true);
-
- // start service with intent
- getActivity().startService(intent);
+ mImportOpHelper = new CryptoOperationHelper<>(this,
+ this, R.string.progress_updating);
+ mImportOpHelper.cryptoOperation();
}
private void consolidate() {
- // Message is received after importing is done in KeychainService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
- if (returnData == null) {
- return;
- }
- final ConsolidateResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
- if (result == null) {
- return;
- }
- result.createNotify(getActivity()).show();
- }
+ CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult> callback
+ = new CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult>() {
+
+ @Override
+ public ConsolidateInputParcel createOperationInput() {
+ return new ConsolidateInputParcel(false); // we want to perform a full consolidate
}
- };
- // Send all information needed to service to import key in other thread
- Intent intent = new Intent(getActivity(), KeychainService.class);
+ @Override
+ public void onCryptoOperationSuccess(ConsolidateResult result) {
+ result.createNotify(getActivity()).show();
+ }
- intent.setAction(KeychainService.ACTION_CONSOLIDATE);
+ @Override
+ public void onCryptoOperationCancelled() {
- // fill values for this action
- Bundle data = new Bundle();
+ }
- intent.putExtra(KeychainService.EXTRA_DATA, data);
+ @Override
+ public void onCryptoOperationError(ConsolidateResult result) {
+ result.createNotify(getActivity()).show();
+ }
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
+ };
- // show progress dialog
- saveHandler.showProgressDialog(
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL, false);
+ mConsolidateOpHelper =
+ new CryptoOperationHelper<>(this, callback, R.string.progress_importing);
- // start service with intent
- getActivity().startService(intent);
+ mConsolidateOpHelper.cryptoOperation();
}
private void showMultiExportDialog(long[] masterKeyIds) {
@@ -724,6 +680,14 @@ public class KeyListFragment extends LoaderFragment
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mImportOpHelper != null) {
+ mImportOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+
+ if (mConsolidateOpHelper != null) {
+ mConsolidateOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+
if (requestCode == REQUEST_REPEAT_PASSPHRASE) {
if (resultCode != Activity.RESULT_OK) {
return;
@@ -769,6 +733,32 @@ public class KeyListFragment extends LoaderFragment
anim.start();
}
+ // CryptoOperationHelper.Callback methods
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return new ImportKeyringParcel(mKeyList, mKeyserver);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(ImportKeyResult result) {
+ result.createNotify(getActivity()).show();
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+
+ }
+
+ @Override
+ public void onCryptoOperationError(ImportKeyResult result) {
+ result.createNotify(getActivity()).show();
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
+
public class KeyListAdapter extends KeyAdapter implements StickyListHeadersAdapter {
private HashMap<Integer, Boolean> mSelection = new HashMap<>();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java
index c5fc9abe0..addfb6a23 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java
@@ -63,7 +63,7 @@ public class NfcOperationActivity extends BaseNfcActivity {
mServiceIntent = data.getParcelable(EXTRA_SERVICE_INTENT);
// obtain passphrase for this subkey
- if (mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_KEYTOCARD) {
+ if (mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_MOVE_KEY_TO_CARD) {
obtainYubiKeyPin(mRequiredInput);
}
}
@@ -96,7 +96,7 @@ public class NfcOperationActivity extends BaseNfcActivity {
}
break;
}
- case NFC_KEYTOCARD: {
+ case NFC_MOVE_KEY_TO_CARD: {
ProviderHelper providerHelper = new ProviderHelper(this);
CanonicalizedSecretKeyRing secretKeyRing;
try {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java
index 7408135ae..a1cb77546 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java
@@ -18,15 +18,11 @@
package org.sufficientlysecure.keychain.ui;
import android.annotation.TargetApi;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
-import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.NumberPicker;
@@ -38,9 +34,9 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainService;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
@@ -52,7 +48,8 @@ import edu.cmu.cylab.starslinger.exchange.ExchangeActivity;
import edu.cmu.cylab.starslinger.exchange.ExchangeConfig;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
-public class SafeSlingerActivity extends BaseActivity {
+public class SafeSlingerActivity extends BaseActivity
+ implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
private static final int REQUEST_CODE_SAFE_SLINGER = 211;
@@ -61,6 +58,12 @@ public class SafeSlingerActivity extends BaseActivity {
private long mMasterKeyId;
private int mSelectedNumber = 2;
+ // for CryptoOperationHelper
+ private ArrayList<ParcelableKeyRing> mKeyList;
+ private String mKeyserver;
+ private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;
+
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -116,65 +119,17 @@ public class SafeSlingerActivity extends BaseActivity {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mOperationHelper != null) {
+ mOperationHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+
if (requestCode == REQUEST_CODE_SAFE_SLINGER) {
if (resultCode == ExchangeActivity.RESULT_EXCHANGE_CANCELED) {
return;
}
- final FragmentActivity activity = SafeSlingerActivity.this;
-
- // Message is received after importing is done in KeychainService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(activity) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
- if (returnData == null) {
- return;
- }
- final ImportKeyResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
- if (result == null) {
- Log.e(Constants.TAG, "result == null");
- return;
- }
-
- if (!result.success()) {
-// result.createNotify(activity).show();
- // only return if no success...
- Intent data = new Intent();
- data.putExtras(returnData);
- setResult(RESULT_OK, data);
- finish();
- return;
- }
-
-// if (mExchangeMasterKeyId == null) {
-// return;
-// }
-
- Intent certifyIntent = new Intent(activity, CertifyKeyActivity.class);
- certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
- certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, result.getImportedMasterKeyIds());
- certifyIntent.putExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, mMasterKeyId);
- startActivityForResult(certifyIntent, 0);
-
-// mExchangeMasterKeyId = null;
- }
- }
- };
-
Log.d(Constants.TAG, "importKeys started");
- // Send all information needed to service to import key in other thread
- Intent intent = new Intent(activity, KeychainService.class);
-
- intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
-
// instead of giving the entries by Intent extra, cache them into a
// file to prevent Java Binder problems on heavy imports
// read FileImportCache for more info.
@@ -185,28 +140,18 @@ public class SafeSlingerActivity extends BaseActivity {
// We parcel this iteratively into a file - anything we can
// display here, we should be able to import.
ParcelableFileCache<ParcelableKeyRing> cache =
- new ParcelableFileCache<>(activity, "key_import.pcl");
+ new ParcelableFileCache<>(this, "key_import.pcl");
cache.writeCache(it.size(), it.iterator());
- // fill values for this action
- Bundle bundle = new Bundle();
- intent.putExtra(KeychainService.EXTRA_DATA, bundle);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL, true
- );
+ mOperationHelper =
+ new CryptoOperationHelper(this, this, R.string.progress_importing);
- // start service with intent
- activity.startService(intent);
+ mKeyList = null;
+ mKeyserver = null;
+ mOperationHelper.cryptoOperation();
} catch (IOException e) {
Log.e(Constants.TAG, "Problem writing cache file", e);
- Notify.create(activity, "Problem writing cache file!", Notify.Style.ERROR).show();
+ Notify.create(this, "Problem writing cache file!", Notify.Style.ERROR).show();
}
} else {
// give everything else down to KeyListActivity!
@@ -233,4 +178,39 @@ public class SafeSlingerActivity extends BaseActivity {
return list;
}
+ // CryptoOperationHelper.Callback functions
+
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return new ImportKeyringParcel(mKeyList, mKeyserver);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(ImportKeyResult result) {
+ Intent certifyIntent = new Intent(this, CertifyKeyActivity.class);
+ certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
+ certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, result.getImportedMasterKeyIds());
+ certifyIntent.putExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, mMasterKeyId);
+ startActivityForResult(certifyIntent, 0);
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+
+ }
+
+ @Override
+ public void onCryptoOperationError(ImportKeyResult result) {
+ Bundle returnData = new Bundle();
+ returnData.putParcelable(OperationResult.EXTRA_RESULT, result);
+ Intent data = new Intent();
+ data.putExtras(returnData);
+ setResult(RESULT_OK, data);
+ finish();
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
index 442bdf8f7..6fc0aaac6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
*
* This program is free software: you can redistribute it and/or modify
@@ -17,14 +18,11 @@
package org.sufficientlysecure.keychain.ui;
-import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
-import android.os.Build;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
-import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.support.v7.widget.Toolbar;
@@ -32,15 +30,16 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
-import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.AppCompatPreferenceActivity;
import org.sufficientlysecure.keychain.ui.widget.IntegerListPreference;
+import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
import java.util.List;
-public class SettingsActivity extends PreferenceActivity {
+public class SettingsActivity extends AppCompatPreferenceActivity {
public static final String ACTION_PREFS_CLOUD = "org.sufficientlysecure.keychain.ui.PREFS_CLOUD";
public static final String ACTION_PREFS_ADV = "org.sufficientlysecure.keychain.ui.PREFS_ADV";
@@ -91,22 +90,6 @@ public class SettingsActivity extends PreferenceActivity {
initializePassphraseCacheTtl(
(IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL));
- int[] valueIds = new int[]{
- CompressionAlgorithmTags.UNCOMPRESSED,
- CompressionAlgorithmTags.ZIP,
- CompressionAlgorithmTags.ZLIB,
- CompressionAlgorithmTags.BZIP2,
- };
- String[] entries = new String[]{
- getString(R.string.choice_none) + " (" + getString(R.string.compression_fast) + ")",
- "ZIP (" + getString(R.string.compression_fast) + ")",
- "ZLIB (" + getString(R.string.compression_fast) + ")",
- "BZIP2 (" + getString(R.string.compression_very_slow) + ")",};
- String[] values = new String[valueIds.length];
- for (int i = 0; i < values.length; ++i) {
- values[i] = "" + valueIds[i];
- }
-
initializeUseDefaultYubiKeyPin(
(CheckBoxPreference) findPreference(Constants.Pref.USE_DEFAULT_YUBIKEY_PIN));
@@ -122,13 +105,14 @@ public class SettingsActivity extends PreferenceActivity {
private void setupToolbar() {
ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
LinearLayout content = (LinearLayout) root.getChildAt(0);
- LinearLayout toolbarContainer = (LinearLayout) View.inflate(this, R.layout.preference_toolbar_activity, null);
+ LinearLayout toolbarContainer = (LinearLayout) View.inflate(this, R.layout.preference_toolbar, null);
root.removeAllViews();
toolbarContainer.addView(content);
root.addView(toolbarContainer);
Toolbar toolbar = (Toolbar) toolbarContainer.findViewById(R.id.toolbar);
+
toolbar.setTitle(R.string.title_preferences);
toolbar.setNavigationIcon(getResources().getDrawable(R.drawable.ic_arrow_back_white_24dp));
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@@ -141,34 +125,13 @@ public class SettingsActivity extends PreferenceActivity {
}
@Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case REQUEST_CODE_KEYSERVER_PREF: {
- if (resultCode == RESULT_CANCELED || data == null) {
- return;
- }
- String servers[] = data
- .getStringArrayExtra(SettingsKeyServerActivity.EXTRA_KEY_SERVERS);
- sPreferences.setKeyServers(servers);
- mKeyServerPreference.setSummary(keyserverSummary(this));
- break;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
- break;
- }
- }
- }
-
- @Override
public void onBuildHeaders(List<Header> target) {
super.onBuildHeaders(target);
loadHeadersFromResource(R.xml.preference_headers, target);
}
/**
- * This fragment shows the Cloud Search preferences in android 3.0+
+ * This fragment shows the Cloud Search preferences
*/
public static class CloudSearchPrefsFragment extends PreferenceFragment {
@@ -207,12 +170,7 @@ public class SettingsActivity extends PreferenceActivity {
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CODE_KEYSERVER_PREF: {
- if (resultCode == RESULT_CANCELED || data == null) {
- return;
- }
- String servers[] = data
- .getStringArrayExtra(SettingsKeyServerActivity.EXTRA_KEY_SERVERS);
- sPreferences.setKeyServers(servers);
+ // update preference, in case it changed
mKeyServerPreference.setSummary(keyserverSummary(getActivity()));
break;
}
@@ -226,7 +184,7 @@ public class SettingsActivity extends PreferenceActivity {
}
/**
- * This fragment shows the advanced preferences in android 3.0+
+ * This fragment shows the PIN/password preferences
*/
public static class AdvancedPrefsFragment extends PreferenceFragment {
@@ -243,25 +201,6 @@ public class SettingsActivity extends PreferenceActivity {
initializePassphraseCacheTtl(
(IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL));
- int[] valueIds = new int[]{
- CompressionAlgorithmTags.UNCOMPRESSED,
- CompressionAlgorithmTags.ZIP,
- CompressionAlgorithmTags.ZLIB,
- CompressionAlgorithmTags.BZIP2,
- };
-
- String[] entries = new String[]{
- getString(R.string.choice_none) + " (" + getString(R.string.compression_fast) + ")",
- "ZIP (" + getString(R.string.compression_fast) + ")",
- "ZLIB (" + getString(R.string.compression_fast) + ")",
- "BZIP2 (" + getString(R.string.compression_very_slow) + ")",
- };
-
- String[] values = new String[valueIds.length];
- for (int i = 0; i < values.length; ++i) {
- values[i] = "" + valueIds[i];
- }
-
initializeUseDefaultYubiKeyPin(
(CheckBoxPreference) findPreference(Constants.Pref.USE_DEFAULT_YUBIKEY_PIN));
@@ -270,7 +209,6 @@ public class SettingsActivity extends PreferenceActivity {
}
}
- @TargetApi(Build.VERSION_CODES.KITKAT)
protected boolean isValidFragment(String fragmentName) {
return AdvancedPrefsFragment.class.getName().equals(fragmentName)
|| CloudSearchPrefsFragment.class.getName().equals(fragmentName)
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java
index dc203756f..f61ada84f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java
@@ -17,89 +17,23 @@
package org.sufficientlysecure.keychain.ui;
-import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
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.EditorListener;
-import org.sufficientlysecure.keychain.ui.widget.KeyServerEditor;
-import java.util.Vector;
-
-public class SettingsKeyServerActivity extends BaseActivity implements OnClickListener,
- EditorListener {
+public class SettingsKeyServerActivity extends BaseActivity {
public static final String EXTRA_KEY_SERVERS = "key_servers";
- private LayoutInflater mInflater;
- private ViewGroup mEditors;
- private View mAdd;
- private View mRotate;
- private TextView mTitle;
- private TextView mSummary;
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- // Inflate a "Done"/"Cancel" custom action bar view
- setFullScreenDialogDoneClose(R.string.btn_save,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- okClicked();
- }
- },
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- cancelClicked();
- }
- });
-
- mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- mTitle = (TextView) findViewById(R.id.title);
- mSummary = (TextView) findViewById(R.id.summary);
- mSummary.setText(getText(R.string.label_first_keyserver_is_used));
-
- mTitle.setText(R.string.label_keyservers);
-
- mEditors = (ViewGroup) findViewById(R.id.editors);
- mAdd = findViewById(R.id.add);
- mAdd.setOnClickListener(this);
-
- mRotate = findViewById(R.id.rotate);
- mRotate.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- Vector<String> servers = serverList();
- String first = servers.get(0);
- if (first != null) {
- servers.remove(0);
- servers.add(first);
- String[] dummy = {};
- makeServerList(servers.toArray(dummy));
- }
- }
- });
-
Intent intent = getIntent();
String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS);
- makeServerList(servers);
+ loadFragment(savedInstanceState, servers);
}
@Override
@@ -107,118 +41,22 @@ public class SettingsKeyServerActivity extends BaseActivity implements OnClickLi
setContentView(R.layout.key_server_preference);
}
- private void makeServerList(String[] servers) {
- if (servers != null) {
- mEditors.removeAllViews();
- for (String serv : servers) {
- KeyServerEditor view = (KeyServerEditor) mInflater.inflate(
- R.layout.key_server_editor, mEditors, false);
- view.setEditorListener(this);
- view.setValue(serv);
- mEditors.addView(view);
- }
+ private void loadFragment(Bundle savedInstanceState, String[] keyservers) {
+ // However, if we're being restored from a previous state,
+ // then we don't need to do anything and should return or else
+ // we could end up with overlapping fragments.
+ if (savedInstanceState != null) {
+ return;
}
- }
-
- public void onDeleted(Editor editor, boolean wasNewItem) {
- // nothing to do
- }
- @Override
- public void onEdited() {
+ SettingsKeyserverFragment fragment = SettingsKeyserverFragment.newInstance(keyservers);
- }
-
- // button to add keyserver clicked
- 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);
- dialogFragment.show(getSupportFragmentManager(), "addKeyserverDialog");
- }
-
- public void addKeyserver(String keyserverUrl) {
- KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor,
- mEditors, false);
- view.setEditorListener(this);
- view.setValue(keyserverUrl);
- mEditors.addView(view);
- }
-
- private void cancelClicked() {
- setResult(RESULT_CANCELED, null);
- finish();
- }
-
- private Vector<String> serverList() {
- Vector<String> servers = new Vector<>();
- for (int i = 0; i < mEditors.getChildCount(); ++i) {
- KeyServerEditor editor = (KeyServerEditor) mEditors.getChildAt(i);
- String tmp = editor.getValue();
- if (tmp.length() > 0) {
- servers.add(tmp);
- }
- }
- return servers;
- }
-
- private void okClicked() {
- Intent data = new Intent();
- Vector<String> servers = new Vector<>();
- for (int i = 0; i < mEditors.getChildCount(); ++i) {
- KeyServerEditor editor = (KeyServerEditor) mEditors.getChildAt(i);
- String tmp = editor.getValue();
- if (tmp.length() > 0) {
- servers.add(tmp);
- }
- }
- String[] dummy = new String[0];
- data.putExtra(EXTRA_KEY_SERVERS, servers.toArray(dummy));
- setResult(RESULT_OK, data);
- finish();
+ // Add the fragment to the 'fragment_container' FrameLayout
+ // NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.keyserver_settings_fragment_container, fragment)
+ .commitAllowingStateLoss();
+ // do it immediately!
+ getSupportFragmentManager().executePendingTransactions();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java
new file mode 100644
index 000000000..2ae64d90b
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2012-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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.graphics.Color;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.support.v4.app.Fragment;
+import android.support.v4.view.MotionEventCompat;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.dialog.AddEditKeyserverDialogFragment;
+import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperAdapter;
+import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperViewHolder;
+import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperDragCallback;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.recyclerview.RecyclerItemClickListener;
+import org.sufficientlysecure.keychain.util.Preferences;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class SettingsKeyserverFragment extends Fragment implements RecyclerItemClickListener.OnItemClickListener {
+
+ private static final String ARG_KEYSERVER_ARRAY = "arg_keyserver_array";
+ private ItemTouchHelper mItemTouchHelper;
+
+ private ArrayList<String> mKeyservers;
+ private KeyserverListAdapter mAdapter;
+
+ public static SettingsKeyserverFragment newInstance(String[] keyservers) {
+ Bundle args = new Bundle();
+ args.putStringArray(ARG_KEYSERVER_ARRAY, keyservers);
+
+ SettingsKeyserverFragment fragment = new SettingsKeyserverFragment();
+ fragment.setArguments(args);
+
+ return fragment;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
+ savedInstanceState) {
+
+ return inflater.inflate(R.layout.settings_keyserver_fragment, null);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ String keyservers[] = getArguments().getStringArray(ARG_KEYSERVER_ARRAY);
+ mKeyservers = new ArrayList<>(Arrays.asList(keyservers));
+
+ mAdapter = new KeyserverListAdapter(mKeyservers);
+
+ RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.keyserver_recycler_view);
+ // recyclerView.setHasFixedSize(true); // the size of the first item changes
+ recyclerView.setAdapter(mAdapter);
+ recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+
+ ItemTouchHelper.Callback callback = new ItemTouchHelperDragCallback(mAdapter);
+ mItemTouchHelper = new ItemTouchHelper(callback);
+ mItemTouchHelper.attachToRecyclerView(recyclerView);
+
+ // for clicks
+ recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(getActivity(), this));
+
+ // can't use item decoration because it doesn't move with drag and drop
+ // recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null));
+
+ // We have a menu item to show in action bar.
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
+ inflater.inflate(R.menu.keyserver_pref_menu, menu);
+
+ super.onCreateOptionsMenu(menu, inflater);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+
+ case R.id.menu_add_keyserver:
+ startAddKeyserverDialog();
+ return true;
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void startAddKeyserverDialog() {
+ // keyserver and position have no meaning
+ startEditKeyserverDialog(AddEditKeyserverDialogFragment.DialogAction.ADD, null, -1);
+ }
+
+ private void startEditKeyserverDialog(AddEditKeyserverDialogFragment.DialogAction action,
+ String keyserver, final int position) {
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ Bundle data = message.getData();
+ switch (message.what) {
+ case AddEditKeyserverDialogFragment.MESSAGE_OKAY: {
+ boolean deleted =
+ data.getBoolean(AddEditKeyserverDialogFragment.MESSAGE_KEYSERVER_DELETED
+ , false);
+ if (deleted) {
+ Notify.create(getActivity(),
+ getActivity().getString(
+ R.string.keyserver_preference_deleted, mKeyservers.get(position)),
+ Notify.Style.OK)
+ .show();
+ deleteKeyserver(position);
+ return;
+ }
+ boolean verified =
+ data.getBoolean(AddEditKeyserverDialogFragment.MESSAGE_VERIFIED);
+ if (verified) {
+ Notify.create(getActivity(),
+ R.string.add_keyserver_verified, Notify.Style.OK).show();
+ } else {
+ Notify.create(getActivity(),
+ R.string.add_keyserver_without_verification,
+ Notify.Style.WARN).show();
+ }
+ String keyserver = data.getString(
+ AddEditKeyserverDialogFragment.MESSAGE_KEYSERVER);
+
+ AddEditKeyserverDialogFragment.DialogAction dialogAction
+ = (AddEditKeyserverDialogFragment.DialogAction) data.getSerializable(
+ AddEditKeyserverDialogFragment.MESSAGE_DIALOG_ACTION);
+ switch (dialogAction) {
+ case ADD:
+ addKeyserver(keyserver);
+ break;
+ case EDIT:
+ editKeyserver(keyserver, position);
+ break;
+ }
+ break;
+ }
+ case AddEditKeyserverDialogFragment.MESSAGE_VERIFICATION_FAILED: {
+ AddEditKeyserverDialogFragment.FailureReason failureReason =
+ (AddEditKeyserverDialogFragment.FailureReason) data.getSerializable(
+ AddEditKeyserverDialogFragment.MESSAGE_FAILURE_REASON);
+ switch (failureReason) {
+ case CONNECTION_FAILED: {
+ Notify.create(getActivity(),
+ R.string.add_keyserver_connection_failed,
+ Notify.Style.ERROR).show();
+ break;
+ }
+ case INVALID_URL: {
+ Notify.create(getActivity(),
+ 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);
+ AddEditKeyserverDialogFragment dialogFragment = AddEditKeyserverDialogFragment
+ .newInstance(messenger, action, keyserver, position);
+ dialogFragment.show(getFragmentManager(), "addKeyserverDialog");
+ }
+
+ private void addKeyserver(String keyserver) {
+ mKeyservers.add(keyserver);
+ mAdapter.notifyItemInserted(mKeyservers.size() - 1);
+ saveKeyserverList();
+ }
+
+ private void editKeyserver(String newKeyserver, int position) {
+ mKeyservers.set(position, newKeyserver);
+ mAdapter.notifyItemChanged(position);
+ saveKeyserverList();
+ }
+
+ private void deleteKeyserver(int position) {
+ if (mKeyservers.size() == 1) {
+ Notify.create(getActivity(), R.string.keyserver_preference_cannot_delete_last,
+ Notify.Style.ERROR).show();
+ return;
+ }
+ mKeyservers.remove(position);
+ // we use this
+ mAdapter.notifyItemRemoved(position);
+ if (position == 0 && mKeyservers.size() > 0) {
+ // if we deleted the first item, we need the adapter to redraw the new first item
+ mAdapter.notifyItemChanged(0);
+ }
+ saveKeyserverList();
+ }
+
+ private void saveKeyserverList() {
+ String servers[] = mKeyservers.toArray(new String[mKeyservers.size()]);
+ Preferences.getPreferences(getActivity()).setKeyServers(servers);
+ }
+
+ @Override
+ public void onItemClick(View view, int position) {
+ startEditKeyserverDialog(AddEditKeyserverDialogFragment.DialogAction.EDIT,
+ mKeyservers.get(position), position);
+ }
+
+ public class KeyserverListAdapter extends RecyclerView.Adapter<KeyserverListAdapter.ViewHolder>
+ implements ItemTouchHelperAdapter {
+
+ // to update the ViewHolder associated with first item, for when an item is deleted
+ private ViewHolder mFirstItem;
+
+ private final List<String> mKeyservers;
+
+ public KeyserverListAdapter(List<String> keyservers) {
+ mKeyservers = keyservers;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.settings_keyserver_item, parent, false);
+ ViewHolder viewHolder = new ViewHolder(view);
+ return viewHolder;
+ }
+
+ @Override
+ public void onBindViewHolder(final ViewHolder holder, int position) {
+ if (position == 0) {
+ mFirstItem = holder;
+ }
+ holder.keyserverUrl.setText(mKeyservers.get(position));
+
+ // Start a drag whenever the handle view it touched
+ holder.dragHandleView.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
+ mItemTouchHelper.startDrag(holder);
+ }
+ return false;
+ }
+ });
+
+ selectUnselectKeyserver(holder, position);
+ }
+
+ private void selectUnselectKeyserver(ViewHolder holder, int position) {
+
+ if (position == 0) {
+ holder.showAsSelectedKeyserver();
+ } else {
+ holder.showAsUnselectedKeyserver();
+ }
+ }
+
+ @Override
+ public void onItemMove(RecyclerView.ViewHolder source, RecyclerView.ViewHolder target,
+ int fromPosition, int toPosition) {
+ Collections.swap(mKeyservers, fromPosition, toPosition);
+ saveKeyserverList();
+ selectUnselectKeyserver((ViewHolder) target, fromPosition);
+ // we don't want source to change color while dragging, therefore we just set
+ // isSelectedKeyserver instead of selectUnselectKeyserver
+ ((ViewHolder) source).isSelectedKeyserver = toPosition == 0;
+
+ notifyItemMoved(fromPosition, toPosition);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mKeyservers.size();
+ }
+
+ public class ViewHolder extends RecyclerView.ViewHolder implements
+ ItemTouchHelperViewHolder {
+
+ public final ViewGroup outerLayout;
+ public final TextView selectedServerLabel;
+ public final TextView keyserverUrl;
+ public final ImageView dragHandleView;
+
+ private boolean isSelectedKeyserver = false;
+
+ public ViewHolder(View itemView) {
+ super(itemView);
+ outerLayout = (ViewGroup) itemView.findViewById(R.id.outer_layout);
+ selectedServerLabel = (TextView) itemView.findViewById(
+ R.id.selected_keyserver_title);
+ keyserverUrl = (TextView) itemView.findViewById(R.id.keyserver_tv);
+ dragHandleView = (ImageView) itemView.findViewById(R.id.drag_handle);
+
+ itemView.setClickable(true);
+ }
+
+ public void showAsSelectedKeyserver() {
+ isSelectedKeyserver = true;
+ selectedServerLabel.setVisibility(View.VISIBLE);
+ outerLayout.setBackgroundColor(getResources().getColor(R.color.android_green_dark));
+ }
+
+ public void showAsUnselectedKeyserver() {
+ isSelectedKeyserver = false;
+ selectedServerLabel.setVisibility(View.GONE);
+ outerLayout.setBackgroundColor(Color.WHITE);
+ }
+
+ @Override
+ public void onItemSelected() {
+ selectedServerLabel.setVisibility(View.GONE);
+ itemView.setBackgroundColor(Color.LTGRAY);
+ }
+
+ @Override
+ public void onItemClear() {
+ if (isSelectedKeyserver) {
+ showAsSelectedKeyserver();
+ } else {
+ showAsUnselectedKeyserver();
+ }
+ }
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
index e4dff6083..8b49f3b96 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
@@ -17,12 +17,9 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.support.v4.app.NavUtils;
import android.view.MenuItem;
import android.view.View;
@@ -33,23 +30,30 @@ import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.service.KeychainService;
+import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
/**
* Sends the selected public key to a keyserver
*/
-public class UploadKeyActivity extends BaseActivity {
+public class UploadKeyActivity extends BaseActivity
+ implements CryptoOperationHelper.Callback<ExportKeyringParcel, ExportResult> {
private View mUploadButton;
private Spinner mKeyServerSpinner;
private Uri mDataUri;
+ // CryptoOperationHelper.Callback vars
+ private String mKeyserver;
+ private Uri mUnifiedKeyringUri;
+ private CryptoOperationHelper<ExportKeyringParcel, ExportResult> mUploadOpHelper;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -89,51 +93,23 @@ public class UploadKeyActivity extends BaseActivity {
setContentView(R.layout.upload_key_activity);
}
- private void uploadKey() {
- // Send all information needed to service to upload key in other thread
- Intent intent = new Intent(this, KeychainService.class);
-
- intent.setAction(KeychainService.ACTION_UPLOAD_KEYRING);
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mUploadOpHelper != null) {
+ mUploadOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
- // set data uri as path to keyring
+ private void uploadKey() {
Uri blobUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
- intent.setData(blobUri);
-
- // fill values for this action
- Bundle data = new Bundle();
+ mUnifiedKeyringUri = blobUri;
String server = (String) mKeyServerSpinner.getSelectedItem();
- data.putString(KeychainService.UPLOAD_KEY_SERVER, server);
-
- intent.putExtra(KeychainService.EXTRA_DATA, data);
-
- // Message is received after uploading is done in KeychainService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(this) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
-
- Toast.makeText(UploadKeyActivity.this, R.string.msg_crt_upload_success,
- Toast.LENGTH_SHORT).show();
- finish();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(
- getString(R.string.progress_uploading),
- ProgressDialog.STYLE_HORIZONTAL, false);
+ mKeyserver = server;
- // start service with intent
- startService(intent);
+ mUploadOpHelper = new CryptoOperationHelper(this, this, R.string.progress_uploading);
+ mUploadOpHelper.cryptoOperation();
}
@Override
@@ -148,4 +124,30 @@ public class UploadKeyActivity extends BaseActivity {
}
return super.onOptionsItemSelected(item);
}
+
+ @Override
+ public ExportKeyringParcel createOperationInput() {
+ return new ExportKeyringParcel(mKeyserver, mUnifiedKeyringUri);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(ExportResult result) {
+ Toast.makeText(UploadKeyActivity.this, R.string.msg_crt_upload_success,
+ Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+
+ }
+
+ @Override
+ public void onCryptoOperationError(ExportResult result) {
+ // TODO: Implement proper log for key upload then show error
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index 5f6a32e5a..d3849c892 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -65,11 +65,13 @@ import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.service.KeychainService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
@@ -89,7 +91,8 @@ import java.util.ArrayList;
import java.util.HashMap;
public class ViewKeyActivity extends BaseNfcActivity implements
- LoaderManager.LoaderCallbacks<Cursor> {
+ LoaderManager.LoaderCallbacks<Cursor>,
+ CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
public static final String EXTRA_NFC_USER_ID = "nfc_user_id";
public static final String EXTRA_NFC_AID = "nfc_aid";
@@ -105,6 +108,11 @@ public class ViewKeyActivity extends BaseNfcActivity implements
protected Uri mDataUri;
+ // For CryptoOperationHelper.Callback
+ private String mKeyserver;
+ private ArrayList<ParcelableKeyRing> mKeyList;
+ private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;
+
private TextView mName;
private TextView mStatusText;
private ImageView mStatusImage;
@@ -396,7 +404,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
private void certifyImmediate() {
Intent intent = new Intent(this, CertifyKeyActivity.class);
- intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[] {mMasterKeyId});
+ intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{mMasterKeyId});
startCertifyIntent(intent);
}
@@ -486,6 +494,10 @@ public class ViewKeyActivity extends BaseNfcActivity implements
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mOperationHelper != null) {
+ mOperationHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+
if (requestCode == REQUEST_QR_FINGERPRINT && resultCode == Activity.RESULT_OK) {
// If there is an EXTRA_RESULT, that's an error. Just show it.
@@ -651,56 +663,20 @@ public class ViewKeyActivity extends BaseNfcActivity implements
ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
ArrayList<ParcelableKeyRing> entries = new ArrayList<>();
entries.add(keyEntry);
-
- // Message is received after importing is done in KeychainService
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(this) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
-
- mIsRefreshing = false;
-
- if (returnData == null) {
- finish();
- return;
- }
- final ImportKeyResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
- result.createNotify(ViewKeyActivity.this).show();
- }
- }
- };
-
- // fill values for this action
- Bundle data = new Bundle();
+ mKeyList = entries;
// search config
{
Preferences prefs = Preferences.getPreferences(this);
Preferences.CloudSearchPrefs cloudPrefs =
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
- data.putString(KeychainService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
+ mKeyserver = cloudPrefs.keyserver;
}
- data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, entries);
-
- // Send all information needed to service to query keys in other thread
- Intent intent = new Intent(this, KeychainService.class);
- intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
- intent.putExtra(KeychainService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
-
- // start service with intent
- startService(intent);
+ mOperationHelper = new CryptoOperationHelper<>(
+ this, this, R.string.progress_importing);
+ mOperationHelper.cryptoOperation();
}
private void editKey(Uri dataUri) {
@@ -986,4 +962,33 @@ public class ViewKeyActivity extends BaseNfcActivity implements
public void onLoaderReset(Loader<Cursor> loader) {
}
+
+ // CryptoOperationHelper.Callback functions
+
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return new ImportKeyringParcel(mKeyList, mKeyserver);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(ImportKeyResult result) {
+ mIsRefreshing = false;
+ result.createNotify(this).show();
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+ mIsRefreshing = false;
+ }
+
+ @Override
+ public void onCryptoOperationError(ImportKeyResult result) {
+ mIsRefreshing = false;
+ result.createNotify(this).show();
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java
index c33485adc..6052eec16 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java
@@ -17,15 +17,12 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -48,9 +45,10 @@ import com.textuality.keybase.lib.User;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.KeybaseVerificationResult;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.service.KeychainService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
@@ -59,7 +57,8 @@ import java.util.Hashtable;
import java.util.List;
public class ViewKeyTrustFragment extends LoaderFragment implements
- LoaderManager.LoaderCallbacks<Cursor> {
+ LoaderManager.LoaderCallbacks<Cursor>,
+ CryptoOperationHelper.Callback<KeybaseVerificationParcel, KeybaseVerificationResult> {
public static final String ARG_DATA_URI = "uri";
@@ -76,6 +75,14 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
// for retrieving the key we’re working on
private Uri mDataUri;
+ private Proof mProof;
+
+ // for CryptoOperationHelper,Callback
+ private String mKeybaseProof;
+ private String mKeybaseFingerprint;
+ private CryptoOperationHelper<KeybaseVerificationParcel, KeybaseVerificationResult>
+ mKeybaseOpHelper;
+
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
@@ -349,112 +356,130 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
}
private void verify(final Proof proof, final String fingerprint) {
- Intent intent = new Intent(getActivity(), KeychainService.class);
- Bundle data = new Bundle();
- intent.setAction(KeychainService.ACTION_VERIFY_KEYBASE_PROOF);
- data.putString(KeychainService.KEYBASE_PROOF, proof.toString());
- data.putString(KeychainService.KEYBASE_REQUIRED_FINGERPRINT, fingerprint);
- intent.putExtra(KeychainService.EXTRA_DATA, data);
+ mProof = proof;
+ mKeybaseProof = proof.toString();
+ mKeybaseFingerprint = fingerprint;
mProofVerifyDetail.setVisibility(View.GONE);
- // Create a new Messenger for the communication back after proof work is done
- ServiceProgressHandler handler = new ServiceProgressHandler(getActivity()) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- Bundle returnData = message.getData();
- String msg = returnData.getString(ServiceProgressHandler.DATA_MESSAGE);
- SpannableStringBuilder ssb = new SpannableStringBuilder();
-
- if ((msg != null) && msg.equals("OK")) {
-
- //yay
- String proofUrl = returnData.getString(ServiceProgressHandler.KEYBASE_PROOF_URL);
- String presenceUrl = returnData.getString(ServiceProgressHandler.KEYBASE_PRESENCE_URL);
- String presenceLabel = returnData.getString(ServiceProgressHandler.KEYBASE_PRESENCE_LABEL);
-
- String proofLabel;
- switch (proof.getType()) {
- case Proof.PROOF_TYPE_TWITTER:
- proofLabel = getString(R.string.keybase_twitter_proof);
- break;
- case Proof.PROOF_TYPE_DNS:
- proofLabel = getString(R.string.keybase_dns_proof);
- break;
- case Proof.PROOF_TYPE_WEB_SITE:
- proofLabel = getString(R.string.keybase_web_site_proof);
- break;
- case Proof.PROOF_TYPE_GITHUB:
- proofLabel = getString(R.string.keybase_github_proof);
- break;
- case Proof.PROOF_TYPE_REDDIT:
- proofLabel = getString(R.string.keybase_reddit_proof);
- break;
- default:
- proofLabel = getString(R.string.keybase_a_post);
- break;
- }
+ mKeybaseOpHelper = new CryptoOperationHelper<>(this, this,
+ R.string.progress_verifying_signature);
+ mKeybaseOpHelper.cryptoOperation();
+ }
- ssb.append(getString(R.string.keybase_proof_succeeded));
- StyleSpan bold = new StyleSpan(Typeface.BOLD);
- ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- ssb.append("\n\n");
- int length = ssb.length();
- ssb.append(proofLabel);
- if (proofUrl != null) {
- URLSpan postLink = new URLSpan(proofUrl);
- ssb.setSpan(postLink, length, length + proofLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- if (Proof.PROOF_TYPE_DNS == proof.getType()) {
- ssb.append(" ").append(getString(R.string.keybase_for_the_domain)).append(" ");
- } else {
- ssb.append(" ").append(getString(R.string.keybase_fetched_from)).append(" ");
- }
- length = ssb.length();
- URLSpan presenceLink = new URLSpan(presenceUrl);
- ssb.append(presenceLabel);
- ssb.setSpan(presenceLink, length, length + presenceLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- if (Proof.PROOF_TYPE_REDDIT == proof.getType()) {
- ssb.append(", ").
- append(getString(R.string.keybase_reddit_attribution)).
- append(" “").append(proof.getHandle()).append("”, ");
- }
- ssb.append(" ").append(getString(R.string.keybase_contained_signature));
- } else {
- // verification failed!
- msg = returnData.getString(ServiceProgressHandler.DATA_ERROR);
- ssb.append(getString(R.string.keybase_proof_failure));
- if (msg == null) {
- msg = getString(R.string.keybase_unknown_proof_failure);
- }
- StyleSpan bold = new StyleSpan(Typeface.BOLD);
- ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- ssb.append("\n\n").append(msg);
- }
- mProofVerifyHeader.setVisibility(View.VISIBLE);
- mProofVerifyDetail.setVisibility(View.VISIBLE);
- mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance());
- mProofVerifyDetail.setText(ssb);
- }
- }
- };
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (mKeybaseOpHelper != null) {
+ mKeybaseOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ // CryptoOperationHelper.Callback methods
+ @Override
+ public KeybaseVerificationParcel createOperationInput() {
+ return new KeybaseVerificationParcel(mKeybaseProof, mKeybaseFingerprint);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(KeybaseVerificationResult result) {
+
+ result.createNotify(getActivity()).show();
+
+ String proofUrl = result.mProofUrl;
+ String presenceUrl = result.mPresenceUrl;
+ String presenceLabel = result.mPresenceLabel;
+
+ Proof proof = mProof; // TODO: should ideally be contained in result
+
+ String proofLabel;
+ switch (proof.getType()) {
+ case Proof.PROOF_TYPE_TWITTER:
+ proofLabel = getString(R.string.keybase_twitter_proof);
+ break;
+ case Proof.PROOF_TYPE_DNS:
+ proofLabel = getString(R.string.keybase_dns_proof);
+ break;
+ case Proof.PROOF_TYPE_WEB_SITE:
+ proofLabel = getString(R.string.keybase_web_site_proof);
+ break;
+ case Proof.PROOF_TYPE_GITHUB:
+ proofLabel = getString(R.string.keybase_github_proof);
+ break;
+ case Proof.PROOF_TYPE_REDDIT:
+ proofLabel = getString(R.string.keybase_reddit_proof);
+ break;
+ default:
+ proofLabel = getString(R.string.keybase_a_post);
+ break;
+ }
+
+ SpannableStringBuilder ssb = new SpannableStringBuilder();
+
+ ssb.append(getString(R.string.keybase_proof_succeeded));
+ StyleSpan bold = new StyleSpan(Typeface.BOLD);
+ ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ssb.append("\n\n");
+ int length = ssb.length();
+ ssb.append(proofLabel);
+ if (proofUrl != null) {
+ URLSpan postLink = new URLSpan(proofUrl);
+ ssb.setSpan(postLink, length, length + proofLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ if (Proof.PROOF_TYPE_DNS == proof.getType()) {
+ ssb.append(" ").append(getString(R.string.keybase_for_the_domain)).append(" ");
+ } else {
+ ssb.append(" ").append(getString(R.string.keybase_fetched_from)).append(" ");
+ }
+ length = ssb.length();
+ URLSpan presenceLink = new URLSpan(presenceUrl);
+ ssb.append(presenceLabel);
+ ssb.setSpan(presenceLink, length, length + presenceLabel.length(), Spanned
+ .SPAN_EXCLUSIVE_EXCLUSIVE);
+ if (Proof.PROOF_TYPE_REDDIT == proof.getType()) {
+ ssb.append(", ").
+ append(getString(R.string.keybase_reddit_attribution)).
+ append(" “").append(proof.getHandle()).append("”, ");
+ }
+ ssb.append(" ").append(getString(R.string.keybase_contained_signature));
+
+ displaySpannableResult(ssb);
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+
+ }
+
+ @Override
+ public void onCryptoOperationError(KeybaseVerificationResult result) {
+
+ result.createNotify(getActivity()).show();
+
+ SpannableStringBuilder ssb = new SpannableStringBuilder();
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(handler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
+ ssb.append(getString(R.string.keybase_proof_failure));
+ String msg = getString(result.getLog().getLast().mType.mMsgId);
+ if (msg == null) {
+ msg = getString(R.string.keybase_unknown_proof_failure);
+ }
+ StyleSpan bold = new StyleSpan(Typeface.BOLD);
+ ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ssb.append("\n\n").append(msg);
+
+ displaySpannableResult(ssb);
+ }
- // show progress dialog
- handler.showProgressDialog(
- getString(R.string.progress_verifying_signature),
- ProgressDialog.STYLE_HORIZONTAL, false
- );
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
- // start service with intent
- getActivity().startService(intent);
+ private void displaySpannableResult(SpannableStringBuilder ssb) {
+ mProofVerifyHeader.setVisibility(View.VISIBLE);
+ mProofVerifyDetail.setVisibility(View.VISIBLE);
+ mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance());
+ mProofVerifyDetail.setText(ssb);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java
index ab25341d3..f8c3b59ea 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java
@@ -21,12 +21,8 @@ package org.sufficientlysecure.keychain.ui;
import java.nio.ByteBuffer;
import java.util.Arrays;
-import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
-import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -39,16 +35,16 @@ import android.widget.TextView;
import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
-import org.sufficientlysecure.keychain.service.KeychainService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.service.PromoteKeyringParcel;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-public class ViewKeyYubiKeyFragment extends Fragment
+public class ViewKeyYubiKeyFragment
+ extends CryptoOperationFragment<PromoteKeyringParcel, PromoteKeyResult>
implements LoaderCallbacks<Cursor> {
public static final String ARG_MASTER_KEY_ID = "master_key_id";
@@ -60,6 +56,8 @@ public class ViewKeyYubiKeyFragment extends Fragment
private String mUserId;
private byte[] mCardAid;
private long mMasterKeyId;
+ private long[] mSubKeyIds;
+
private Button vButton;
private TextView vStatus;
@@ -127,50 +125,15 @@ public class ViewKeyYubiKeyFragment extends Fragment
}
public void promoteToSecretKey() {
-
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle returnData = message.getData();
-
- PromoteKeyResult result =
- returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
-
- result.createNotify(getActivity()).show();
- }
-
- }
- };
-
- // Send all information needed to service to decrypt in other thread
- Intent intent = new Intent(getActivity(), KeychainService.class);
-
- // fill values for this action
-
- intent.setAction(KeychainService.ACTION_PROMOTE_KEYRING);
-
- Bundle data = new Bundle();
- data.putLong(KeychainService.PROMOTE_MASTER_KEY_ID, mMasterKeyId);
- data.putByteArray(KeychainService.PROMOTE_CARD_AID, mCardAid);
long[] subKeyIds = new long[mFingerprints.length];
for (int i = 0; i < subKeyIds.length; i++) {
subKeyIds[i] = KeyFormattingUtils.getKeyIdFromFingerprint(mFingerprints[i]);
}
- data.putLongArray(KeychainService.PROMOTE_SUBKEY_IDS, subKeyIds);
- intent.putExtra(KeychainService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
- // start service with intent
- getActivity().startService(intent);
+ // mMasterKeyId and mCardAid are already set
+ mSubKeyIds = subKeyIds;
+ cryptoOperation();
}
public static final String[] PROJECTION = new String[]{
@@ -240,4 +203,14 @@ public class ViewKeyYubiKeyFragment extends Fragment
public void onLoaderReset(Loader<Cursor> loader) {
}
+
+ @Override
+ public PromoteKeyringParcel createOperationInput() {
+ return new PromoteKeyringParcel(mMasterKeyId, mCardAid, mSubKeyIds);
+ }
+
+ @Override
+ protected void onCryptoOperationResult(PromoteKeyResult result) {
+ result.createNotify(getActivity()).show();
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
index db88de676..93c6593ab 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
@@ -33,6 +33,7 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
+import org.sufficientlysecure.keychain.operations.ImportOperation;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Highlighter;
@@ -92,8 +93,8 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
}
/** This method returns a list of all selected entries, with public keys sorted
- * before secret keys, see ImportExportOperation for specifics.
- * @see org.sufficientlysecure.keychain.operations.ImportExportOperation
+ * before secret keys, see ImportOperation for specifics.
+ * @see ImportOperation
*/
public ArrayList<ImportKeysListEntry> getSelectedEntries() {
ArrayList<ImportKeysListEntry> result = new ArrayList<>();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java
index a28a5ea59..bede16b2a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java
@@ -94,7 +94,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
public void onNewIntent(Intent intent) {
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
try {
- handleNdefDiscoveredIntent(intent);
+ handleTagDiscoveredIntent(intent);
} catch (CardException e) {
handleNfcError(e);
} catch (IOException e) {
@@ -269,7 +269,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
* This method is called by onNewIntent above upon discovery of an NFC tag.
* It handles initialization and login to the application, subsequently
* calls either nfcCalculateSignature() or nfcDecryptSessionKey(), then
- * finishes the activity with an appropiate result.
+ * finishes the activity with an appropriate result.
*
* On general communication, see also
* http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_annex-a.aspx
@@ -278,7 +278,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
* on ISO SmartCard Systems specification.
*
*/
- protected void handleNdefDiscoveredIntent(Intent intent) throws IOException {
+ protected void handleTagDiscoveredIntent(Intent intent) throws IOException {
Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java
index 87c6ee3ca..17e4e6ede 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java
@@ -36,7 +36,8 @@ public abstract class CachingCryptoOperationFragment <T extends Parcelable, S ex
mCachedActionsParcel = null;
}
- protected abstract T createOperationInput();
+ @Override
+ public abstract T createOperationInput();
protected T getCachedActionsParcel() {
return mCachedActionsParcel;
@@ -46,7 +47,7 @@ public abstract class CachingCryptoOperationFragment <T extends Parcelable, S ex
mCachedActionsParcel = cachedActionsParcel;
}
- protected void onCryptoOperationCancelled() {
+ public void onCryptoOperationCancelled() {
mCachedActionsParcel = null;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java
index 764602735..115f52d56 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java
@@ -18,217 +18,78 @@
package org.sufficientlysecure.keychain.ui.base;
-import android.app.Activity;
-import android.app.ProgressDialog;
import android.content.Intent;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.service.KeychainNewService;
-import org.sufficientlysecure.keychain.service.KeychainService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
-import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
-import org.sufficientlysecure.keychain.ui.NfcOperationActivity;
-import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
-import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
-
/**
* All fragments executing crypto operations need to extend this class.
*/
-public abstract class CryptoOperationFragment <T extends Parcelable, S extends OperationResult>
- extends Fragment {
-
- public static final int REQUEST_CODE_PASSPHRASE = 0x00008001;
- public static final int REQUEST_CODE_NFC = 0x00008002;
-
- private void initiateInputActivity(RequiredInputParcel requiredInput) {
-
- switch (requiredInput.mType) {
- case NFC_KEYTOCARD:
- case NFC_DECRYPT:
- case NFC_SIGN: {
- Intent intent = new Intent(getActivity(), NfcOperationActivity.class);
- intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput);
- startActivityForResult(intent, REQUEST_CODE_NFC);
- return;
- }
-
- case PASSPHRASE:
- case PASSPHRASE_SYMMETRIC: {
- Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class);
- intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, requiredInput);
- startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
- return;
- }
- }
-
- throw new RuntimeException("Unhandled pending result!");
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode == Activity.RESULT_CANCELED) {
- onCryptoOperationCancelled();
- return;
- }
-
- switch (requestCode) {
- case REQUEST_CODE_PASSPHRASE: {
- if (resultCode == Activity.RESULT_OK && data != null) {
- CryptoInputParcel cryptoInput =
- data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT);
- cryptoOperation(cryptoInput);
- return;
- }
- break;
- }
-
- case REQUEST_CODE_NFC: {
- if (resultCode == Activity.RESULT_OK && data != null) {
- CryptoInputParcel cryptoInput =
- data.getParcelableExtra(NfcOperationActivity.RESULT_DATA);
- cryptoOperation(cryptoInput);
- return;
- }
- break;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
- }
- }
- }
+public abstract class CryptoOperationFragment<T extends Parcelable, S extends OperationResult>
+ extends Fragment implements CryptoOperationHelper.Callback<T, S> {
- protected void dismissProgress() {
+ private CryptoOperationHelper<T, S> mOperationHelper;
- ProgressDialogFragment progressDialogFragment =
- (ProgressDialogFragment) getFragmentManager().findFragmentByTag("progressDialog");
+ public CryptoOperationFragment() {
- if (progressDialogFragment == null) {
- return;
- }
-
- progressDialogFragment.dismissAllowingStateLoss();
+ mOperationHelper = new CryptoOperationHelper<>(this, this);
+ }
+ public void setProgressMessageResource(int id) {
+ mOperationHelper.setProgressMessageResource(id);
}
- protected abstract T createOperationInput();
- protected void cryptoOperation(CryptoInputParcel cryptoInput) {
- cryptoOperation(cryptoInput, true);
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ mOperationHelper.handleActivityResult(requestCode, resultCode, data);
}
+ @Override
+ public abstract T createOperationInput();
+
protected void cryptoOperation() {
cryptoOperation(new CryptoInputParcel());
}
- protected void cryptoOperation(CryptoInputParcel cryptoInput, boolean showProgress) {
-
- T operationInput = createOperationInput();
- if (operationInput == null) {
- return;
- }
-
- // Send all information needed to service to edit key in other thread
- Intent intent = new Intent(getActivity(), KeychainNewService.class);
-
- intent.putExtra(KeychainNewService.EXTRA_OPERATION_INPUT, operationInput);
- intent.putExtra(KeychainNewService.EXTRA_CRYPTO_INPUT, cryptoInput);
-
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
-
- // get returned data bundle
- Bundle returnData = message.getData();
- if (returnData == null) {
- return;
- }
-
- final OperationResult result =
- returnData.getParcelable(OperationResult.EXTRA_RESULT);
-
- onHandleResult(result);
- }
- }
-
- @Override
- protected void onSetProgress(String msg, int progress, int max) {
- // allow handling of progress in fragment, or delegate upwards
- if ( ! onCryptoSetProgress(msg, progress, max)) {
- super.onSetProgress(msg, progress, max);
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
-
- if (showProgress) {
- saveHandler.showProgressDialog(
- getString(R.string.progress_building_key),
- ProgressDialog.STYLE_HORIZONTAL, false);
- }
-
- getActivity().startService(intent);
-
+ protected void cryptoOperation(CryptoInputParcel cryptoInput) {
+ cryptoOperation(cryptoInput, true);
}
- protected void onCryptoOperationResult(S result) {
- if (result.success()) {
- onCryptoOperationSuccess(result);
- } else {
- onCryptoOperationError(result);
- }
+ protected void cryptoOperation(CryptoInputParcel cryptoInput, boolean showProgress) {
+ mOperationHelper.cryptoOperation(cryptoInput, showProgress);
}
- abstract protected void onCryptoOperationSuccess(S result);
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
- protected void onCryptoOperationError(S result) {
+ @Override
+ public void onCryptoOperationError(S result) {
+ onCryptoOperationResult(result);
result.createNotify(getActivity()).show();
}
- protected void onCryptoOperationCancelled() {
+ @Override
+ public void onCryptoOperationCancelled() {
}
- public void onHandleResult(OperationResult result) {
-
- if (result instanceof InputPendingResult) {
- InputPendingResult pendingResult = (InputPendingResult) result;
- if (pendingResult.isPending()) {
- RequiredInputParcel requiredInput = pendingResult.getRequiredInputParcel();
- initiateInputActivity(requiredInput);
- return;
- }
- }
-
- dismissProgress();
-
- try {
- // noinspection unchecked, because type erasure :(
- onCryptoOperationResult((S) result);
- } catch (ClassCastException e) {
- throw new AssertionError("bad return class ("
- + result.getClass().getSimpleName() + "), this is a programming error!");
- }
-
+ @Override
+ public void onCryptoOperationSuccess(S result) {
+ onCryptoOperationResult(result);
}
- protected boolean onCryptoSetProgress(String msg, int progress, int max) {
- return false;
+ /**
+ *
+ * To be overriden by subclasses, if desired. Provides a way to access the method by the
+ * same name in CryptoOperationHelper, if super.onCryptoOperationSuccess and
+ * super.onCryptoOperationError are called at the start of the respective functions in the
+ * subclass overriding them
+ * @param result
+ */
+ protected void onCryptoOperationResult(S result) {
}
-
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java
new file mode 100644
index 000000000..240dd0972
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 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.base;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcelable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+
+import android.support.v4.app.FragmentManager;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.service.KeychainService;
+import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
+import org.sufficientlysecure.keychain.ui.NfcOperationActivity;
+import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
+import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
+import org.sufficientlysecure.keychain.util.Log;
+
+/**
+ * Designed to be integrated into activities or fragments used for CryptoOperations.
+ * Encapsulates the execution of a crypto operation and handling of input pending cases.s
+ *
+ * @param <T> The type of input parcel sent to the operation
+ * @param <S> The type of result returned by the operation
+ */
+public class CryptoOperationHelper<T extends Parcelable, S extends OperationResult> {
+
+ public interface Callback <T extends Parcelable, S extends OperationResult> {
+ T createOperationInput();
+ void onCryptoOperationSuccess(S result);
+ void onCryptoOperationCancelled();
+ void onCryptoOperationError(S result);
+ boolean onCryptoSetProgress(String msg, int progress, int max);
+ }
+
+ public static final int REQUEST_CODE_PASSPHRASE = 0x00008001;
+ public static final int REQUEST_CODE_NFC = 0x00008002;
+
+ // keeps track of request code used to start an activity from this CryptoOperationHelper.
+ // this is necessary when multiple CryptoOperationHelpers are used in the same fragment/activity
+ // otherwise all CryptoOperationHandlers may respond to the same onActivityResult
+ private int mRequestedCode = -1;
+
+ private int mProgressMessageResource;
+
+ private FragmentActivity mActivity;
+ private Fragment mFragment;
+ private Callback<T, S> mCallback;
+
+ private boolean mUseFragment; // short hand for mActivity == null
+
+ /**
+ * If OperationHelper is being integrated into an activity
+ *
+ * @param activity
+ */
+ public CryptoOperationHelper(FragmentActivity activity, Callback<T, S> callback, int progressMessageString) {
+ mActivity = activity;
+ mUseFragment = false;
+ mCallback = callback;
+ mProgressMessageResource = progressMessageString;
+ }
+
+ /**
+ * if OperationHelper is being integrated into a fragment
+ *
+ * @param fragment
+ */
+ public CryptoOperationHelper(Fragment fragment, Callback<T, S> callback, int progressMessageString) {
+ mFragment = fragment;
+ mUseFragment = true;
+ mProgressMessageResource = progressMessageString;
+ mCallback = callback;
+ }
+
+ /**
+ * if OperationHelper is being integrated into a fragment with default message for the progress dialog
+ *
+ * @param fragment
+ */
+ public CryptoOperationHelper(Fragment fragment, Callback<T, S> callback) {
+ mFragment = fragment;
+ mUseFragment = true;
+ mProgressMessageResource = R.string.progress_building_key;
+ mCallback = callback;
+ }
+
+ public void setProgressMessageResource(int id) {
+ mProgressMessageResource = id;
+ }
+
+ private void initiateInputActivity(RequiredInputParcel requiredInput) {
+
+ Activity activity = mUseFragment ? mFragment.getActivity() : mActivity;
+
+ switch (requiredInput.mType) {
+ case NFC_MOVE_KEY_TO_CARD:
+ case NFC_DECRYPT:
+ case NFC_SIGN: {
+ Intent intent = new Intent(activity, NfcOperationActivity.class);
+ intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput);
+ mRequestedCode = REQUEST_CODE_NFC;
+ if (mUseFragment) {
+ mFragment.startActivityForResult(intent, mRequestedCode);
+ } else {
+ mActivity.startActivityForResult(intent, mRequestedCode);
+ }
+ return;
+ }
+
+ case PASSPHRASE:
+ case PASSPHRASE_SYMMETRIC: {
+ Intent intent = new Intent(activity, PassphraseDialogActivity.class);
+ intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, requiredInput);
+ mRequestedCode = REQUEST_CODE_PASSPHRASE;
+ if (mUseFragment) {
+ mFragment.startActivityForResult(intent, mRequestedCode);
+ } else {
+ mActivity.startActivityForResult(intent, mRequestedCode);
+ }
+ return;
+ }
+ }
+
+ throw new RuntimeException("Unhandled pending result!");
+ }
+
+ /**
+ * Attempts the result of an activity started by this helper. Returns true if requestCode is recognized,
+ * false otherwise.
+ *
+ * @param requestCode
+ * @param resultCode
+ * @param data
+ * @return true if requestCode was recognized, false otherwise
+ */
+ public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.d(Constants.TAG, "received activity result in OperationHelper");
+
+ if (mRequestedCode != requestCode) {
+ // this wasn't meant for us to handle
+ return false;
+ } else {
+ mRequestedCode = -1;
+ }
+ if (resultCode == Activity.RESULT_CANCELED) {
+ mCallback.onCryptoOperationCancelled();
+ return true;
+ }
+
+ switch (requestCode) {
+ case REQUEST_CODE_PASSPHRASE: {
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ CryptoInputParcel cryptoInput =
+ data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT);
+ cryptoOperation(cryptoInput);
+ return true;
+ }
+ break;
+ }
+
+ case REQUEST_CODE_NFC: {
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ CryptoInputParcel cryptoInput =
+ data.getParcelableExtra(NfcOperationActivity.RESULT_DATA);
+ cryptoOperation(cryptoInput);
+ return true;
+ }
+ break;
+ }
+
+ default: {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ protected void dismissProgress() {
+ FragmentManager fragmentManager =
+ mUseFragment ? mFragment.getFragmentManager() :
+ mActivity.getSupportFragmentManager();
+
+ if (fragmentManager == null) { // the fragment holding us has died
+ // fragmentManager was null when used with DialogFragments. (they close on click?)
+ return;
+ }
+
+ ProgressDialogFragment progressDialogFragment =
+ (ProgressDialogFragment) fragmentManager.findFragmentByTag(
+ ServiceProgressHandler.TAG_PROGRESS_DIALOG);
+
+ if (progressDialogFragment == null) {
+ return;
+ }
+
+ progressDialogFragment.dismissAllowingStateLoss();
+
+ }
+
+ public void cryptoOperation(CryptoInputParcel cryptoInput, boolean showProgress) {
+
+ FragmentActivity activity = mUseFragment ? mFragment.getActivity() : mActivity;
+
+ T operationInput = mCallback.createOperationInput();
+ if (operationInput == null) {
+ return;
+ }
+
+ // Send all information needed to service to edit key in other thread
+ Intent intent = new Intent(activity, KeychainService.class);
+
+ intent.putExtra(KeychainService.EXTRA_OPERATION_INPUT, operationInput);
+ intent.putExtra(KeychainService.EXTRA_CRYPTO_INPUT, cryptoInput);
+
+ ServiceProgressHandler saveHandler = new ServiceProgressHandler(activity) {
+ @Override
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == MessageStatus.OKAY.ordinal()) {
+
+ // get returned data bundle
+ Bundle returnData = message.getData();
+ if (returnData == null) {
+ return;
+ }
+
+ final OperationResult result =
+ returnData.getParcelable(OperationResult.EXTRA_RESULT);
+
+ onHandleResult(result);
+ }
+ }
+
+ @Override
+ protected void onSetProgress(String msg, int progress, int max) {
+ // allow handling of progress in fragment, or delegate upwards
+ if ( ! mCallback.onCryptoSetProgress(msg, progress, max)) {
+ super.onSetProgress(msg, progress, max);
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
+
+ if (showProgress) {
+ saveHandler.showProgressDialog(
+ activity.getString(mProgressMessageResource),
+ ProgressDialog.STYLE_HORIZONTAL, false);
+ }
+
+ activity.startService(intent);
+ }
+
+ public void cryptoOperation(CryptoInputParcel cryptoInputParcel) {
+ cryptoOperation(cryptoInputParcel, true);
+ }
+
+ public void cryptoOperation() {
+ cryptoOperation(new CryptoInputParcel());
+ }
+
+ protected void onCryptoOperationResult(S result) {
+ if (result.success()) {
+ mCallback.onCryptoOperationSuccess(result);
+ } else {
+ mCallback.onCryptoOperationError(result);
+ }
+ }
+
+ public void onHandleResult(OperationResult result) {
+ Log.d(Constants.TAG, "Handling result in OperationHelper success: " + result.success());
+
+ if (result instanceof InputPendingResult) {
+ InputPendingResult pendingResult = (InputPendingResult) result;
+ if (pendingResult.isPending()) {
+
+ RequiredInputParcel requiredInput = pendingResult.getRequiredInputParcel();
+ initiateInputActivity(requiredInput);
+ return;
+ }
+ }
+
+ dismissProgress();
+
+ try {
+ // noinspection unchecked, because type erasure :(
+ onCryptoOperationResult((S) result);
+ } catch (ClassCastException e) {
+ throw new AssertionError("bad return class ("
+ + result.getClass().getSimpleName() + "), this is a programming error!");
+ }
+
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddKeyserverDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java
index 2c1714b67..321242b2f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddKeyserverDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java
@@ -53,8 +53,11 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.TlsHelper;
-public class AddKeyserverDialogFragment extends DialogFragment implements OnEditorActionListener {
- private static final String ARG_MESSENGER = "messenger";
+public class AddEditKeyserverDialogFragment extends DialogFragment implements OnEditorActionListener {
+ private static final String ARG_MESSENGER = "arg_messenger";
+ private static final String ARG_ACTION = "arg_dialog_action";
+ private static final String ARG_POSITION = "arg_position";
+ private static final String ARG_KEYSERVER = "arg_keyserver";
public static final int MESSAGE_OKAY = 1;
public static final int MESSAGE_VERIFICATION_FAILED = 2;
@@ -62,20 +65,37 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
public static final String MESSAGE_KEYSERVER = "new_keyserver";
public static final String MESSAGE_VERIFIED = "verified";
public static final String MESSAGE_FAILURE_REASON = "failure_reason";
+ public static final String MESSAGE_KEYSERVER_DELETED = "keyserver_deleted";
+ public static final String MESSAGE_DIALOG_ACTION = "message_dialog_action";
+ public static final String MESSAGE_EDIT_POSITION = "keyserver_edited_position";
private Messenger mMessenger;
+ private DialogAction mDialogAction;
+ private int mPosition;
+
private EditText mKeyserverEditText;
private CheckBox mVerifyKeyserverCheckBox;
+ public enum DialogAction {
+ ADD,
+ EDIT
+ }
+
public enum FailureReason {
INVALID_URL,
CONNECTION_FAILED
}
- public static AddKeyserverDialogFragment newInstance(Messenger messenger) {
- AddKeyserverDialogFragment frag = new AddKeyserverDialogFragment();
+ public static AddEditKeyserverDialogFragment newInstance(Messenger messenger,
+ DialogAction action,
+ String keyserver,
+ int position) {
+ AddEditKeyserverDialogFragment frag = new AddEditKeyserverDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger);
+ args.putSerializable(ARG_ACTION, action);
+ args.putString(ARG_KEYSERVER, keyserver);
+ args.putInt(ARG_POSITION, position);
frag.setArguments(args);
@@ -88,11 +108,11 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
final Activity activity = getActivity();
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
+ mDialogAction = (DialogAction) getArguments().getSerializable(ARG_ACTION);
+ mPosition = getArguments().getInt(ARG_POSITION);
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
- alert.setTitle(R.string.add_keyserver_dialog_title);
-
LayoutInflater inflater = activity.getLayoutInflater();
View view = inflater.inflate(R.layout.add_keyserver_dialog, null);
alert.setView(view);
@@ -100,14 +120,26 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
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
+ switch (mDialogAction) {
+ case ADD: {
+ alert.setTitle(R.string.add_keyserver_dialog_title);
+ break;
+ }
+ case EDIT: {
+ alert.setTitle(R.string.edit_keyserver_dialog_title);
+ mKeyserverEditText.setText(getArguments().getString(ARG_KEYSERVER));
+ break;
+ }
+ }
+
+ // we don't want dialog to be dismissed on click for keyserver addition or edit,
+ // 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
+ // actual listener set in onStart for adding keyservers or editing them
}
});
@@ -119,6 +151,23 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
}
});
+ switch (mDialogAction) {
+ case EDIT: {
+ alert.setNeutralButton(R.string.label_keyserver_dialog_delete,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ deleteKeyserver(mPosition);
+ }
+ });
+ break;
+ }
+ case ADD: {
+ // do nothing
+ break;
+ }
+ }
+
// 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/
@@ -155,25 +204,41 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
positiveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
+ // behaviour same for edit and add
String keyserverUrl = mKeyserverEditText.getText().toString();
if (mVerifyKeyserverCheckBox.isChecked()) {
verifyConnection(keyserverUrl);
} else {
dismiss();
// return unverified keyserver back to activity
- addKeyserver(keyserverUrl, false);
+ keyserverEdited(keyserverUrl, false);
}
}
});
}
}
- public void addKeyserver(String keyserver, boolean verified) {
+ public void keyserverEdited(String keyserver, boolean verified) {
dismiss();
Bundle data = new Bundle();
+ data.putSerializable(MESSAGE_DIALOG_ACTION, mDialogAction);
data.putString(MESSAGE_KEYSERVER, keyserver);
data.putBoolean(MESSAGE_VERIFIED, verified);
+ if (mDialogAction == DialogAction.EDIT) {
+ data.putInt(MESSAGE_EDIT_POSITION, mPosition);
+ }
+
+ sendMessageToHandler(MESSAGE_OKAY, data);
+ }
+
+ public void deleteKeyserver(int position) {
+ dismiss();
+ Bundle data = new Bundle();
+ data.putSerializable(MESSAGE_DIALOG_ACTION, DialogAction.EDIT);
+ data.putInt(MESSAGE_EDIT_POSITION, position);
+ data.putBoolean(MESSAGE_KEYSERVER_DELETED, true);
+
sendMessageToHandler(MESSAGE_OKAY, data);
}
@@ -238,7 +303,7 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
protected void onPostExecute(FailureReason failureReason) {
mProgressDialog.dismiss();
if (failureReason == null) {
- addKeyserver(mKeyserver, true);
+ keyserverEdited(mKeyserver, true);
} else {
verificationFailed(failureReason);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
index 076876b5b..7fc9da9ec 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
@@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Dialog;
-import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
@@ -33,16 +32,20 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.DeleteResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainService;
+import org.sufficientlysecure.keychain.service.DeleteKeyringParcel;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.Log;
import java.util.HashMap;
-public class DeleteKeyDialogFragment extends DialogFragment {
+public class DeleteKeyDialogFragment extends DialogFragment
+ implements CryptoOperationHelper.Callback<DeleteKeyringParcel, DeleteResult> {
private static final String ARG_MESSENGER = "messenger";
private static final String ARG_DELETE_MASTER_KEY_IDS = "delete_master_key_ids";
@@ -52,6 +55,13 @@ public class DeleteKeyDialogFragment extends DialogFragment {
private TextView mMainMessage;
private View mInflateView;
+ private Messenger mMessenger;
+
+ // for CryptoOperationHelper.Callback
+ private long[] mMasterKeyIds;
+ private boolean mHasSecret;
+ private CryptoOperationHelper<DeleteKeyringParcel, DeleteResult> mDeleteOpHelper;
+
/**
* Creates new instance of this delete file dialog fragment
*/
@@ -68,9 +78,17 @@ public class DeleteKeyDialogFragment extends DialogFragment {
}
@Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mDeleteOpHelper != null) {
+ mDeleteOpHelper.handleActivityResult(requestCode, resultCode, data);
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+ @Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final FragmentActivity activity = getActivity();
- final Messenger messenger = getArguments().getParcelable(ARG_MESSENGER);
+ mMessenger = getArguments().getParcelable(ARG_MESSENGER);
final long[] masterKeyIds = getArguments().getLongArray(ARG_DELETE_MASTER_KEY_IDS);
@@ -129,47 +147,16 @@ public class DeleteKeyDialogFragment extends DialogFragment {
@Override
public void onClick(DialogInterface dialog, int which) {
- // Send all information needed to service to import key in other thread
- Intent intent = new Intent(getActivity(), KeychainService.class);
-
- intent.setAction(KeychainService.ACTION_DELETE);
-
- // Message is received after importing is done in KeychainService
- ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
- @Override
- public void handleMessage(Message message) {
- super.handleMessage(message);
- // handle messages by standard KeychainIntentServiceHandler first
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- try {
- Message msg = Message.obtain();
- msg.copyFrom(message);
- messenger.send(msg);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "messenger error", e);
- }
- }
- }
- };
-
- // fill values for this action
- Bundle data = new Bundle();
- data.putLongArray(KeychainService.DELETE_KEY_LIST, masterKeyIds);
- data.putBoolean(KeychainService.DELETE_IS_SECRET, hasSecret);
- intent.putExtra(KeychainService.EXTRA_DATA, data);
+ mMasterKeyIds = masterKeyIds;
+ mHasSecret = hasSecret;
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(getString(R.string.progress_deleting),
- ProgressDialog.STYLE_HORIZONTAL, true);
-
- // start service with intent
- getActivity().startService(intent);
-
- dismiss();
+ mDeleteOpHelper = new CryptoOperationHelper<>
+ (DeleteKeyDialogFragment.this, DeleteKeyDialogFragment.this,
+ R.string.progress_deleting);
+ mDeleteOpHelper.cryptoOperation();
+ // do NOT dismiss here, it'll give
+ // OperationHelper a null fragmentManager
+ // dismiss();
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@@ -183,4 +170,42 @@ public class DeleteKeyDialogFragment extends DialogFragment {
return builder.show();
}
+ @Override
+ public DeleteKeyringParcel createOperationInput() {
+ return new DeleteKeyringParcel(mMasterKeyIds, mHasSecret);
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(DeleteResult result) {
+ handleResult(result);
+ }
+
+ @Override
+ public void onCryptoOperationCancelled() {
+
+ }
+
+ @Override
+ public void onCryptoOperationError(DeleteResult result) {
+ handleResult(result);
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
+
+ public void handleResult(DeleteResult result) {
+ try {
+ Bundle data = new Bundle();
+ data.putParcelable(OperationResult.EXTRA_RESULT, result);
+ Message msg = Message.obtain();
+ msg.arg1 = ServiceProgressHandler.MessageStatus.OKAY.ordinal();
+ msg.setData(data);
+ mMessenger.send(msg);
+ } catch (RemoteException e) {
+ Log.e(Constants.TAG, "messenger error", e);
+ }
+ dismiss();
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/DividerItemDecoration.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/DividerItemDecoration.java
new file mode 100644
index 000000000..95199bcd5
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/DividerItemDecoration.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.ui.util.recyclerview;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+public class DividerItemDecoration extends RecyclerView.ItemDecoration {
+
+ private static final int[] ATTRS = new int[]{
+ android.R.attr.listDivider
+ };
+
+ public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
+
+ public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
+
+ private Drawable mDivider;
+
+ private int mOrientation;
+
+ public DividerItemDecoration(Context context, int orientation) {
+ final TypedArray a = context.obtainStyledAttributes(ATTRS);
+ mDivider = a.getDrawable(0);
+ a.recycle();
+ setOrientation(orientation);
+ }
+
+ public void setOrientation(int orientation) {
+ if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
+ throw new IllegalArgumentException("invalid orientation");
+ }
+ mOrientation = orientation;
+ }
+
+ @Override
+ public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+ if (mOrientation == VERTICAL_LIST) {
+ drawVertical(c, parent);
+ } else {
+ drawHorizontal(c, parent);
+ }
+ }
+
+ public void drawVertical(Canvas c, RecyclerView parent) {
+ final int left = parent.getPaddingLeft();
+ final int right = parent.getWidth() - parent.getPaddingRight();
+
+ final int childCount = parent.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = parent.getChildAt(i);
+ final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
+ .getLayoutParams();
+ final int top = child.getBottom() + params.bottomMargin;
+ final int bottom = top + mDivider.getIntrinsicHeight();
+ mDivider.setBounds(left, top, right, bottom);
+ mDivider.draw(c);
+ }
+ }
+
+ public void drawHorizontal(Canvas c, RecyclerView parent) {
+ final int top = parent.getPaddingTop();
+ final int bottom = parent.getHeight() - parent.getPaddingBottom();
+
+ final int childCount = parent.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = parent.getChildAt(i);
+ final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
+ .getLayoutParams();
+ final int left = child.getRight() + params.rightMargin;
+ final int right = left + mDivider.getIntrinsicHeight();
+ mDivider.setBounds(left, top, right, bottom);
+ mDivider.draw(c);
+ }
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
+ RecyclerView.State state) {
+ if (mOrientation == VERTICAL_LIST) {
+ outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
+ } else {
+ outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
+ }
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperAdapter.java
new file mode 100644
index 000000000..c691182bf
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperAdapter.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 Paul Burke
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.ui.util.recyclerview;
+
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
+
+/**
+ * Interface to listen for a move or dismissal event from a {@link ItemTouchHelper.Callback}.
+ */
+public interface ItemTouchHelperAdapter {
+
+ /**
+ * Called when an item has been dragged far enough to trigger a move. This is called every time
+ * an item is shifted, and <strong>not</strong> at the end of a "drop" event.<br/>
+ * <br/>
+ * Implementations should call {@link RecyclerView.Adapter#notifyItemMoved(int, int)} after
+ * adjusting the underlying data to reflect this move.
+ *
+ * @param fromPosition The start position of the moved item.
+ * @param toPosition Then resolved position of the moved item.
+ * @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
+ * @see RecyclerView.ViewHolder#getAdapterPosition()
+ */
+ void onItemMove(RecyclerView.ViewHolder source, RecyclerView.ViewHolder target,
+ int fromPosition, int toPosition);
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperDragCallback.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperDragCallback.java
new file mode 100644
index 000000000..0fd24581d
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperDragCallback.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 Paul Burke
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.ui.util.recyclerview;
+
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
+
+/**
+ * An implementation of {@link ItemTouchHelper.Callback} that enables basic drag & drop and
+ * swipe-to-dismiss. Drag events are automatically started by an item long-press.<br/>
+ * </br/>
+ * Expects the <code>RecyclerView.Adapter</code> to listen for {@link
+ * ItemTouchHelperAdapter} callbacks and the <code>RecyclerView.ViewHolder</code> to implement
+ * {@link ItemTouchHelperViewHolder}.
+ */
+public class ItemTouchHelperDragCallback extends ItemTouchHelper.Callback {
+
+ private final ItemTouchHelperAdapter mAdapter;
+
+ public ItemTouchHelperDragCallback(ItemTouchHelperAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ @Override
+ public boolean isLongPressDragEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isItemViewSwipeEnabled() {
+ return false;
+ }
+
+ @Override
+ public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
+ // Enable drag and swipe in both directions
+ final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
+ final int swipeFlags = 0;
+ return makeMovementFlags(dragFlags, swipeFlags);
+ }
+
+ @Override
+ public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source,
+ RecyclerView.ViewHolder target) {
+ if (source.getItemViewType() != target.getItemViewType()) {
+ return false;
+ }
+
+ // Notify the adapter of the move
+ mAdapter.onItemMove(source, target, source.getAdapterPosition(), target.getAdapterPosition());
+ return true;
+ }
+
+ @Override
+ public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
+ // we don't support swipe
+ }
+
+ @Override
+ public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
+ if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
+ // Let the view holder know that this item is being moved or dragged
+ ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
+ itemViewHolder.onItemSelected();
+ }
+
+ super.onSelectedChanged(viewHolder, actionState);
+ }
+
+ @Override
+ public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
+ super.clearView(recyclerView, viewHolder);
+
+ // Tell the view holder it's time to restore the idle state
+ ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
+ itemViewHolder.onItemClear();
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperViewHolder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperViewHolder.java
new file mode 100644
index 000000000..97e70d71e
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperViewHolder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 Paul Burke
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.ui.util.recyclerview;
+
+import android.support.v7.widget.helper.ItemTouchHelper;
+
+/**
+ * Interface to notify an item ViewHolder of relevant callbacks from {@link
+ * android.support.v7.widget.helper.ItemTouchHelper.Callback}.
+ */
+public interface ItemTouchHelperViewHolder {
+
+ /**
+ * Called when the {@link ItemTouchHelper} first registers an item as being moved or swiped.
+ * Implementations should update the item view to indicate it's active state.
+ */
+ void onItemSelected();
+
+
+ /**
+ * Called when the {@link ItemTouchHelper} has completed the move or swipe, and the active item
+ * state should be cleared.
+ */
+ void onItemClear();
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/RecyclerItemClickListener.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/RecyclerItemClickListener.java
new file mode 100644
index 000000000..7efcbb30c
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/RecyclerItemClickListener.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 Jacob Tabak
+ *
+ * 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.util.recyclerview;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * based on http://stackoverflow.com/a/26196831/3000919
+ */
+public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
+ private OnItemClickListener mListener;
+ private boolean mIgnoreTouch = false;
+
+ public interface OnItemClickListener {
+ void onItemClick(View view, int position);
+ }
+
+ GestureDetector mGestureDetector;
+
+ public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
+ mListener = listener;
+ mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ return true;
+ }
+ });
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
+ if (mIgnoreTouch) {
+ return false;
+ }
+ View childView = view.findChildViewUnder(e.getX(), e.getY());
+ if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
+ mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
+ // TODO: should we move mListener.onItemClick here
+ }
+
+ @Override
+ public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ mIgnoreTouch = disallowIntercept;
+ }
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java
deleted file mode 100644
index ec91b9fe4..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
- *
- * 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.widget;
-
-public interface Editor {
- public interface EditorListener {
- public void onDeleted(Editor editor, boolean wasNewItem);
- public void onEdited();
- }
-
- public void setEditorListener(EditorListener listener);
- public boolean needsSaving();
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
deleted file mode 100644
index 3fd01958a..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
- *
- * 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.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import org.sufficientlysecure.keychain.R;
-
-public class KeyServerEditor extends LinearLayout implements Editor, OnClickListener {
- private EditorListener mEditorListener = null;
-
- ImageButton mDeleteButton;
- TextView mServer;
-
- public KeyServerEditor(Context context) {
- super(context);
- }
-
- public KeyServerEditor(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- setDrawingCacheEnabled(true);
- setAlwaysDrawnWithCacheEnabled(true);
-
- mServer = (TextView) findViewById(R.id.server);
-
- mDeleteButton = (ImageButton) findViewById(R.id.delete);
- mDeleteButton.setOnClickListener(this);
-
- super.onFinishInflate();
- }
-
- public void setValue(String value) {
- mServer.setText(value);
- }
-
- public String getValue() {
- return mServer.getText().toString().trim();
- }
-
- public void onClick(View v) {
- final ViewGroup parent = (ViewGroup) getParent();
- if (v == mDeleteButton) {
- parent.removeView(this);
- if (mEditorListener != null) {
- mEditorListener.onDeleted(this, false);
- }
- }
- }
-
- @Override
- public boolean needsSaving() {
- return false;
- }
-
- public void setEditorListener(EditorListener listener) {
- mEditorListener = listener;
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java
index bbc08a2aa..b814f72b2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java
@@ -18,15 +18,14 @@
package org.sufficientlysecure.keychain.util;
import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Messenger;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.service.KeychainService;
+import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import java.util.ArrayList;
import java.util.HashSet;
@@ -35,24 +34,35 @@ import java.util.Locale;
import java.util.Set;
public class EmailKeyHelper {
+ // to import keys, simply use CryptoOperationHelper with this callback
+ public abstract class ImportContactKeysCallback
+ implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
- public static void importContacts(Context context, Messenger messenger) {
- importAll(context, messenger, ContactHelper.getContactMails(context));
- }
+ private ArrayList<ParcelableKeyRing> mKeyList;
+ private String mKeyserver;
- public static void importAll(Context context, Messenger messenger, List<String> mails) {
- // Collect all candidates as ImportKeysListEntry (set for deduplication)
- Set<ImportKeysListEntry> entries = new HashSet<>();
- for (String mail : mails) {
- entries.addAll(getEmailKeys(context, mail));
+ public ImportContactKeysCallback(Context context, String keyserver) {
+ this(context, ContactHelper.getContactMails(context), keyserver);
}
- // Put them in a list and import
- ArrayList<ParcelableKeyRing> keys = new ArrayList<>(entries.size());
- for (ImportKeysListEntry entry : entries) {
- keys.add(new ParcelableKeyRing(entry.getFingerprintHex(), entry.getKeyIdHex(), null));
+ public ImportContactKeysCallback(Context context, List<String> mails, String keyserver) {
+ Set<ImportKeysListEntry> entries = new HashSet<>();
+ for (String mail : mails) {
+ entries.addAll(getEmailKeys(context, mail));
+ }
+
+ // Put them in a list and import
+ ArrayList<ParcelableKeyRing> keys = new ArrayList<>(entries.size());
+ for (ImportKeysListEntry entry : entries) {
+ keys.add(new ParcelableKeyRing(entry.getFingerprintHex(), entry.getKeyIdHex(), null));
+ }
+ mKeyList = keys;
+ mKeyserver = keyserver;
+ }
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return new ImportKeyringParcel(mKeyList, mKeyserver);
}
- importKeys(context, messenger, keys);
}
public static Set<ImportKeysListEntry> getEmailKeys(Context context, String mail) {
@@ -78,17 +88,6 @@ public class EmailKeyHelper {
return keys;
}
- private static void importKeys(Context context, Messenger messenger, ArrayList<ParcelableKeyRing> keys) {
- Intent importIntent = new Intent(context, KeychainService.class);
- importIntent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
- Bundle importData = new Bundle();
- importData.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, keys);
- importIntent.putExtra(KeychainService.EXTRA_DATA, importData);
- importIntent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
-
- context.startService(importIntent);
- }
-
public static List<ImportKeysListEntry> getEmailKeys(String mail, Keyserver keyServer) {
Set<ImportKeysListEntry> keys = new HashSet<>();
try {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java
index 2fd09dc79..9567fc9c0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java
@@ -17,26 +17,26 @@
package org.sufficientlysecure.keychain.util;
-import android.app.ProgressDialog;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.support.v4.app.FragmentActivity;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
-import org.sufficientlysecure.keychain.service.KeychainService;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import java.io.File;
-public class ExportHelper {
+public class ExportHelper
+ implements CryptoOperationHelper.Callback <ExportKeyringParcel, ExportResult> {
protected File mExportFile;
FragmentActivity mActivity;
+ private CryptoOperationHelper<ExportKeyringParcel, ExportResult> mExportOpHelper;
+ private boolean mExportSecret;
+ private long[] mMasterKeyIds;
+
public ExportHelper(FragmentActivity activity) {
super();
this.mActivity = activity;
@@ -71,60 +71,43 @@ public class ExportHelper {
}, mActivity.getSupportFragmentManager() ,title, message, exportFile, checkMsg);
}
+ // TODO: If ExportHelper requires pending data (see CryptoOPerationHelper), activities using
+ // TODO: this class should be able to call mExportOpHelper.handleActivity
+
/**
* Export keys
*/
public void exportKeys(long[] masterKeyIds, boolean exportSecret) {
Log.d(Constants.TAG, "exportKeys started");
+ mExportSecret = exportSecret;
+ mMasterKeyIds = masterKeyIds; // if masterKeyIds is null it means export all
- // Send all information needed to service to export key in other thread
- final Intent intent = new Intent(mActivity, KeychainService.class);
-
- intent.setAction(KeychainService.ACTION_EXPORT_KEYRING);
-
- // fill values for this action
- Bundle data = new Bundle();
-
- data.putString(KeychainService.EXPORT_FILENAME, mExportFile.getAbsolutePath());
- data.putBoolean(KeychainService.EXPORT_SECRET, exportSecret);
-
- if (masterKeyIds == null) {
- data.putBoolean(KeychainService.EXPORT_ALL, true);
- } else {
- data.putLongArray(KeychainService.EXPORT_KEY_RING_MASTER_KEY_ID, masterKeyIds);
- }
-
- intent.putExtra(KeychainService.EXTRA_DATA, data);
-
- // Message is received after exporting is done in KeychainService
- ServiceProgressHandler exportHandler = new ServiceProgressHandler(mActivity) {
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
+ mExportOpHelper = new CryptoOperationHelper(mActivity, this, R.string.progress_exporting);
+ mExportOpHelper.cryptoOperation();
+ }
- if (message.arg1 == MessageStatus.OKAY.ordinal()) {
- // get returned data bundle
- Bundle data = message.getData();
+ @Override
+ public ExportKeyringParcel createOperationInput() {
+ return new ExportKeyringParcel(mMasterKeyIds, mExportSecret, mExportFile.getAbsolutePath());
+ }
- ExportResult result = data.getParcelable(ExportResult.EXTRA_RESULT);
- result.createNotify(mActivity).show();
- }
- }
- };
+ @Override
+ public void onCryptoOperationSuccess(ExportResult result) {
+ result.createNotify(mActivity).show();
+ }
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(exportHandler);
- intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
+ @Override
+ public void onCryptoOperationCancelled() {
- // show progress dialog
- exportHandler.showProgressDialog(
- mActivity.getString(R.string.progress_exporting),
- ProgressDialog.STYLE_HORIZONTAL, false
- );
+ }
- // start service with intent
- mActivity.startService(intent);
+ @Override
+ public void onCryptoOperationError(ExportResult result) {
+ result.createNotify(mActivity).show();
}
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
index a5b0088c0..713d5f5ea 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
@@ -138,6 +138,9 @@ public class Preferences {
public String[] getKeyServers() {
String rawData = mSharedPreferences.getString(Constants.Pref.KEY_SERVERS,
Constants.Defaults.KEY_SERVERS);
+ if (rawData.equals("")) {
+ return new String[0];
+ }
Vector<String> servers = new Vector<>();
String chunks[] = rawData.split(",");
for (String c : chunks) {
@@ -150,7 +153,8 @@ public class Preferences {
}
public String getPreferredKeyserver() {
- return getKeyServers()[0];
+ String[] keyservers = getKeyServers();
+ return keyservers.length == 0 ? null : keyservers[0];
}
public void setKeyServers(String[] value) {