mirror of
https://github.com/moparisthebest/open-keychain
synced 2025-02-23 06:12:20 -05:00
Merge branch 'development' into linked-identities
Conflicts: OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
This commit is contained in:
commit
f9ef1160ca
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -34,3 +34,6 @@
|
||||
path = extern/safeslinger-exchange
|
||||
url = https://github.com/open-keychain/exchange-android
|
||||
ignore = dirty
|
||||
[submodule "extern/snackbar"]
|
||||
path = extern/snackbar
|
||||
url = https://github.com/open-keychain/snackbar
|
||||
|
@ -47,6 +47,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
|
||||
import org.sufficientlysecure.keychain.util.InputData;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
import org.sufficientlysecure.keychain.util.TestingUtils;
|
||||
|
||||
@ -65,8 +66,8 @@ import java.util.Random;
|
||||
public class CertifyOperationTest {
|
||||
|
||||
static UncachedKeyRing mStaticRing1, mStaticRing2;
|
||||
static String mKeyPhrase1 = TestingUtils.genPassphrase(true);
|
||||
static String mKeyPhrase2 = TestingUtils.genPassphrase(true);
|
||||
static Passphrase mKeyPhrase1 = TestingUtils.genPassphrase(true);
|
||||
static Passphrase mKeyPhrase2 = TestingUtils.genPassphrase(true);
|
||||
|
||||
static PrintStream oldShadowStream;
|
||||
|
||||
@ -255,13 +256,13 @@ public class CertifyOperationTest {
|
||||
}
|
||||
|
||||
private CertifyOperation operationWithFakePassphraseCache(
|
||||
final Long checkMasterKeyId, final Long checkSubKeyId, final String passphrase) {
|
||||
final Long checkMasterKeyId, final Long checkSubKeyId, final Passphrase passphrase) {
|
||||
|
||||
return new CertifyOperation(Robolectric.application,
|
||||
new ProviderHelper(Robolectric.application),
|
||||
null, null) {
|
||||
@Override
|
||||
public String getCachedPassphrase(long masterKeyId, long subKeyId)
|
||||
public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId)
|
||||
throws NoSecretKeyException {
|
||||
if (checkMasterKeyId != null) {
|
||||
Assert.assertEquals("requested passphrase should be for expected master key id",
|
||||
|
@ -38,6 +38,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
import org.sufficientlysecure.keychain.util.TestingUtils;
|
||||
|
||||
@ -51,11 +52,11 @@ import java.util.Iterator;
|
||||
@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
|
||||
public class ExportTest {
|
||||
|
||||
static String mPassphrase = TestingUtils.genPassphrase(true);
|
||||
static Passphrase mPassphrase = TestingUtils.genPassphrase(true);
|
||||
|
||||
static UncachedKeyRing mStaticRing1, mStaticRing2;
|
||||
static String mKeyPhrase1 = TestingUtils.genPassphrase(true);
|
||||
static String mKeyPhrase2 = TestingUtils.genPassphrase(true);
|
||||
static Passphrase mKeyPhrase1 = TestingUtils.genPassphrase(true);
|
||||
static Passphrase mKeyPhrase2 = TestingUtils.genPassphrase(true);
|
||||
|
||||
static PrintStream oldShadowStream;
|
||||
|
||||
@ -94,7 +95,7 @@ public class ExportTest {
|
||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||
Algorithm.ELGAMAL, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
|
||||
parcel.mAddUserIds.add("snails");
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel(null, "1234");
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel(null, new Passphrase("1234"));
|
||||
|
||||
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
|
||||
Assert.assertTrue("initial test key creation must succeed", result.success());
|
||||
|
@ -38,6 +38,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
import org.sufficientlysecure.keychain.util.TestingUtils;
|
||||
|
||||
@ -50,7 +51,7 @@ import java.util.Iterator;
|
||||
public class PromoteKeyOperationTest {
|
||||
|
||||
static UncachedKeyRing mStaticRing;
|
||||
static String mKeyPhrase1 = TestingUtils.genPassphrase(true);
|
||||
static Passphrase mKeyPhrase1 = TestingUtils.genPassphrase(true);
|
||||
|
||||
static PrintStream oldShadowStream;
|
||||
|
||||
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.pgp;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
|
||||
public class KeyRingTest {
|
||||
|
||||
@Test
|
||||
public void splitCompleteUserIdShouldReturnAll3Components() throws Exception {
|
||||
KeyRing.UserId info = KeyRing.splitUserId("Max Mustermann (this is a comment) <max@example.com>");
|
||||
Assert.assertEquals("Max Mustermann", info.name);
|
||||
Assert.assertEquals("this is a comment", info.comment);
|
||||
Assert.assertEquals("max@example.com", info.email);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void splitUserIdWithAllButCommentShouldReturnNameAndEmail() throws Exception {
|
||||
KeyRing.UserId info = KeyRing.splitUserId("Max Mustermann <max@example.com>");
|
||||
Assert.assertEquals("Max Mustermann", info.name);
|
||||
Assert.assertNull(info.comment);
|
||||
Assert.assertEquals("max@example.com", info.email);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void splitUserIdWithAllButEmailShouldReturnNameAndComment() throws Exception {
|
||||
KeyRing.UserId info = KeyRing.splitUserId("Max Mustermann (this is a comment)");
|
||||
Assert.assertEquals(info.name, "Max Mustermann");
|
||||
Assert.assertEquals(info.comment, "this is a comment");
|
||||
Assert.assertNull(info.email);
|
||||
}
|
||||
|
||||
}
|
@ -39,6 +39,7 @@ import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
|
||||
import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
|
||||
import org.sufficientlysecure.keychain.util.InputData;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
import org.sufficientlysecure.keychain.util.TestingUtils;
|
||||
|
||||
@ -47,17 +48,18 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.security.Security;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
|
||||
public class PgpEncryptDecryptTest {
|
||||
|
||||
static String mPassphrase = TestingUtils.genPassphrase(true);
|
||||
static Passphrase mPassphrase = TestingUtils.genPassphrase(true);
|
||||
|
||||
static UncachedKeyRing mStaticRing1, mStaticRing2;
|
||||
static String mKeyPhrase1 = TestingUtils.genPassphrase(true);
|
||||
static String mKeyPhrase2 = TestingUtils.genPassphrase(true);
|
||||
static Passphrase mKeyPhrase1 = TestingUtils.genPassphrase(true);
|
||||
static Passphrase mKeyPhrase2 = TestingUtils.genPassphrase(true);
|
||||
|
||||
static PrintStream oldShadowStream;
|
||||
|
||||
@ -180,7 +182,7 @@ public class PgpEncryptDecryptTest {
|
||||
new ProviderHelper(Robolectric.application),
|
||||
null, // new DummyPassphraseCache(mPassphrase, 0L),
|
||||
data, out);
|
||||
b.setPassphrase(mPassphrase + "x");
|
||||
b.setPassphrase(new Passphrase(Arrays.toString(mPassphrase.getCharArray()) + "x"));
|
||||
DecryptVerifyResult result = b.build().execute();
|
||||
Assert.assertFalse("decryption must succeed", result.success());
|
||||
Assert.assertEquals("decrypted plaintext should be empty", 0, out.size());
|
||||
@ -511,7 +513,7 @@ public class PgpEncryptDecryptTest {
|
||||
|
||||
private PgpDecryptVerify.Builder builderWithFakePassphraseCache (
|
||||
InputData data, OutputStream out,
|
||||
final String passphrase, final Long checkMasterKeyId, final Long checkSubKeyId) {
|
||||
final Passphrase passphrase, final Long checkMasterKeyId, final Long checkSubKeyId) {
|
||||
|
||||
return new PgpDecryptVerify.Builder(Robolectric.application,
|
||||
new ProviderHelper(Robolectric.application),
|
||||
@ -520,7 +522,7 @@ public class PgpEncryptDecryptTest {
|
||||
public PgpDecryptVerify build() {
|
||||
return new PgpDecryptVerify(this) {
|
||||
@Override
|
||||
public String getCachedPassphrase(long masterKeyId, long subKeyId)
|
||||
public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId)
|
||||
throws NoSecretKeyException {
|
||||
if (checkMasterKeyId != null) {
|
||||
Assert.assertEquals("requested passphrase should be for expected master key id",
|
||||
|
@ -53,6 +53,7 @@ import org.sufficientlysecure.keychain.support.KeyringBuilder;
|
||||
import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
|
||||
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
|
||||
import org.sufficientlysecure.keychain.support.TestDataUtil;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
import org.sufficientlysecure.keychain.util.TestingUtils;
|
||||
|
||||
@ -72,7 +73,7 @@ import java.util.Random;
|
||||
public class PgpKeyOperationTest {
|
||||
|
||||
static UncachedKeyRing staticRing;
|
||||
final static String passphrase = TestingUtils.genPassphrase();
|
||||
final static Passphrase passphrase = TestingUtils.genPassphrase();
|
||||
|
||||
UncachedKeyRing ring;
|
||||
PgpKeyOperation op;
|
||||
@ -295,9 +296,9 @@ public class PgpKeyOperationTest {
|
||||
}
|
||||
|
||||
{
|
||||
String badphrase = "";
|
||||
Passphrase badphrase = new Passphrase();
|
||||
if (badphrase.equals(passphrase)) {
|
||||
badphrase = "a";
|
||||
badphrase = new Passphrase("a");
|
||||
}
|
||||
|
||||
assertModifyFailure("keyring modification with bad passphrase should fail",
|
||||
@ -1036,7 +1037,7 @@ public class PgpKeyOperationTest {
|
||||
public void testPassphraseChange() throws Exception {
|
||||
|
||||
// change passphrase to empty
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel("");
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
|
||||
// note that canonicalization here necessarily strips the empty notation packet
|
||||
UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB,
|
||||
passphrase);
|
||||
@ -1050,9 +1051,9 @@ public class PgpKeyOperationTest {
|
||||
PacketTags.SECRET_SUBKEY, sKeyNoPassphrase.tag);
|
||||
|
||||
// modify keyring, change to non-empty passphrase
|
||||
String otherPassphrase = TestingUtils.genPassphrase(true);
|
||||
Passphrase otherPassphrase = TestingUtils.genPassphrase(true);
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel(otherPassphrase);
|
||||
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB, "");
|
||||
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB, new Passphrase());
|
||||
|
||||
Assert.assertEquals("exactly three packets should have been modified (the secret keys)",
|
||||
3, onlyB.size());
|
||||
@ -1075,7 +1076,7 @@ public class PgpKeyOperationTest {
|
||||
Assert.assertEquals("extracted packet should be a secret subkey",
|
||||
PacketTags.SECRET_SUBKEY, sKeyNoPassphrase.tag);
|
||||
|
||||
String otherPassphrase2 = TestingUtils.genPassphrase(true);
|
||||
Passphrase otherPassphrase2 = TestingUtils.genPassphrase(true);
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel(otherPassphrase2);
|
||||
{
|
||||
// if we replace a secret key with one without passphrase
|
||||
@ -1112,7 +1113,7 @@ public class PgpKeyOperationTest {
|
||||
@Test
|
||||
public void testUnlockPin() throws Exception {
|
||||
|
||||
String pin = "5235125";
|
||||
Passphrase pin = new Passphrase("5235125");
|
||||
|
||||
// change passphrase to a pin type
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel(null, pin);
|
||||
@ -1138,7 +1139,7 @@ public class PgpKeyOperationTest {
|
||||
Thread.sleep(1000);
|
||||
|
||||
{
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel("phrayse", null);
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase("phrayse"), null);
|
||||
applyModificationWithChecks(parcel, modified, onlyA, onlyB, pin, true, false);
|
||||
|
||||
Assert.assertEquals("exactly four packets should have been removed (the secret keys + notation packet)",
|
||||
@ -1171,7 +1172,7 @@ public class PgpKeyOperationTest {
|
||||
UncachedKeyRing ring,
|
||||
ArrayList<RawPacket> onlyA,
|
||||
ArrayList<RawPacket> onlyB,
|
||||
String passphrase) {
|
||||
Passphrase passphrase) {
|
||||
return applyModificationWithChecks(parcel, ring, onlyA, onlyB, passphrase, true, true);
|
||||
}
|
||||
|
||||
@ -1180,7 +1181,7 @@ public class PgpKeyOperationTest {
|
||||
UncachedKeyRing ring,
|
||||
ArrayList<RawPacket> onlyA,
|
||||
ArrayList<RawPacket> onlyB,
|
||||
String passphrase,
|
||||
Passphrase passphrase,
|
||||
boolean canonicalize,
|
||||
boolean constantCanonicalize) {
|
||||
|
||||
@ -1257,7 +1258,7 @@ public class PgpKeyOperationTest {
|
||||
}
|
||||
|
||||
private void assertModifyFailure(String reason, UncachedKeyRing ring,
|
||||
SaveKeyringParcel parcel, String passphrase, LogType expected)
|
||||
SaveKeyringParcel parcel, Passphrase passphrase, LogType expected)
|
||||
throws Exception {
|
||||
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
|
||||
|
@ -61,6 +61,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
|
||||
import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
|
||||
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.security.Security;
|
||||
@ -111,7 +112,7 @@ public class UncachedKeyringCanonicalizeTest {
|
||||
}
|
||||
|
||||
// passphrase is tested in PgpKeyOperationTest, just use empty here
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel("");
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
|
||||
PgpKeyOperation op = new PgpKeyOperation(null);
|
||||
|
||||
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
|
||||
@ -546,7 +547,7 @@ public class UncachedKeyringCanonicalizeTest {
|
||||
CanonicalizedSecretKeyRing canonicalized = (CanonicalizedSecretKeyRing) ring.canonicalize(log, 0);
|
||||
|
||||
CanonicalizedSecretKey masterSecretKey = canonicalized.getSecretKey();
|
||||
masterSecretKey.unlock("");
|
||||
masterSecretKey.unlock(new Passphrase());
|
||||
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
|
||||
PGPSignature cert = PgpKeyOperation.generateSubkeyBindingSignature(
|
||||
masterPublicKey, masterSecretKey.getPrivateKey(), masterSecretKey.getPrivateKey(),
|
||||
|
@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
|
||||
import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
|
||||
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@ -105,7 +106,7 @@ public class UncachedKeyringMergeTest {
|
||||
}
|
||||
|
||||
// passphrase is tested in PgpKeyOperationTest, just use empty here
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel("");
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
|
||||
PgpKeyOperation op = new PgpKeyOperation(null);
|
||||
|
||||
OperationResult.OperationLog log = new OperationResult.OperationLog();
|
||||
@ -122,7 +123,7 @@ public class UncachedKeyringMergeTest {
|
||||
|
||||
parcel.mAddUserIds.add("shy");
|
||||
// passphrase is tested in PgpKeyOperationTest, just use empty here
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel("");
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
|
||||
PgpKeyOperation op = new PgpKeyOperation(null);
|
||||
|
||||
OperationResult.OperationLog log = new OperationResult.OperationLog();
|
||||
@ -185,11 +186,11 @@ public class UncachedKeyringMergeTest {
|
||||
|
||||
parcel.reset();
|
||||
parcel.mAddUserIds.add("flim");
|
||||
modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
|
||||
modifiedA = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing();
|
||||
|
||||
parcel.reset();
|
||||
parcel.mAddUserIds.add("flam");
|
||||
modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
|
||||
modifiedB = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing();
|
||||
}
|
||||
|
||||
{ // merge A into base
|
||||
@ -226,8 +227,8 @@ public class UncachedKeyringMergeTest {
|
||||
parcel.reset();
|
||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||
Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
|
||||
modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
|
||||
modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
|
||||
modifiedA = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing();
|
||||
modifiedB = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing();
|
||||
|
||||
subKeyIdA = KeyringTestingHelper.getSubkeyId(modifiedA, 2);
|
||||
subKeyIdB = KeyringTestingHelper.getSubkeyId(modifiedB, 2);
|
||||
@ -268,7 +269,7 @@ public class UncachedKeyringMergeTest {
|
||||
parcel.mRevokeSubKeys.add(KeyringTestingHelper.getSubkeyId(ringA, 1));
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(
|
||||
ringA.getEncoded(), false, 0);
|
||||
modified = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
|
||||
modified = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing();
|
||||
}
|
||||
|
||||
{
|
||||
@ -293,7 +294,7 @@ public class UncachedKeyringMergeTest {
|
||||
|
||||
CanonicalizedSecretKey secretKey = new CanonicalizedSecretKeyRing(
|
||||
ringB.getEncoded(), false, 0).getSecretKey();
|
||||
secretKey.unlock("");
|
||||
secretKey.unlock(new Passphrase());
|
||||
// sign all user ids
|
||||
modified = secretKey.certifyUserIds(publicRing, publicRing.getPublicKey().getUnorderedUserIds(), null, null);
|
||||
}
|
||||
@ -362,7 +363,7 @@ public class UncachedKeyringMergeTest {
|
||||
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(
|
||||
ringA.getEncoded(), false, 0);
|
||||
modified = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
|
||||
modified = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing();
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -32,6 +32,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@ -70,7 +71,7 @@ public class UncachedKeyringTest {
|
||||
parcel.mAddUserAttribute.add(uat);
|
||||
}
|
||||
// passphrase is tested in PgpKeyOperationTest, just use empty here
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel("");
|
||||
parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
|
||||
PgpKeyOperation op = new PgpKeyOperation(null);
|
||||
|
||||
PgpEditKeyResult result = op.createSecretKeyRing(parcel);
|
||||
|
@ -1,13 +1,30 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class TestingUtils {
|
||||
public static String genPassphrase() {
|
||||
public static Passphrase genPassphrase() {
|
||||
return genPassphrase(false);
|
||||
}
|
||||
|
||||
public static String genPassphrase(boolean noEmpty) {
|
||||
public static Passphrase genPassphrase(boolean noEmpty) {
|
||||
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789!@#$%^&*()-_=";
|
||||
Random r = new Random();
|
||||
StringBuilder passbuilder = new StringBuilder();
|
||||
@ -16,6 +33,6 @@ public class TestingUtils {
|
||||
passbuilder.append(chars.charAt(r.nextInt(chars.length())));
|
||||
}
|
||||
System.out.println("Generated passphrase: '" + passbuilder.toString() + "'");
|
||||
return passbuilder.toString();
|
||||
return new Passphrase(passbuilder.toString());
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ dependencies {
|
||||
compile 'com.google.zxing:core:3.0.1'
|
||||
compile 'com.jpardogo.materialtabstrip:library:1.0.9'
|
||||
compile 'it.neokree:MaterialNavigationDrawer:1.3.1'
|
||||
compile 'com.nispok:snackbar:2.9.1'
|
||||
compile 'com.getbase:floatingactionbutton:1.8.0'
|
||||
compile 'com.ocpsoft:ocpsoft-pretty-time:1.0.6'
|
||||
|
||||
@ -35,6 +34,7 @@ dependencies {
|
||||
compile project(':extern:KeybaseLib:Lib')
|
||||
compile project(':extern:TokenAutoComplete:library')
|
||||
compile project(':extern:safeslinger-exchange')
|
||||
compile project(':extern:snackbar:lib')
|
||||
}
|
||||
|
||||
// Output of ./gradlew -q calculateChecksums
|
||||
@ -51,7 +51,7 @@ dependencyVerification {
|
||||
'com.google.zxing:core:38c49045765281e4c170062fa3f48e4e988629bf985cab850c7497be5eaa72a1',
|
||||
'com.jpardogo.materialtabstrip:library:c6ef812fba4f74be7dc4a905faa4c2908cba261a94c13d4f96d5e67e4aad4aaa',
|
||||
'it.neokree:MaterialNavigationDrawer:1174d751a54689fccf53c1fbcdf439745926ae19024f4f1017afb6b29643c57d',
|
||||
'com.nispok:snackbar:59dc092a44c877e9ce5f9040c632d99e62d8932b0a4d67ba0ec9e35467d9047c',
|
||||
// 'com.nispok:snackbar:59dc092a44c877e9ce5f9040c632d99e62d8932b0a4d67ba0ec9e35467d9047c',
|
||||
'com.getbase:floatingactionbutton:e63966148212e9685afad2370780ea239b6dbd2a06f6a3f919b98882318e6a32',
|
||||
'com.android.support:support-annotations:fdee2354787ef66b268e75958de3f7f6c4f8f325510a6dac9f49c929f83a63de',
|
||||
'com.balysv:material-ripple:587f19c1e27f16c7dc67ff9ac73838aa1451086ef05a15cee38bee3e4e1454ae',
|
||||
|
@ -86,16 +86,10 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.FirstTimeActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
android:label="@string/app_name"
|
||||
android:windowSoftInputMode="stateAlwaysHidden" />
|
||||
<activity
|
||||
android:name=".ui.CreateKeyActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:label="@string/title_create_key"
|
||||
android:label="@string/title_manage_my_keys"
|
||||
android:parentActivityName=".ui.MainActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
@ -418,7 +412,7 @@
|
||||
<activity
|
||||
android:name=".ui.ViewKeyAdvActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
android:label="@string/title_advanced_key_info"/>
|
||||
android:label="@string/title_advanced_key_info" />
|
||||
<activity
|
||||
android:name=".ui.SettingsActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
@ -660,6 +654,10 @@
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".ui.MainActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.LogDisplayActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
android:label="@string/title_log_display" />
|
||||
<activity
|
||||
android:name=".ui.ConsolidateDialogActivity"
|
||||
android:theme="@android:style/Theme.NoDisplay" />
|
||||
@ -714,14 +712,12 @@
|
||||
android:name=".remote.ui.RemoteServiceActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTop"
|
||||
android:process=":remote_api" />
|
||||
android:launchMode="singleTop" />
|
||||
<activity
|
||||
android:name=".remote.ui.SelectSignKeyIdActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTop"
|
||||
android:process=":remote_api" />
|
||||
android:launchMode="singleTop" />
|
||||
<activity
|
||||
android:name=".remote.ui.AppSettingsActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
@ -749,13 +745,10 @@
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<activity
|
||||
android:name=".ui.LogDisplayActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||
android:label="@string/title_log_display"
|
||||
android:exported="false" />
|
||||
|
||||
<service android:name=".service.DummyAccountService">
|
||||
<!-- Contact Sync services -->
|
||||
<service
|
||||
android:name=".service.DummyAccountService"
|
||||
tools:ignore="ExportedService">
|
||||
<intent-filter>
|
||||
<action android:name="android.accounts.AccountAuthenticator" />
|
||||
</intent-filter>
|
||||
@ -767,7 +760,8 @@
|
||||
<service
|
||||
android:name=".service.ContactSyncAdapterService"
|
||||
android:exported="true"
|
||||
android:process=":sync">
|
||||
android:process=":sync"
|
||||
tools:ignore="ExportedService">
|
||||
<intent-filter>
|
||||
<action android:name="android.content.SyncAdapter" />
|
||||
</intent-filter>
|
||||
@ -780,6 +774,7 @@
|
||||
android:resource="@xml/custom_pgp_contacts_structure" />
|
||||
</service>
|
||||
|
||||
<!-- Storage Provider for temporary decrypted files -->
|
||||
<provider
|
||||
android:name=".provider.TemporaryStorageProvider"
|
||||
android:authorities="org.sufficientlysecure.keychain.tempstorage"
|
||||
|
@ -42,6 +42,15 @@ public final class Constants {
|
||||
// as defined in http://tools.ietf.org/html/rfc3156, section 7
|
||||
public static final String NFC_MIME = "application/pgp-keys";
|
||||
|
||||
// as defined in http://tools.ietf.org/html/rfc3156
|
||||
// we don't use application/pgp-encrypted as it only holds the version number
|
||||
public static final String ENCRYPTED_FILES_MIME = "application/octet-stream";
|
||||
public static final String ENCRYPTED_TEXT_MIME = "text/plain";
|
||||
|
||||
public static final String FILE_EXTENSION_PGP_MAIN = ".gpg";
|
||||
public static final String FILE_EXTENSION_PGP_ALTERNATE = ".pgp";
|
||||
public static final String FILE_EXTENSION_ASC = ".asc";
|
||||
|
||||
// used by QR Codes (Guardian Project, Monkeysphere compatiblity)
|
||||
public static final String FINGERPRINT_SCHEME = "openpgp4fpr";
|
||||
|
||||
|
@ -118,24 +118,21 @@ public class KeychainApplication extends Application {
|
||||
* @param context
|
||||
*/
|
||||
public static void setupAccountAsNeeded(Context context) {
|
||||
// only enabled for Jelly Bean because we need some newer methods in our sync adapter
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
try {
|
||||
AccountManager manager = AccountManager.get(context);
|
||||
Account[] accounts = manager.getAccountsByType(Constants.ACCOUNT_TYPE);
|
||||
if (accounts == null || accounts.length == 0) {
|
||||
Account account = new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE);
|
||||
if (manager.addAccountExplicitly(account, null, null)) {
|
||||
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
|
||||
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
|
||||
} else {
|
||||
Log.e(Constants.TAG, "Adding account failed!");
|
||||
}
|
||||
try {
|
||||
AccountManager manager = AccountManager.get(context);
|
||||
Account[] accounts = manager.getAccountsByType(Constants.ACCOUNT_TYPE);
|
||||
if (accounts == null || accounts.length == 0) {
|
||||
Account account = new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE);
|
||||
if (manager.addAccountExplicitly(account, null, null)) {
|
||||
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
|
||||
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
|
||||
} else {
|
||||
Log.e(Constants.TAG, "Adding account failed!");
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
Log.e(Constants.TAG, "SecurityException when adding the account", e);
|
||||
Toast.makeText(context, R.string.reinstall_openkeychain, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
Log.e(Constants.TAG, "SecurityException when adding the account", e);
|
||||
Toast.makeText(context, R.string.reinstall_openkeychain, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,8 @@ public class ImportKeysList extends ArrayList<ImportKeysListEntry> {
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// keep track if this key result is from a HKP keyserver
|
||||
boolean incomingFromHkpServer = true;
|
||||
// we’re going to want to try to fetch the key from everywhere we found it, so remember
|
||||
// all the origins
|
||||
for (String origin : incoming.getOrigins()) {
|
||||
@ -78,13 +80,24 @@ public class ImportKeysList extends ArrayList<ImportKeysListEntry> {
|
||||
// to work properly, Keybase-sourced entries need to pass along the extra
|
||||
if (KeybaseKeyserver.ORIGIN.equals(origin)) {
|
||||
existing.setExtraData(incoming.getExtraData());
|
||||
// one of the origins is not a HKP keyserver
|
||||
incomingFromHkpServer = false;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<String> incomingIDs = incoming.getUserIds();
|
||||
ArrayList<String> existingIDs = existing.getUserIds();
|
||||
for (String incomingID : incomingIDs) {
|
||||
if (!existingIDs.contains(incomingID)) {
|
||||
existingIDs.add(incomingID);
|
||||
// prepend HKP server results to the start of the list,
|
||||
// so that the UI (for cloud key search, which is picking the first list item)
|
||||
// shows the right main email address, as mail addresses returned by HKP servers
|
||||
// are preferred over keybase.io IDs
|
||||
if (incomingFromHkpServer) {
|
||||
existingIDs.add(0, incomingID);
|
||||
} else {
|
||||
existingIDs.add(incomingID);
|
||||
}
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
@ -307,24 +307,22 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
||||
public void updateMergedUserIds() {
|
||||
mMergedUserIds = new HashMap<>();
|
||||
for (String userId : mUserIds) {
|
||||
String[] userIdSplit = KeyRing.splitUserId(userId);
|
||||
KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId);
|
||||
|
||||
// TODO: comment field?
|
||||
|
||||
// name
|
||||
if (userIdSplit[0] != null) {
|
||||
// email
|
||||
if (userIdSplit[1] != null) {
|
||||
if (!mMergedUserIds.containsKey(userIdSplit[0])) {
|
||||
if (userIdSplit.name != null) {
|
||||
if (userIdSplit.email != null) {
|
||||
if (!mMergedUserIds.containsKey(userIdSplit.name)) {
|
||||
HashSet<String> emails = new HashSet<>();
|
||||
emails.add(userIdSplit[1]);
|
||||
mMergedUserIds.put(userIdSplit[0], emails);
|
||||
emails.add(userIdSplit.email);
|
||||
mMergedUserIds.put(userIdSplit.name, emails);
|
||||
} else {
|
||||
mMergedUserIds.get(userIdSplit[0]).add(userIdSplit[1]);
|
||||
mMergedUserIds.get(userIdSplit.name).add(userIdSplit.email);
|
||||
}
|
||||
} else {
|
||||
// name only
|
||||
mMergedUserIds.put(userIdSplit[0], new HashSet<String>());
|
||||
mMergedUserIds.put(userIdSplit.name, new HashSet<String>());
|
||||
}
|
||||
} else {
|
||||
// fallback
|
||||
|
@ -24,6 +24,7 @@ import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
|
||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@ -101,7 +102,7 @@ public abstract class BaseOperation implements PassphraseCacheInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCachedPassphrase(long subKeyId) throws NoSecretKeyException {
|
||||
public Passphrase getCachedPassphrase(long subKeyId) throws NoSecretKeyException {
|
||||
try {
|
||||
long masterKeyId = mProviderHelper.getMasterKeyId(subKeyId);
|
||||
return getCachedPassphrase(masterKeyId, subKeyId);
|
||||
@ -111,7 +112,7 @@ public abstract class BaseOperation implements PassphraseCacheInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException {
|
||||
public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException {
|
||||
try {
|
||||
return PassphraseCacheService.getCachedPassphrase(
|
||||
mContext, masterKeyId, subKeyId);
|
||||
|
@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyActio
|
||||
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@ -79,7 +80,7 @@ public class CertifyOperation extends BaseOperation {
|
||||
}
|
||||
|
||||
// certification is always with the master key id, so use that one
|
||||
String passphrase = getCachedPassphrase(parcel.mMasterKeyId, parcel.mMasterKeyId);
|
||||
Passphrase passphrase = getCachedPassphrase(parcel.mMasterKeyId, parcel.mMasterKeyId);
|
||||
|
||||
if (!certificationKey.unlock(passphrase)) {
|
||||
log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2);
|
||||
|
@ -35,6 +35,7 @@ import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
|
||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@ -55,7 +56,7 @@ public class EditKeyOperation extends BaseOperation {
|
||||
super(context, providerHelper, progressable, cancelled);
|
||||
}
|
||||
|
||||
public EditKeyResult execute(SaveKeyringParcel saveParcel, String passphrase) {
|
||||
public EditKeyResult execute(SaveKeyringParcel saveParcel, Passphrase passphrase) {
|
||||
|
||||
OperationLog log = new OperationLog();
|
||||
log.add(LogType.MSG_ED, 0);
|
||||
|
@ -120,7 +120,7 @@ public class CertifyResult extends OperationResult {
|
||||
mCertifyError, mCertifyError);
|
||||
}
|
||||
|
||||
return Notify.createNotify(activity, str, duration, style, new ActionListener() {
|
||||
return Notify.create(activity, str, duration, style, new ActionListener() {
|
||||
@Override
|
||||
public void onAction() {
|
||||
Intent intent = new Intent(
|
||||
|
@ -22,6 +22,7 @@ import android.os.Parcel;
|
||||
|
||||
import org.openintents.openpgp.OpenPgpMetadata;
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
public class DecryptVerifyResult extends OperationResult {
|
||||
|
||||
@ -37,7 +38,7 @@ public class DecryptVerifyResult extends OperationResult {
|
||||
|
||||
long mNfcSubKeyId;
|
||||
byte[] mNfcSessionKey;
|
||||
String mNfcPassphrase;
|
||||
Passphrase mNfcPassphrase;
|
||||
|
||||
OpenPgpSignatureResult mSignatureResult;
|
||||
OpenPgpMetadata mDecryptMetadata;
|
||||
@ -53,7 +54,7 @@ public class DecryptVerifyResult extends OperationResult {
|
||||
mKeyIdPassphraseNeeded = keyIdPassphraseNeeded;
|
||||
}
|
||||
|
||||
public void setNfcState(long subKeyId, byte[] sessionKey, String passphrase) {
|
||||
public void setNfcState(long subKeyId, byte[] sessionKey, Passphrase passphrase) {
|
||||
mNfcSubKeyId = subKeyId;
|
||||
mNfcSessionKey = sessionKey;
|
||||
mNfcPassphrase = passphrase;
|
||||
@ -67,7 +68,7 @@ public class DecryptVerifyResult extends OperationResult {
|
||||
return mNfcSessionKey;
|
||||
}
|
||||
|
||||
public String getNfcPassphrase() {
|
||||
public Passphrase getNfcPassphrase() {
|
||||
return mNfcPassphrase;
|
||||
}
|
||||
|
||||
@ -109,7 +110,7 @@ public class DecryptVerifyResult extends OperationResult {
|
||||
mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
|
||||
mDecryptMetadata = source.readParcelable(OpenPgpMetadata.class.getClassLoader());
|
||||
mNfcSessionKey = source.readInt() != 0 ? source.createByteArray() : null;
|
||||
mNfcPassphrase = source.readString();
|
||||
mNfcPassphrase = source.readParcelable(Passphrase.class.getClassLoader());
|
||||
}
|
||||
|
||||
public int describeContents() {
|
||||
@ -127,7 +128,7 @@ public class DecryptVerifyResult extends OperationResult {
|
||||
} else {
|
||||
dest.writeInt(0);
|
||||
}
|
||||
dest.writeString(mNfcPassphrase);
|
||||
dest.writeParcelable(mNfcPassphrase, flags);
|
||||
}
|
||||
|
||||
public static final Creator<DecryptVerifyResult> CREATOR = new Creator<DecryptVerifyResult>() {
|
||||
|
@ -116,7 +116,7 @@ public class DeleteResult extends OperationResult {
|
||||
}
|
||||
}
|
||||
|
||||
return Notify.createNotify(activity, str, duration, style, new ActionListener() {
|
||||
return Notify.create(activity, str, duration, style, new ActionListener() {
|
||||
@Override
|
||||
public void onAction() {
|
||||
Intent intent = new Intent(
|
||||
|
@ -179,7 +179,7 @@ public class ImportKeyResult extends OperationResult {
|
||||
}
|
||||
}
|
||||
|
||||
return Notify.createNotify(activity, str, duration, style, new ActionListener() {
|
||||
return Notify.create(activity, str, duration, style, new ActionListener() {
|
||||
@Override
|
||||
public void onAction() {
|
||||
Intent intent = new Intent(
|
||||
|
@ -273,19 +273,19 @@ public abstract class OperationResult implements Parcelable {
|
||||
}
|
||||
|
||||
if (getLog() == null || getLog().isEmpty()) {
|
||||
return Notify.createNotify(activity, msgId, Notify.LENGTH_LONG, style);
|
||||
return Notify.create(activity, msgId, Notify.LENGTH_LONG, style);
|
||||
}
|
||||
|
||||
return Notify.createNotify(activity, msgId, Notify.LENGTH_LONG, style,
|
||||
new ActionListener() {
|
||||
@Override
|
||||
public void onAction() {
|
||||
Intent intent = new Intent(
|
||||
activity, LogDisplayActivity.class);
|
||||
intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResult.this);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
}, R.string.view_log);
|
||||
return Notify.create(activity, msgId, Notify.LENGTH_LONG, style,
|
||||
new ActionListener() {
|
||||
@Override
|
||||
public void onAction() {
|
||||
Intent intent = new Intent(
|
||||
activity, LogDisplayActivity.class);
|
||||
intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResult.this);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
}, R.string.view_log);
|
||||
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.operations.results;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class PgpSignEncryptResult extends OperationResult {
|
||||
@ -36,7 +38,7 @@ public class PgpSignEncryptResult extends OperationResult {
|
||||
byte[] mNfcHash;
|
||||
int mNfcAlgo;
|
||||
Date mNfcTimestamp;
|
||||
String mNfcPassphrase;
|
||||
Passphrase mNfcPassphrase;
|
||||
byte[] mDetachedSignature;
|
||||
|
||||
public long getKeyIdPassphraseNeeded() {
|
||||
@ -47,7 +49,7 @@ public class PgpSignEncryptResult extends OperationResult {
|
||||
mKeyIdPassphraseNeeded = keyIdPassphraseNeeded;
|
||||
}
|
||||
|
||||
public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, Date nfcTimestamp, String passphrase) {
|
||||
public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, Date nfcTimestamp, Passphrase passphrase) {
|
||||
mNfcKeyId = nfcKeyId;
|
||||
mNfcHash = nfcHash;
|
||||
mNfcAlgo = nfcAlgo;
|
||||
@ -75,7 +77,7 @@ public class PgpSignEncryptResult extends OperationResult {
|
||||
return mNfcTimestamp;
|
||||
}
|
||||
|
||||
public String getNfcPassphrase() {
|
||||
public Passphrase getNfcPassphrase() {
|
||||
return mNfcPassphrase;
|
||||
}
|
||||
|
||||
|
@ -55,8 +55,8 @@ public class CanonicalizedPublicKey extends UncachedPublicKey {
|
||||
return new IterableIterator<String>(mPublicKey.getUserIDs());
|
||||
}
|
||||
|
||||
JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator() {
|
||||
return new JcePublicKeyKeyEncryptionMethodGenerator(mPublicKey);
|
||||
JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator(boolean hiddenRecipients) {
|
||||
return new JcePublicKeyKeyEncryptionMethodGenerator(mPublicKey, hiddenRecipients);
|
||||
}
|
||||
|
||||
public boolean canSign() {
|
||||
|
@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
@ -149,7 +150,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
/**
|
||||
* Returns true on right passphrase
|
||||
*/
|
||||
public boolean unlock(String passphrase) throws PgpGeneralException {
|
||||
public boolean unlock(Passphrase passphrase) throws PgpGeneralException {
|
||||
// handle keys on OpenPGP cards like they were unlocked
|
||||
if (mSecretKey.getS2K() != null
|
||||
&& mSecretKey.getS2K().getType() == S2K.GNU_DUMMY_S2K
|
||||
@ -161,7 +162,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
// try to extract keys using the passphrase
|
||||
try {
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray());
|
||||
mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor);
|
||||
mPrivateKeyState = PRIVATE_KEY_STATE_UNLOCKED;
|
||||
} catch (PGPException e) {
|
||||
|
@ -44,7 +44,7 @@ public abstract class KeyRing {
|
||||
|
||||
abstract public String getPrimaryUserIdWithFallback() throws PgpKeyNotFoundException;
|
||||
|
||||
public String[] getSplitPrimaryUserIdWithFallback() throws PgpKeyNotFoundException {
|
||||
public UserId getSplitPrimaryUserIdWithFallback() throws PgpKeyNotFoundException {
|
||||
return splitUserId(getPrimaryUserIdWithFallback());
|
||||
}
|
||||
|
||||
@ -62,55 +62,45 @@ public abstract class KeyRing {
|
||||
|
||||
/**
|
||||
* Splits userId string into naming part, email part, and comment part
|
||||
*
|
||||
* @param userId
|
||||
* @return array with naming (0), email (1), comment (2)
|
||||
* <p/>
|
||||
* User ID matching:
|
||||
* http://fiddle.re/t4p6f
|
||||
*/
|
||||
public static String[] splitUserId(String userId) {
|
||||
String[] result = new String[]{null, null, null};
|
||||
|
||||
if (userId == null || userId.equals("")) {
|
||||
return result;
|
||||
public static UserId splitUserId(final String userId) {
|
||||
if (!TextUtils.isEmpty(userId)) {
|
||||
final Matcher matcher = USER_ID_PATTERN.matcher(userId);
|
||||
if (matcher.matches()) {
|
||||
return new UserId(matcher.group(1), matcher.group(3), matcher.group(2));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* User ID matching:
|
||||
* http://fiddle.re/t4p6f
|
||||
*
|
||||
* test cases:
|
||||
* "Max Mustermann (this is a comment) <max@example.com>"
|
||||
* "Max Mustermann <max@example.com>"
|
||||
* "Max Mustermann (this is a comment)"
|
||||
* "Max Mustermann [this is nothing]"
|
||||
*/
|
||||
Matcher matcher = USER_ID_PATTERN.matcher(userId);
|
||||
if (matcher.matches()) {
|
||||
result[0] = matcher.group(1);
|
||||
result[1] = matcher.group(3);
|
||||
result[2] = matcher.group(2);
|
||||
}
|
||||
|
||||
return result;
|
||||
return new UserId(null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a composed user id. Returns null if name is null!
|
||||
*
|
||||
* @param name
|
||||
* @param email
|
||||
* @param comment
|
||||
* @return
|
||||
*/
|
||||
public static String createUserId(String name, String email, String comment) {
|
||||
String userId = name; // consider name a required value
|
||||
if (userId != null && !TextUtils.isEmpty(comment)) {
|
||||
userId += " (" + comment + ")";
|
||||
public static String createUserId(UserId userId) {
|
||||
String userIdString = userId.name; // consider name a required value
|
||||
if (userIdString != null && !TextUtils.isEmpty(userId.comment)) {
|
||||
userIdString += " (" + userId.comment + ")";
|
||||
}
|
||||
if (userId != null && !TextUtils.isEmpty(email)) {
|
||||
userId += " <" + email + ">";
|
||||
if (userIdString != null && !TextUtils.isEmpty(userId.email)) {
|
||||
userIdString += " <" + userId.email + ">";
|
||||
}
|
||||
|
||||
return userId;
|
||||
return userIdString;
|
||||
}
|
||||
|
||||
public static class UserId {
|
||||
public final String name;
|
||||
public final String email;
|
||||
public final String comment;
|
||||
|
||||
public UserId(String name, String email, String comment) {
|
||||
this.name = name;
|
||||
this.email = email;
|
||||
this.comment = comment;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,13 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.pgp;
|
||||
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
public interface PassphraseCacheInterface {
|
||||
public static class NoSecretKeyException extends Exception {
|
||||
public NoSecretKeyException() {
|
||||
}
|
||||
}
|
||||
|
||||
public String getCachedPassphrase(long subKeyId) throws NoSecretKeyException;
|
||||
public Passphrase getCachedPassphrase(long subKeyId) throws NoSecretKeyException;
|
||||
|
||||
public String getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException;
|
||||
public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException;
|
||||
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.InputData;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
@ -83,7 +84,7 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
private OutputStream mOutStream;
|
||||
|
||||
private boolean mAllowSymmetricDecryption;
|
||||
private String mPassphrase;
|
||||
private Passphrase mPassphrase;
|
||||
private Set<Long> mAllowedKeyIds;
|
||||
private boolean mDecryptMetadataOnly;
|
||||
private byte[] mDecryptedSessionKey;
|
||||
@ -118,7 +119,7 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
private OutputStream mOutStream = null;
|
||||
private Progressable mProgressable = null;
|
||||
private boolean mAllowSymmetricDecryption = true;
|
||||
private String mPassphrase = null;
|
||||
private Passphrase mPassphrase = null;
|
||||
private Set<Long> mAllowedKeyIds = null;
|
||||
private boolean mDecryptMetadataOnly = false;
|
||||
private byte[] mDecryptedSessionKey = null;
|
||||
@ -159,7 +160,7 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPassphrase(String passphrase) {
|
||||
public Builder setPassphrase(Passphrase passphrase) {
|
||||
mPassphrase = passphrase;
|
||||
return this;
|
||||
}
|
||||
@ -572,7 +573,7 @@ public class PgpDecryptVerify extends BaseOperation {
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build();
|
||||
PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder(
|
||||
digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||
mPassphrase.toCharArray());
|
||||
mPassphrase.getCharArray());
|
||||
|
||||
clear = encryptedDataSymmetric.getDataStream(decryptorFactory);
|
||||
encryptedData = encryptedDataSymmetric;
|
||||
|
@ -57,6 +57,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
import org.sufficientlysecure.keychain.util.Primes;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
|
||||
@ -316,7 +317,7 @@ public class PgpKeyOperation {
|
||||
masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());
|
||||
|
||||
subProgressPush(50, 100);
|
||||
return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, "", log);
|
||||
return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, new Passphrase(), log);
|
||||
|
||||
} catch (PGPException e) {
|
||||
log.add(LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
|
||||
@ -348,7 +349,7 @@ public class PgpKeyOperation {
|
||||
*
|
||||
*/
|
||||
public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
|
||||
String passphrase) {
|
||||
Passphrase passphrase) {
|
||||
|
||||
OperationLog log = new OperationLog();
|
||||
int indent = 0;
|
||||
@ -404,7 +405,7 @@ public class PgpKeyOperation {
|
||||
|
||||
private PgpEditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
|
||||
int masterKeyFlags, long masterKeyExpiry,
|
||||
SaveKeyringParcel saveParcel, String passphrase,
|
||||
SaveKeyringParcel saveParcel, Passphrase passphrase,
|
||||
OperationLog log) {
|
||||
|
||||
int indent = 1;
|
||||
@ -420,7 +421,7 @@ public class PgpKeyOperation {
|
||||
{
|
||||
try {
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray());
|
||||
masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor);
|
||||
} catch (PGPException e) {
|
||||
log.add(LogType.MSG_MF_UNLOCK_ERROR, indent + 1);
|
||||
@ -839,7 +840,7 @@ public class PgpKeyOperation {
|
||||
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray());
|
||||
|
||||
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
|
||||
.build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
|
||||
@ -967,7 +968,7 @@ public class PgpKeyOperation {
|
||||
PGPSecretKeyRing sKR,
|
||||
PGPPublicKey masterPublicKey,
|
||||
PGPPrivateKey masterPrivateKey,
|
||||
String passphrase,
|
||||
Passphrase passphrase,
|
||||
ChangeUnlockParcel newUnlock,
|
||||
OperationLog log, int indent) throws PGPException {
|
||||
|
||||
@ -1051,20 +1052,20 @@ public class PgpKeyOperation {
|
||||
private static PGPSecretKeyRing applyNewPassphrase(
|
||||
PGPSecretKeyRing sKR,
|
||||
PGPPublicKey masterPublicKey,
|
||||
String passphrase,
|
||||
String newPassphrase,
|
||||
Passphrase passphrase,
|
||||
Passphrase newPassphrase,
|
||||
OperationLog log, int indent) throws PGPException {
|
||||
|
||||
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build()
|
||||
.get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray());
|
||||
// Build key encryptor based on new passphrase
|
||||
PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
|
||||
PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||
newPassphrase.toCharArray());
|
||||
newPassphrase.getCharArray());
|
||||
|
||||
// noinspection unchecked
|
||||
for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) {
|
||||
@ -1295,11 +1296,11 @@ public class PgpKeyOperation {
|
||||
|
||||
private static PGPSignature generateSubkeyBindingSignature(
|
||||
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
|
||||
PGPSecretKey sKey, PGPPublicKey pKey, int flags, long expiry, String passphrase)
|
||||
PGPSecretKey sKey, PGPPublicKey pKey, int flags, long expiry, Passphrase passphrase)
|
||||
throws IOException, PGPException, SignatureException {
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||
passphrase.toCharArray());
|
||||
passphrase.getCharArray());
|
||||
PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
|
||||
return generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, subPrivateKey,
|
||||
pKey, flags, expiry);
|
||||
|
@ -1,7 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.pgp;
|
||||
|
||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@ -11,19 +30,20 @@ public class PgpSignEncryptInput {
|
||||
protected boolean mEnableAsciiArmorOutput = false;
|
||||
protected int mCompressionId = CompressionAlgorithmTags.UNCOMPRESSED;
|
||||
protected long[] mEncryptionMasterKeyIds = null;
|
||||
protected String mSymmetricPassphrase = null;
|
||||
protected Passphrase mSymmetricPassphrase = null;
|
||||
protected int mSymmetricEncryptionAlgorithm = PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED;
|
||||
protected long mSignatureMasterKeyId = Constants.key.none;
|
||||
protected Long mSignatureSubKeyId = null;
|
||||
protected int mSignatureHashAlgorithm = PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED;
|
||||
protected String mSignaturePassphrase = null;
|
||||
protected Passphrase mSignaturePassphrase = null;
|
||||
protected long mAdditionalEncryptId = Constants.key.none;
|
||||
protected byte[] mNfcSignedHash = null;
|
||||
protected Date mNfcCreationTimestamp = null;
|
||||
protected boolean mFailOnMissingEncryptionKeyIds = false;
|
||||
protected String mCharset;
|
||||
protected boolean mCleartextSignature;
|
||||
protected boolean mDetachedSignature;
|
||||
protected boolean mDetachedSignature = false;
|
||||
protected boolean mHiddenRecipients = false;
|
||||
|
||||
public String getCharset() {
|
||||
return mCharset;
|
||||
@ -33,7 +53,7 @@ public class PgpSignEncryptInput {
|
||||
this.mCharset = mCharset;
|
||||
}
|
||||
|
||||
public boolean ismFailOnMissingEncryptionKeyIds() {
|
||||
public boolean isFailOnMissingEncryptionKeyIds() {
|
||||
return mFailOnMissingEncryptionKeyIds;
|
||||
}
|
||||
|
||||
@ -54,11 +74,11 @@ public class PgpSignEncryptInput {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getSignaturePassphrase() {
|
||||
public Passphrase getSignaturePassphrase() {
|
||||
return mSignaturePassphrase;
|
||||
}
|
||||
|
||||
public PgpSignEncryptInput setSignaturePassphrase(String signaturePassphrase) {
|
||||
public PgpSignEncryptInput setSignaturePassphrase(Passphrase signaturePassphrase) {
|
||||
mSignaturePassphrase = signaturePassphrase;
|
||||
return this;
|
||||
}
|
||||
@ -99,11 +119,11 @@ public class PgpSignEncryptInput {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getSymmetricPassphrase() {
|
||||
public Passphrase getSymmetricPassphrase() {
|
||||
return mSymmetricPassphrase;
|
||||
}
|
||||
|
||||
public PgpSignEncryptInput setSymmetricPassphrase(String symmetricPassphrase) {
|
||||
public PgpSignEncryptInput setSymmetricPassphrase(Passphrase symmetricPassphrase) {
|
||||
mSymmetricPassphrase = symmetricPassphrase;
|
||||
return this;
|
||||
}
|
||||
@ -126,7 +146,7 @@ public class PgpSignEncryptInput {
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean ismEnableAsciiArmorOutput() {
|
||||
public boolean isEnableAsciiArmorOutput() {
|
||||
return mEnableAsciiArmorOutput;
|
||||
}
|
||||
|
||||
@ -172,5 +192,14 @@ public class PgpSignEncryptInput {
|
||||
public boolean isDetachedSignature() {
|
||||
return mDetachedSignature;
|
||||
}
|
||||
|
||||
public PgpSignEncryptInput setHiddenRecipients(boolean hiddenRecipients) {
|
||||
this.mHiddenRecipients = hiddenRecipients;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isHiddenRecipients() {
|
||||
return mHiddenRecipients;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2012-2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
|
||||
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
*
|
||||
@ -60,7 +60,6 @@ import java.security.SignatureException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/** This class supports a single, low-level, sign/encrypt operation.
|
||||
@ -117,7 +116,8 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
||||
Log.d(Constants.TAG, "enableSignature:" + enableSignature
|
||||
+ "\nenableEncryption:" + enableEncryption
|
||||
+ "\nenableCompression:" + enableCompression
|
||||
+ "\nenableAsciiArmorOutput:" + input.ismEnableAsciiArmorOutput());
|
||||
+ "\nenableAsciiArmorOutput:" + input.isEnableAsciiArmorOutput()
|
||||
+ "\nisHiddenRecipients:" + input.isHiddenRecipients());
|
||||
|
||||
// add additional key id to encryption ids (mostly to do self-encryption)
|
||||
if (enableEncryption && input.getAdditionalEncryptId() != Constants.key.none) {
|
||||
@ -127,7 +127,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
||||
|
||||
ArmoredOutputStream armorOut = null;
|
||||
OutputStream out;
|
||||
if (input.ismEnableAsciiArmorOutput()) {
|
||||
if (input.isEnableAsciiArmorOutput()) {
|
||||
armorOut = new ArmoredOutputStream(outputStream);
|
||||
if (input.getVersionHeader() != null) {
|
||||
armorOut.setHeader("Version", input.getVersionHeader());
|
||||
@ -243,7 +243,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
||||
log.add(LogType.MSG_PSE_SYMMETRIC, indent);
|
||||
|
||||
JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator =
|
||||
new JcePBEKeyEncryptionMethodGenerator(input.getSymmetricPassphrase().toCharArray());
|
||||
new JcePBEKeyEncryptionMethodGenerator(input.getSymmetricPassphrase().getCharArray());
|
||||
cPk.addMethod(symmetricEncryptionGenerator);
|
||||
} else {
|
||||
log.add(LogType.MSG_PSE_ASYMMETRIC, indent);
|
||||
@ -254,19 +254,19 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
||||
CanonicalizedPublicKeyRing keyRing = mProviderHelper.getCanonicalizedPublicKeyRing(
|
||||
KeyRings.buildUnifiedKeyRingUri(id));
|
||||
CanonicalizedPublicKey key = keyRing.getEncryptionSubKey();
|
||||
cPk.addMethod(key.getPubKeyEncryptionGenerator());
|
||||
cPk.addMethod(key.getPubKeyEncryptionGenerator(input.isHiddenRecipients()));
|
||||
log.add(LogType.MSG_PSE_KEY_OK, indent + 1,
|
||||
KeyFormattingUtils.convertKeyIdToHex(id));
|
||||
} catch (PgpKeyNotFoundException e) {
|
||||
log.add(LogType.MSG_PSE_KEY_WARN, indent + 1,
|
||||
KeyFormattingUtils.convertKeyIdToHex(id));
|
||||
if (input.ismFailOnMissingEncryptionKeyIds()) {
|
||||
if (input.isFailOnMissingEncryptionKeyIds()) {
|
||||
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
|
||||
}
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
log.add(LogType.MSG_PSE_KEY_UNKNOWN, indent + 1,
|
||||
KeyFormattingUtils.convertKeyIdToHex(id));
|
||||
if (input.ismFailOnMissingEncryptionKeyIds()) {
|
||||
if (input.isFailOnMissingEncryptionKeyIds()) {
|
||||
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
|
||||
}
|
||||
}
|
||||
@ -280,7 +280,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
||||
updateProgress(R.string.progress_preparing_signature, 4, 100);
|
||||
|
||||
try {
|
||||
boolean cleartext = input.isCleartextSignature() && input.ismEnableAsciiArmorOutput() && !enableEncryption;
|
||||
boolean cleartext = input.isCleartextSignature() && input.isEnableAsciiArmorOutput() && !enableEncryption;
|
||||
signatureGenerator = signingKey.getSignatureGenerator(
|
||||
input.getSignatureHashAlgorithm(), cleartext, input.getNfcSignedHash(), input.getNfcCreationTimestamp());
|
||||
} catch (PgpGeneralException e) {
|
||||
@ -358,7 +358,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
||||
literalGen.close();
|
||||
indent -= 1;
|
||||
|
||||
} else if (enableSignature && input.isCleartextSignature() && input.ismEnableAsciiArmorOutput()) {
|
||||
} else if (enableSignature && input.isCleartextSignature() && input.isEnableAsciiArmorOutput()) {
|
||||
/* cleartext signature: sign-only of ascii text */
|
||||
|
||||
updateProgress(R.string.progress_signing, 8, 100);
|
||||
@ -404,7 +404,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
||||
// handle output stream separately for detached signatures
|
||||
detachedByteOut = new ByteArrayOutputStream();
|
||||
OutputStream detachedOut = detachedByteOut;
|
||||
if (input.ismEnableAsciiArmorOutput()) {
|
||||
if (input.isEnableAsciiArmorOutput()) {
|
||||
detachedArmorOut = new ArmoredOutputStream(detachedOut);
|
||||
if (input.getVersionHeader() != null) {
|
||||
detachedArmorOut.setHeader("Version", input.getVersionHeader());
|
||||
|
@ -1,9 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.pgp;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -39,12 +59,12 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable
|
||||
mEnableAsciiArmorOutput = src.readInt() == 1;
|
||||
mCompressionId = src.readInt();
|
||||
mEncryptionMasterKeyIds = src.createLongArray();
|
||||
mSymmetricPassphrase = src.readString();
|
||||
mSymmetricPassphrase = src.readParcelable(Passphrase.class.getClassLoader());
|
||||
mSymmetricEncryptionAlgorithm = src.readInt();
|
||||
mSignatureMasterKeyId = src.readLong();
|
||||
mSignatureSubKeyId = src.readInt() == 1 ? src.readLong() : null;
|
||||
mSignatureHashAlgorithm = src.readInt();
|
||||
mSignaturePassphrase = src.readString();
|
||||
mSignaturePassphrase = src.readParcelable(Passphrase.class.getClassLoader());
|
||||
mAdditionalEncryptId = src.readLong();
|
||||
mNfcSignedHash = src.createByteArray();
|
||||
mNfcCreationTimestamp = src.readInt() == 1 ? new Date(src.readLong()) : null;
|
||||
@ -52,6 +72,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable
|
||||
mCharset = src.readString();
|
||||
mCleartextSignature = src.readInt() == 1;
|
||||
mDetachedSignature = src.readInt() == 1;
|
||||
mHiddenRecipients = src.readInt() == 1;
|
||||
|
||||
mInputUris = src.createTypedArrayList(Uri.CREATOR);
|
||||
mOutputUris = src.createTypedArrayList(Uri.CREATOR);
|
||||
@ -93,7 +114,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable
|
||||
dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0);
|
||||
dest.writeInt(mCompressionId);
|
||||
dest.writeLongArray(mEncryptionMasterKeyIds);
|
||||
dest.writeString(mSymmetricPassphrase);
|
||||
dest.writeParcelable(mSymmetricPassphrase, flags);
|
||||
dest.writeInt(mSymmetricEncryptionAlgorithm);
|
||||
dest.writeLong(mSignatureMasterKeyId);
|
||||
if (mSignatureSubKeyId != null) {
|
||||
@ -103,7 +124,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable
|
||||
dest.writeInt(0);
|
||||
}
|
||||
dest.writeInt(mSignatureHashAlgorithm);
|
||||
dest.writeString(mSignaturePassphrase);
|
||||
dest.writeParcelable(mSignaturePassphrase, flags);
|
||||
dest.writeLong(mAdditionalEncryptId);
|
||||
dest.writeByteArray(mNfcSignedHash);
|
||||
if (mNfcCreationTimestamp != null) {
|
||||
@ -116,6 +137,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable
|
||||
dest.writeString(mCharset);
|
||||
dest.writeInt(mCleartextSignature ? 1 : 0);
|
||||
dest.writeInt(mDetachedSignature ? 1 : 0);
|
||||
dest.writeInt(mHiddenRecipients ? 1 : 0);
|
||||
|
||||
dest.writeTypedList(mInputUris);
|
||||
dest.writeTypedList(mOutputUris);
|
||||
|
@ -54,6 +54,7 @@ import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
|
||||
import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
|
||||
import org.sufficientlysecure.keychain.util.InputData;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -179,7 +180,7 @@ public class OpenPgpService extends RemoteService {
|
||||
return result;
|
||||
}
|
||||
|
||||
private PendingIntent getNfcSignPendingIntent(Intent data, long keyId, String pin, byte[] hashToSign, int hashAlgo) {
|
||||
private PendingIntent getNfcSignPendingIntent(Intent data, long keyId, Passphrase pin, byte[] hashToSign, int hashAlgo) {
|
||||
// build PendingIntent for Yubikey NFC operations
|
||||
Intent intent = new Intent(getBaseContext(), NfcActivity.class);
|
||||
intent.setAction(NfcActivity.ACTION_SIGN_HASH);
|
||||
@ -195,7 +196,7 @@ public class OpenPgpService extends RemoteService {
|
||||
PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
}
|
||||
|
||||
private PendingIntent getNfcDecryptPendingIntent(Intent data, long subKeyId, String pin, byte[] encryptedSessionKey) {
|
||||
private PendingIntent getNfcDecryptPendingIntent(Intent data, long subKeyId, Passphrase pin, byte[] encryptedSessionKey) {
|
||||
// build PendingIntent for Yubikey NFC operations
|
||||
Intent intent = new Intent(getBaseContext(), NfcActivity.class);
|
||||
intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY);
|
||||
@ -239,6 +240,11 @@ public class OpenPgpService extends RemoteService {
|
||||
try {
|
||||
boolean asciiArmor = cleartextSign || data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||
|
||||
Passphrase passphrase = null;
|
||||
if (data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE) != null) {
|
||||
passphrase = new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
|
||||
}
|
||||
|
||||
byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH);
|
||||
if (nfcSignedHash != null) {
|
||||
Log.d(Constants.TAG, "nfcSignedHash:" + Hex.toHexString(nfcSignedHash));
|
||||
@ -277,6 +283,7 @@ public class OpenPgpService extends RemoteService {
|
||||
|
||||
// sign-only
|
||||
PgpSignEncryptInput pseInput = new PgpSignEncryptInput()
|
||||
.setSignaturePassphrase(passphrase)
|
||||
.setEnableAsciiArmorOutput(asciiArmor)
|
||||
.setCleartextSignature(cleartextSign)
|
||||
.setDetachedSignature(!cleartextSign)
|
||||
@ -365,6 +372,11 @@ public class OpenPgpService extends RemoteService {
|
||||
compressionId = CompressionAlgorithmTags.UNCOMPRESSED;
|
||||
}
|
||||
|
||||
Passphrase passphrase = null;
|
||||
if (data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE) != null) {
|
||||
passphrase = new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
|
||||
}
|
||||
|
||||
// first try to get key ids from non-ambiguous key id extra
|
||||
long[] keyIds = data.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS);
|
||||
if (keyIds == null) {
|
||||
@ -390,7 +402,8 @@ public class OpenPgpService extends RemoteService {
|
||||
InputData inputData = new InputData(is, inputLength, originalFilename);
|
||||
|
||||
PgpSignEncryptInput pseInput = new PgpSignEncryptInput();
|
||||
pseInput.setEnableAsciiArmorOutput(asciiArmor)
|
||||
pseInput.setSignaturePassphrase(passphrase)
|
||||
.setEnableAsciiArmorOutput(asciiArmor)
|
||||
.setVersionHeader(null)
|
||||
.setCompressionId(compressionId)
|
||||
.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED)
|
||||
@ -498,6 +511,11 @@ public class OpenPgpService extends RemoteService {
|
||||
os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
|
||||
}
|
||||
|
||||
Passphrase passphrase = null;
|
||||
if (data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE) != null) {
|
||||
passphrase = new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
|
||||
}
|
||||
|
||||
String currentPkg = getCurrentCallingPackage();
|
||||
Set<Long> allowedKeyIds;
|
||||
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 7) {
|
||||
@ -508,7 +526,6 @@ public class OpenPgpService extends RemoteService {
|
||||
KeychainContract.ApiAllowedKeys.buildBaseUri(currentPkg));
|
||||
}
|
||||
|
||||
String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
|
||||
long inputLength = is.available();
|
||||
InputData inputData = new InputData(is, inputLength);
|
||||
|
||||
@ -554,15 +571,16 @@ public class OpenPgpService extends RemoteService {
|
||||
}
|
||||
} else if (pgpResult.success()) {
|
||||
Intent result = new Intent();
|
||||
int resultType = OpenPgpApi.RESULT_TYPE_UNENCRYPTED_UNSIGNED;
|
||||
|
||||
OpenPgpSignatureResult signatureResult = pgpResult.getSignatureResult();
|
||||
// TODO: currently RESULT_TYPE_UNENCRYPTED_UNSIGNED is never returned
|
||||
// instead an error is returned when no pgp data has been found
|
||||
int resultType = OpenPgpApi.RESULT_TYPE_UNENCRYPTED_UNSIGNED;
|
||||
if (signatureResult != null) {
|
||||
resultType |= OpenPgpApi.RESULT_TYPE_SIGNED;
|
||||
if (!signatureResult.isSignatureOnly()) {
|
||||
resultType |= OpenPgpApi.RESULT_TYPE_ENCRYPTED;
|
||||
}
|
||||
result.putExtra(OpenPgpApi.RESULT_TYPE, resultType);
|
||||
|
||||
result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult);
|
||||
|
||||
@ -582,7 +600,10 @@ public class OpenPgpService extends RemoteService {
|
||||
// If signature key is known, return PendingIntent to show key
|
||||
result.putExtra(OpenPgpApi.RESULT_INTENT, getShowKeyPendingIntent(signatureResult.getKeyId()));
|
||||
}
|
||||
} else {
|
||||
resultType |= OpenPgpApi.RESULT_TYPE_ENCRYPTED;
|
||||
}
|
||||
result.putExtra(OpenPgpApi.RESULT_TYPE, resultType);
|
||||
|
||||
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) >= 4) {
|
||||
OpenPgpMetadata metadata = pgpResult.getDecryptMetadata();
|
||||
|
@ -92,11 +92,11 @@ public class AccountSettingsFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void createKey() {
|
||||
String[] userId = KeyRing.splitUserId(mAccSettings.getAccountName());
|
||||
KeyRing.UserId userId = KeyRing.splitUserId(mAccSettings.getAccountName());
|
||||
|
||||
Intent intent = new Intent(getActivity(), CreateKeyActivity.class);
|
||||
intent.putExtra(CreateKeyActivity.EXTRA_NAME, userId[0]);
|
||||
intent.putExtra(CreateKeyActivity.EXTRA_EMAIL, userId[1]);
|
||||
intent.putExtra(CreateKeyActivity.EXTRA_NAME, userId.name);
|
||||
intent.putExtra(CreateKeyActivity.EXTRA_EMAIL, userId.email);
|
||||
startActivityForResult(intent, REQUEST_CODE_CREATE_KEY);
|
||||
}
|
||||
|
||||
|
@ -186,7 +186,7 @@ public class RemoteServiceActivity extends BaseActivity {
|
||||
|
||||
// user needs to select a key if it has explicitly requested (None is only allowed for new accounts)
|
||||
if (mUpdateExistingAccount && mAccSettingsFragment.getAccSettings().getKeyId() == Constants.key.none) {
|
||||
Notify.showNotify(RemoteServiceActivity.this, getString(R.string.api_register_error_select_key), Notify.Style.ERROR);
|
||||
Notify.create(RemoteServiceActivity.this, getString(R.string.api_register_error_select_key), Notify.Style.ERROR).show();
|
||||
} else {
|
||||
if (mUpdateExistingAccount) {
|
||||
Uri baseUri = KeychainContract.ApiAccounts.buildBaseUri(packageName);
|
||||
|
@ -42,9 +42,11 @@ public class SelectSignKeyIdActivity extends BaseActivity {
|
||||
|
||||
private Uri mAppUri;
|
||||
private String mPreferredUserId;
|
||||
private Intent mData;
|
||||
|
||||
private SelectSignKeyIdListFragment mListFragment;
|
||||
private TextView mActionCreateKey;
|
||||
private TextView mNone;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@ -67,27 +69,38 @@ public class SelectSignKeyIdActivity extends BaseActivity {
|
||||
createKey(mPreferredUserId);
|
||||
}
|
||||
});
|
||||
mNone = (TextView) findViewById(R.id.api_select_sign_key_none);
|
||||
mNone.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// 0 is "none"
|
||||
mData.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, 0);
|
||||
|
||||
setResult(Activity.RESULT_OK, mData);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
Intent intent = getIntent();
|
||||
mAppUri = intent.getData();
|
||||
mPreferredUserId = intent.getStringExtra(EXTRA_USER_ID);
|
||||
Intent data = intent.getParcelableExtra(EXTRA_DATA);
|
||||
mData = intent.getParcelableExtra(EXTRA_DATA);
|
||||
if (mAppUri == null) {
|
||||
Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
|
||||
finish();
|
||||
return;
|
||||
} else {
|
||||
Log.d(Constants.TAG, "uri: " + mAppUri);
|
||||
startListFragments(savedInstanceState, mAppUri, data);
|
||||
startListFragments(savedInstanceState, mAppUri, mData);
|
||||
}
|
||||
}
|
||||
|
||||
private void createKey(String userId) {
|
||||
String[] userIdSplit = KeyRing.splitUserId(userId);
|
||||
KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId);
|
||||
|
||||
Intent intent = new Intent(this, CreateKeyActivity.class);
|
||||
intent.putExtra(CreateKeyActivity.EXTRA_NAME, userIdSplit[0]);
|
||||
intent.putExtra(CreateKeyActivity.EXTRA_EMAIL, userIdSplit[1]);
|
||||
intent.putExtra(CreateKeyActivity.EXTRA_NAME, userIdSplit.name);
|
||||
intent.putExtra(CreateKeyActivity.EXTRA_EMAIL, userIdSplit.email);
|
||||
startActivityForResult(intent, REQUEST_CODE_CREATE_KEY);
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,7 @@ import org.sufficientlysecure.keychain.util.FileHelper;
|
||||
import org.sufficientlysecure.keychain.util.InputData;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@ -289,7 +290,7 @@ public class KeychainIntentService extends IntentService implements Progressable
|
||||
|
||||
try {
|
||||
/* Input */
|
||||
String passphrase = data.getString(DECRYPT_PASSPHRASE);
|
||||
Passphrase passphrase = data.getParcelable(DECRYPT_PASSPHRASE);
|
||||
byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY);
|
||||
|
||||
InputData inputData = createDecryptInputData(data);
|
||||
@ -419,7 +420,7 @@ public class KeychainIntentService extends IntentService implements Progressable
|
||||
|
||||
try {
|
||||
/* Input */
|
||||
String passphrase = data.getString(DECRYPT_PASSPHRASE);
|
||||
Passphrase passphrase = data.getParcelable(DECRYPT_PASSPHRASE);
|
||||
byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY);
|
||||
|
||||
InputData inputData = createDecryptInputData(data);
|
||||
@ -477,7 +478,7 @@ public class KeychainIntentService extends IntentService implements Progressable
|
||||
|
||||
// Input
|
||||
SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL);
|
||||
String passphrase = data.getString(EDIT_KEYRING_PASSPHRASE);
|
||||
Passphrase passphrase = data.getParcelable(EDIT_KEYRING_PASSPHRASE);
|
||||
|
||||
// Operation
|
||||
EditKeyOperation op = new EditKeyOperation(this, providerHelper, this, mActionCanceled);
|
||||
|
@ -129,9 +129,9 @@ public class KeychainIntentServiceHandler extends Handler {
|
||||
|
||||
// show error from service
|
||||
if (data.containsKey(DATA_ERROR)) {
|
||||
Notify.showNotify(mActivity,
|
||||
Notify.create(mActivity,
|
||||
mActivity.getString(R.string.error_message, data.getString(DATA_ERROR)),
|
||||
Notify.Style.ERROR);
|
||||
Notify.Style.ERROR).show();
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
|
||||
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
import java.util.Date;
|
||||
@ -121,7 +122,7 @@ public class PassphraseCacheService extends Service {
|
||||
* new events to the alarm manager for new passphrases to let them timeout in the future.
|
||||
*/
|
||||
public static void addCachedPassphrase(Context context, long masterKeyId, long subKeyId,
|
||||
String passphrase,
|
||||
Passphrase passphrase,
|
||||
String primaryUserId) {
|
||||
Log.d(Constants.TAG, "PassphraseCacheService.cacheNewPassphrase() for " + masterKeyId);
|
||||
|
||||
@ -143,7 +144,7 @@ public class PassphraseCacheService extends Service {
|
||||
|
||||
* @return passphrase or null (if no passphrase is cached for this keyId)
|
||||
*/
|
||||
public static String getCachedPassphrase(Context context, long masterKeyId, long subKeyId) throws KeyNotFoundException {
|
||||
public static Passphrase getCachedPassphrase(Context context, long masterKeyId, long subKeyId) throws KeyNotFoundException {
|
||||
Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphrase() for masterKeyId "
|
||||
+ masterKeyId + ", subKeyId " + subKeyId);
|
||||
|
||||
@ -190,7 +191,9 @@ public class PassphraseCacheService extends Service {
|
||||
|
||||
switch (returnMessage.what) {
|
||||
case MSG_PASSPHRASE_CACHE_GET_OKAY:
|
||||
return returnMessage.getData().getString(EXTRA_PASSPHRASE);
|
||||
Bundle returnData = returnMessage.getData();
|
||||
returnData.setClassLoader(context.getClassLoader());
|
||||
return returnData.getParcelable(EXTRA_PASSPHRASE);
|
||||
case MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND:
|
||||
throw new KeyNotFoundException();
|
||||
default:
|
||||
@ -202,11 +205,11 @@ public class PassphraseCacheService extends Service {
|
||||
/**
|
||||
* Internal implementation to get cached passphrase.
|
||||
*/
|
||||
private String getCachedPassphraseImpl(long masterKeyId, long subKeyId) throws ProviderHelper.NotFoundException {
|
||||
private Passphrase getCachedPassphraseImpl(long masterKeyId, long subKeyId) throws ProviderHelper.NotFoundException {
|
||||
// passphrase for symmetric encryption?
|
||||
if (masterKeyId == Constants.key.symmetric) {
|
||||
Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for symmetric encryption");
|
||||
String cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric).getPassphrase();
|
||||
Passphrase cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric).getPassphrase();
|
||||
if (cachedPassphrase == null) {
|
||||
return null;
|
||||
}
|
||||
@ -232,13 +235,13 @@ public class PassphraseCacheService extends Service {
|
||||
case DIVERT_TO_CARD:
|
||||
if (Preferences.getPreferences(this).useDefaultYubikeyPin()) {
|
||||
Log.d(Constants.TAG, "PassphraseCacheService: Using default Yubikey PIN: 123456");
|
||||
return "123456"; // default Yubikey PIN, see http://www.yubico.com/2012/12/yubikey-neo-openpgp/
|
||||
return new Passphrase("123456"); // default Yubikey PIN, see http://www.yubico.com/2012/12/yubikey-neo-openpgp/
|
||||
} else {
|
||||
Log.d(Constants.TAG, "PassphraseCacheService: NOT using default Yubikey PIN");
|
||||
break;
|
||||
}
|
||||
case PASSPHRASE_EMPTY:
|
||||
return "";
|
||||
return new Passphrase("");
|
||||
case UNAVAILABLE:
|
||||
throw new ProviderHelper.NotFoundException("secret key for this subkey is not available");
|
||||
case GNU_DUMMY:
|
||||
@ -331,7 +334,7 @@ public class PassphraseCacheService extends Service {
|
||||
long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
|
||||
long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, -1);
|
||||
|
||||
String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE);
|
||||
Passphrase passphrase = intent.getParcelableExtra(EXTRA_PASSPHRASE);
|
||||
String primaryUserID = intent.getStringExtra(EXTRA_USER_ID);
|
||||
|
||||
Log.d(Constants.TAG,
|
||||
@ -373,10 +376,10 @@ public class PassphraseCacheService extends Service {
|
||||
Log.e(Constants.TAG, "PassphraseCacheService: Bad request, missing masterKeyId or subKeyId!");
|
||||
msg.what = MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND;
|
||||
} else {
|
||||
String passphrase = getCachedPassphraseImpl(masterKeyId, subKeyId);
|
||||
Passphrase passphrase = getCachedPassphraseImpl(masterKeyId, subKeyId);
|
||||
msg.what = MSG_PASSPHRASE_CACHE_GET_OKAY;
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(EXTRA_PASSPHRASE, passphrase);
|
||||
bundle.putParcelable(EXTRA_PASSPHRASE, passphrase);
|
||||
msg.setData(bundle);
|
||||
}
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
@ -412,7 +415,10 @@ public class PassphraseCacheService extends Service {
|
||||
* Called when one specific passphrase for keyId timed out
|
||||
*/
|
||||
private void timeout(Context context, long keyId) {
|
||||
// remove passphrase corresponding to keyId from memory
|
||||
CachedPassphrase cPass = mPassphraseCache.get(keyId);
|
||||
// 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!");
|
||||
@ -517,9 +523,9 @@ public class PassphraseCacheService extends Service {
|
||||
|
||||
public class CachedPassphrase {
|
||||
private String primaryUserID;
|
||||
private String passphrase;
|
||||
private Passphrase passphrase;
|
||||
|
||||
public CachedPassphrase(String passphrase, String primaryUserID) {
|
||||
public CachedPassphrase(Passphrase passphrase, String primaryUserID) {
|
||||
setPassphrase(passphrase);
|
||||
setPrimaryUserID(primaryUserID);
|
||||
}
|
||||
@ -528,7 +534,7 @@ public class PassphraseCacheService extends Service {
|
||||
return primaryUserID;
|
||||
}
|
||||
|
||||
public String getPassphrase() {
|
||||
public Passphrase getPassphrase() {
|
||||
return passphrase;
|
||||
}
|
||||
|
||||
@ -536,7 +542,7 @@ public class PassphraseCacheService extends Service {
|
||||
this.primaryUserID = primaryUserID;
|
||||
}
|
||||
|
||||
public void setPassphrase(String passphrase) {
|
||||
public void setPassphrase(Passphrase passphrase) {
|
||||
this.passphrase = passphrase;
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
@ -296,33 +297,30 @@ public class SaveKeyringParcel implements Parcelable {
|
||||
public static class ChangeUnlockParcel implements Parcelable {
|
||||
|
||||
// The new passphrase to use
|
||||
public final String mNewPassphrase;
|
||||
public final Passphrase mNewPassphrase;
|
||||
// A new pin to use. Must only contain [0-9]+
|
||||
public final String mNewPin;
|
||||
public final Passphrase mNewPin;
|
||||
|
||||
public ChangeUnlockParcel(String newPassphrase) {
|
||||
public ChangeUnlockParcel(Passphrase newPassphrase) {
|
||||
this(newPassphrase, null);
|
||||
}
|
||||
public ChangeUnlockParcel(String newPassphrase, String newPin) {
|
||||
public ChangeUnlockParcel(Passphrase newPassphrase, Passphrase newPin) {
|
||||
if (newPassphrase == null && newPin == null) {
|
||||
throw new RuntimeException("Cannot set both passphrase and pin. THIS IS A BUG!");
|
||||
}
|
||||
if (newPin != null && !newPin.matches("[0-9]+")) {
|
||||
throw new RuntimeException("Pin must be numeric digits only. THIS IS A BUG!");
|
||||
}
|
||||
mNewPassphrase = newPassphrase;
|
||||
mNewPin = newPin;
|
||||
}
|
||||
|
||||
public ChangeUnlockParcel(Parcel source) {
|
||||
mNewPassphrase = source.readString();
|
||||
mNewPin = source.readString();
|
||||
mNewPassphrase = source.readParcelable(Passphrase.class.getClassLoader());
|
||||
mNewPin = source.readParcelable(Passphrase.class.getClassLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel destination, int flags) {
|
||||
destination.writeString(mNewPassphrase);
|
||||
destination.writeString(mNewPin);
|
||||
destination.writeParcelable(mNewPassphrase, flags);
|
||||
destination.writeParcelable(mNewPin, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -62,6 +62,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner;
|
||||
import org.sufficientlysecure.keychain.ui.widget.KeySpinner;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
@ -168,8 +169,8 @@ public class CertifyKeyFragment extends LoaderFragment
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mSignMasterKeyId == Constants.key.none) {
|
||||
Notify.showNotify(getActivity(), getString(R.string.select_key_to_certify),
|
||||
Notify.Style.ERROR);
|
||||
Notify.create(getActivity(), getString(R.string.select_key_to_certify),
|
||||
Notify.Style.ERROR).show();
|
||||
} else {
|
||||
initiateCertifying();
|
||||
}
|
||||
@ -246,14 +247,14 @@ public class CertifyKeyFragment extends LoaderFragment
|
||||
while (!data.isAfterLast()) {
|
||||
long masterKeyId = data.getLong(INDEX_MASTER_KEY_ID);
|
||||
String userId = data.getString(INDEX_USER_ID);
|
||||
String[] pieces = KeyRing.splitUserId(userId);
|
||||
KeyRing.UserId pieces = KeyRing.splitUserId(userId);
|
||||
|
||||
// Two cases:
|
||||
|
||||
boolean grouped = masterKeyId == lastMasterKeyId;
|
||||
boolean subGrouped = data.isFirst() || grouped && lastName.equals(pieces[0]);
|
||||
boolean subGrouped = data.isFirst() || grouped && lastName.equals(pieces.name);
|
||||
// Remember for next loop
|
||||
lastName = pieces[0];
|
||||
lastName = pieces.name;
|
||||
|
||||
Log.d(Constants.TAG, Long.toString(masterKeyId, 16) + (grouped ? "grouped" : "not grouped"));
|
||||
|
||||
@ -318,7 +319,7 @@ public class CertifyKeyFragment extends LoaderFragment
|
||||
*/
|
||||
private void initiateCertifying() {
|
||||
// get the user's passphrase for this key (if required)
|
||||
String passphrase;
|
||||
Passphrase passphrase;
|
||||
try {
|
||||
passphrase = PassphraseCacheService.getCachedPassphrase(getActivity(), mSignMasterKeyId, mSignMasterKeyId);
|
||||
} catch (PassphraseCacheService.KeyNotFoundException e) {
|
||||
@ -341,7 +342,6 @@ public class CertifyKeyFragment extends LoaderFragment
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_PASSPHRASE: {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
String passphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
|
||||
startCertifying();
|
||||
}
|
||||
return;
|
||||
@ -360,8 +360,8 @@ public class CertifyKeyFragment extends LoaderFragment
|
||||
// Bail out if there is not at least one user id selected
|
||||
ArrayList<CertifyAction> certifyActions = mUserIdsAdapter.getSelectedCertifyActions();
|
||||
if (certifyActions.isEmpty()) {
|
||||
Notify.showNotify(getActivity(), "No identities selected!",
|
||||
Notify.Style.ERROR);
|
||||
Notify.create(getActivity(), "No identities selected!",
|
||||
Notify.Style.ERROR).show();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -20,31 +20,74 @@ package org.sufficientlysecure.keychain.ui;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.view.View;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class CreateKeyActivity extends BaseActivity {
|
||||
|
||||
public static final String EXTRA_NAME = "name";
|
||||
public static final String EXTRA_EMAIL = "email";
|
||||
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 enum FragAction {
|
||||
START,
|
||||
TO_RIGHT,
|
||||
TO_LEFT
|
||||
}
|
||||
public static final String FRAGMENT_TAG = "currentFragment";
|
||||
|
||||
String mName;
|
||||
String mEmail;
|
||||
ArrayList<String> mAdditionalEmails;
|
||||
Passphrase mPassphrase;
|
||||
boolean mFirstTime;
|
||||
|
||||
Fragment mCurrentFragment;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// pass extras into fragment
|
||||
CreateKeyNameFragment frag =
|
||||
CreateKeyNameFragment.newInstance(
|
||||
getIntent().getStringExtra(EXTRA_NAME),
|
||||
getIntent().getStringExtra(EXTRA_EMAIL)
|
||||
);
|
||||
loadFragment(null, frag, FragAction.START);
|
||||
// Check whether we're recreating a previously destroyed instance
|
||||
if (savedInstanceState != null) {
|
||||
// Restore value of members from saved state
|
||||
mName = savedInstanceState.getString(EXTRA_NAME);
|
||||
mEmail = savedInstanceState.getString(EXTRA_EMAIL);
|
||||
mAdditionalEmails = savedInstanceState.getStringArrayList(EXTRA_ADDITIONAL_EMAILS);
|
||||
mPassphrase = savedInstanceState.getParcelable(EXTRA_PASSPHRASE);
|
||||
mFirstTime = savedInstanceState.getBoolean(EXTRA_FIRST_TIME);
|
||||
|
||||
mCurrentFragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG);
|
||||
} else {
|
||||
// Initialize members with default values for a new instance
|
||||
mName = getIntent().getStringExtra(EXTRA_NAME);
|
||||
mEmail = getIntent().getStringExtra(EXTRA_EMAIL);
|
||||
mFirstTime = getIntent().getBooleanExtra(EXTRA_FIRST_TIME, false);
|
||||
|
||||
// Start with first fragment of wizard
|
||||
CreateKeyStartFragment frag = CreateKeyStartFragment.newInstance();
|
||||
loadFragment(frag, FragAction.START);
|
||||
}
|
||||
|
||||
if (mFirstTime) {
|
||||
setTitle(R.string.app_name);
|
||||
setActionBarIcon(R.drawable.ic_launcher);
|
||||
mToolbar.setNavigationOnClickListener(null);
|
||||
} else {
|
||||
setTitle(R.string.title_manage_my_keys);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
outState.putString(EXTRA_NAME, mName);
|
||||
outState.putString(EXTRA_EMAIL, mEmail);
|
||||
outState.putStringArrayList(EXTRA_ADDITIONAL_EMAILS, mAdditionalEmails);
|
||||
outState.putParcelable(EXTRA_PASSPHRASE, mPassphrase);
|
||||
outState.putBoolean(EXTRA_FIRST_TIME, mFirstTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -52,23 +95,23 @@ public class CreateKeyActivity extends BaseActivity {
|
||||
setContentView(R.layout.create_key_activity);
|
||||
}
|
||||
|
||||
public void loadFragment(Bundle savedInstanceState, Fragment fragment, FragAction action) {
|
||||
// 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 static enum FragAction {
|
||||
START,
|
||||
TO_RIGHT,
|
||||
TO_LEFT
|
||||
}
|
||||
|
||||
public void loadFragment(Fragment fragment, FragAction action) {
|
||||
mCurrentFragment = fragment;
|
||||
|
||||
// Add the fragment to the 'fragment_container' FrameLayout
|
||||
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
|
||||
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
|
||||
|
||||
switch (action) {
|
||||
case START:
|
||||
transaction.setCustomAnimations(0, 0);
|
||||
transaction.replace(R.id.create_key_fragment_container, fragment)
|
||||
.commitAllowingStateLoss();
|
||||
transaction.replace(R.id.create_key_fragment_container, fragment, FRAGMENT_TAG)
|
||||
.commit();
|
||||
break;
|
||||
case TO_LEFT:
|
||||
getSupportFragmentManager().popBackStackImmediate();
|
||||
@ -77,8 +120,8 @@ public class CreateKeyActivity extends BaseActivity {
|
||||
transaction.setCustomAnimations(R.anim.frag_slide_in_from_right, R.anim.frag_slide_out_to_left,
|
||||
R.anim.frag_slide_in_from_left, R.anim.frag_slide_out_to_right);
|
||||
transaction.addToBackStack(null);
|
||||
transaction.replace(R.id.create_key_fragment_container, fragment)
|
||||
.commitAllowingStateLoss();
|
||||
transaction.replace(R.id.create_key_fragment_container, fragment, FRAGMENT_TAG)
|
||||
.commit();
|
||||
break;
|
||||
|
||||
}
|
||||
|
@ -46,16 +46,12 @@ import java.util.List;
|
||||
|
||||
public class CreateKeyEmailFragment extends Fragment {
|
||||
|
||||
public static final String ARG_NAME = "name";
|
||||
public static final String ARG_EMAIL = "email";
|
||||
|
||||
CreateKeyActivity mCreateKeyActivity;
|
||||
EmailEditText mEmailEdit;
|
||||
RecyclerView mEmailsRecyclerView;
|
||||
View mBackButton;
|
||||
View mNextButton;
|
||||
|
||||
String mName;
|
||||
ArrayList<EmailAdapter.ViewModel> mAdditionalEmailModels;
|
||||
|
||||
EmailAdapter mEmailAdapter;
|
||||
@ -63,13 +59,10 @@ public class CreateKeyEmailFragment extends Fragment {
|
||||
/**
|
||||
* Creates new instance of this fragment
|
||||
*/
|
||||
public static CreateKeyEmailFragment newInstance(String name, String email) {
|
||||
public static CreateKeyEmailFragment newInstance() {
|
||||
CreateKeyEmailFragment frag = new CreateKeyEmailFragment();
|
||||
|
||||
Bundle args = new Bundle();
|
||||
args.putString(ARG_NAME, name);
|
||||
args.putString(ARG_EMAIL, email);
|
||||
|
||||
frag.setArguments(args);
|
||||
|
||||
return frag;
|
||||
@ -85,7 +78,7 @@ public class CreateKeyEmailFragment extends Fragment {
|
||||
*/
|
||||
private static boolean isEditTextNotEmpty(Context context, EditText editText) {
|
||||
boolean output = true;
|
||||
if (editText.getText().toString().length() == 0) {
|
||||
if (editText.getText().length() == 0) {
|
||||
editText.setError(context.getString(R.string.create_key_empty));
|
||||
editText.requestFocus();
|
||||
output = false;
|
||||
@ -106,31 +99,33 @@ public class CreateKeyEmailFragment extends Fragment {
|
||||
mEmailsRecyclerView = (RecyclerView) view.findViewById(R.id.create_key_emails);
|
||||
|
||||
// initial values
|
||||
mName = getArguments().getString(ARG_NAME);
|
||||
String email = getArguments().getString(ARG_EMAIL);
|
||||
mEmailEdit.setText(email);
|
||||
mEmailEdit.setText(mCreateKeyActivity.mEmail);
|
||||
|
||||
// focus empty edit fields
|
||||
if (email == null) {
|
||||
if (mCreateKeyActivity.mEmail == null) {
|
||||
mEmailEdit.requestFocus();
|
||||
}
|
||||
mBackButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mCreateKeyActivity.loadFragment(null, null, FragAction.TO_LEFT);
|
||||
mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
|
||||
}
|
||||
});
|
||||
mNextButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
createKeyCheck();
|
||||
nextClicked();
|
||||
}
|
||||
});
|
||||
mEmailsRecyclerView.setHasFixedSize(true);
|
||||
mEmailsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
mEmailsRecyclerView.setItemAnimator(new DefaultItemAnimator());
|
||||
|
||||
// initial values
|
||||
mAdditionalEmailModels = new ArrayList<>();
|
||||
if (mCreateKeyActivity.mAdditionalEmails != null) {
|
||||
setAdditionalEmails(mCreateKeyActivity.mAdditionalEmails);
|
||||
}
|
||||
mEmailAdapter = new EmailAdapter(mAdditionalEmailModels, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -171,25 +166,38 @@ public class CreateKeyEmailFragment extends Fragment {
|
||||
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
||||
}
|
||||
|
||||
private void createKeyCheck() {
|
||||
private void nextClicked() {
|
||||
if (isEditTextNotEmpty(getActivity(), mEmailEdit)) {
|
||||
// save state
|
||||
mCreateKeyActivity.mEmail = mEmailEdit.getText().toString();
|
||||
mCreateKeyActivity.mAdditionalEmails = getAdditionalEmails();
|
||||
|
||||
ArrayList<String> emails = new ArrayList<>();
|
||||
for (EmailAdapter.ViewModel holder : mAdditionalEmailModels) {
|
||||
emails.add(holder.toString());
|
||||
}
|
||||
|
||||
CreateKeyPassphraseFragment frag =
|
||||
CreateKeyPassphraseFragment.newInstance(
|
||||
mName,
|
||||
mEmailEdit.getText().toString(),
|
||||
emails
|
||||
);
|
||||
|
||||
mCreateKeyActivity.loadFragment(null, frag, FragAction.TO_RIGHT);
|
||||
CreateKeyPassphraseFragment frag = CreateKeyPassphraseFragment.newInstance();
|
||||
mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<String> getAdditionalEmails() {
|
||||
ArrayList<String> emails = new ArrayList<>();
|
||||
for (EmailAdapter.ViewModel holder : mAdditionalEmailModels) {
|
||||
emails.add(holder.toString());
|
||||
}
|
||||
return emails;
|
||||
}
|
||||
|
||||
private void setAdditionalEmails(ArrayList<String> emails) {
|
||||
for (String email : emails) {
|
||||
mAdditionalEmailModels.add(new EmailAdapter.ViewModel(email));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
// save state in activity
|
||||
mCreateKeyActivity.mAdditionalEmails = getAdditionalEmails();
|
||||
}
|
||||
|
||||
public static class EmailAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
private List<ViewModel> mDataset;
|
||||
private View.OnClickListener mFooterOnClickListener;
|
||||
|
@ -45,6 +45,7 @@ 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.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -64,32 +65,15 @@ public class CreateKeyFinalFragment extends Fragment {
|
||||
TextView mEditText;
|
||||
View mEditButton;
|
||||
|
||||
public static final String ARG_NAME = "name";
|
||||
public static final String ARG_EMAIL = "email";
|
||||
public static final String ARG_ADDITIONAL_EMAILS = "emails";
|
||||
public static final String ARG_PASSPHRASE = "passphrase";
|
||||
|
||||
String mName;
|
||||
String mEmail;
|
||||
ArrayList<String> mAdditionalEmails;
|
||||
String mPassphrase;
|
||||
|
||||
SaveKeyringParcel mSaveKeyringParcel;
|
||||
|
||||
/**
|
||||
* Creates new instance of this fragment
|
||||
*/
|
||||
public static CreateKeyFinalFragment newInstance(String name, String email,
|
||||
ArrayList<String> additionalEmails,
|
||||
String passphrase) {
|
||||
public static CreateKeyFinalFragment newInstance() {
|
||||
CreateKeyFinalFragment frag = new CreateKeyFinalFragment();
|
||||
|
||||
Bundle args = new Bundle();
|
||||
args.putString(ARG_NAME, name);
|
||||
args.putString(ARG_EMAIL, email);
|
||||
args.putStringArrayList(ARG_ADDITIONAL_EMAILS, additionalEmails);
|
||||
args.putString(ARG_PASSPHRASE, passphrase);
|
||||
|
||||
frag.setArguments(args);
|
||||
|
||||
return frag;
|
||||
@ -107,17 +91,11 @@ public class CreateKeyFinalFragment extends Fragment {
|
||||
mEditText = (TextView) view.findViewById(R.id.create_key_edit_text);
|
||||
mEditButton = view.findViewById(R.id.create_key_edit_button);
|
||||
|
||||
// get args
|
||||
mName = getArguments().getString(ARG_NAME);
|
||||
mEmail = getArguments().getString(ARG_EMAIL);
|
||||
mAdditionalEmails = getArguments().getStringArrayList(ARG_ADDITIONAL_EMAILS);
|
||||
mPassphrase = getArguments().getString(ARG_PASSPHRASE);
|
||||
|
||||
// set values
|
||||
mNameEdit.setText(mName);
|
||||
if (mAdditionalEmails != null && mAdditionalEmails.size() > 0) {
|
||||
String emailText = mEmail + ", ";
|
||||
Iterator<?> it = mAdditionalEmails.iterator();
|
||||
mNameEdit.setText(mCreateKeyActivity.mName);
|
||||
if (mCreateKeyActivity.mAdditionalEmails != null && mCreateKeyActivity.mAdditionalEmails.size() > 0) {
|
||||
String emailText = mCreateKeyActivity.mEmail + ", ";
|
||||
Iterator<?> it = mCreateKeyActivity.mAdditionalEmails.iterator();
|
||||
while (it.hasNext()) {
|
||||
Object next = it.next();
|
||||
emailText += next;
|
||||
@ -127,7 +105,7 @@ public class CreateKeyFinalFragment extends Fragment {
|
||||
}
|
||||
mEmailEdit.setText(emailText);
|
||||
} else {
|
||||
mEmailEdit.setText(mEmail);
|
||||
mEmailEdit.setText(mCreateKeyActivity.mEmail);
|
||||
}
|
||||
|
||||
mCreateButton.setOnClickListener(new View.OnClickListener() {
|
||||
@ -140,7 +118,7 @@ public class CreateKeyFinalFragment extends Fragment {
|
||||
mBackButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mCreateKeyActivity.loadFragment(null, null, FragAction.TO_LEFT);
|
||||
mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
|
||||
}
|
||||
});
|
||||
|
||||
@ -156,6 +134,12 @@ public class CreateKeyFinalFragment extends Fragment {
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
switch (requestCode) {
|
||||
@ -186,17 +170,22 @@ public class CreateKeyFinalFragment extends Fragment {
|
||||
Algorithm.RSA, 4096, null, KeyFlags.SIGN_DATA, 0L));
|
||||
mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||
Algorithm.RSA, 4096, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L));
|
||||
String userId = KeyRing.createUserId(mName, mEmail, null);
|
||||
String userId = KeyRing.createUserId(
|
||||
new KeyRing.UserId(mCreateKeyActivity.mName, mCreateKeyActivity.mEmail, null)
|
||||
);
|
||||
mSaveKeyringParcel.mAddUserIds.add(userId);
|
||||
mSaveKeyringParcel.mChangePrimaryUserId = userId;
|
||||
if (mAdditionalEmails != null && mAdditionalEmails.size() > 0) {
|
||||
for (String email : mAdditionalEmails) {
|
||||
String thisUserId = KeyRing.createUserId(mName, email, null);
|
||||
if (mCreateKeyActivity.mAdditionalEmails != null
|
||||
&& mCreateKeyActivity.mAdditionalEmails.size() > 0) {
|
||||
for (String email : mCreateKeyActivity.mAdditionalEmails) {
|
||||
String thisUserId = KeyRing.createUserId(
|
||||
new KeyRing.UserId(mCreateKeyActivity.mName, email, null)
|
||||
);
|
||||
mSaveKeyringParcel.mAddUserIds.add(thisUserId);
|
||||
}
|
||||
}
|
||||
mSaveKeyringParcel.mNewUnlock = mPassphrase != null
|
||||
? new ChangeUnlockParcel(mPassphrase, null)
|
||||
mSaveKeyringParcel.mNewUnlock = mCreateKeyActivity.mPassphrase != null
|
||||
? new ChangeUnlockParcel(mCreateKeyActivity.mPassphrase, null)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
@ -288,8 +277,8 @@ public class CreateKeyFinalFragment extends Fragment {
|
||||
// TODO: upload operation needs a result!
|
||||
// TODO: then combine these results
|
||||
//if (result.getResult() == OperationResultParcel.RESULT_OK) {
|
||||
//Notify.showNotify(getActivity(), R.string.key_send_success,
|
||||
//Notify.Style.INFO);
|
||||
//Notify.create(getActivity(), R.string.key_send_success,
|
||||
//Notify.Style.OK).show();
|
||||
|
||||
Intent data = new Intent();
|
||||
data.putExtra(OperationResult.EXTRA_RESULT, saveKeyResult);
|
||||
|
@ -24,34 +24,26 @@ 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;
|
||||
import org.sufficientlysecure.keychain.ui.widget.EmailEditText;
|
||||
import org.sufficientlysecure.keychain.ui.widget.NameEditText;
|
||||
|
||||
public class CreateKeyNameFragment extends Fragment {
|
||||
|
||||
public static final String ARG_NAME = "name";
|
||||
public static final String ARG_EMAIL = "email";
|
||||
|
||||
CreateKeyActivity mCreateKeyActivity;
|
||||
NameEditText mNameEdit;
|
||||
View mBackButton;
|
||||
View mNextButton;
|
||||
|
||||
String mEmail;
|
||||
|
||||
/**
|
||||
* Creates new instance of this fragment
|
||||
*/
|
||||
public static CreateKeyNameFragment newInstance(String name, String email) {
|
||||
public static CreateKeyNameFragment newInstance() {
|
||||
CreateKeyNameFragment frag = new CreateKeyNameFragment();
|
||||
|
||||
Bundle args = new Bundle();
|
||||
args.putString(ARG_NAME, name);
|
||||
args.putString(ARG_EMAIL, email);
|
||||
|
||||
frag.setArguments(args);
|
||||
|
||||
@ -68,7 +60,7 @@ public class CreateKeyNameFragment extends Fragment {
|
||||
*/
|
||||
private static boolean isEditTextNotEmpty(Context context, EditText editText) {
|
||||
boolean output = true;
|
||||
if (editText.getText().toString().length() == 0) {
|
||||
if (editText.getText().length() == 0) {
|
||||
editText.setError(context.getString(R.string.create_key_empty));
|
||||
editText.requestFocus();
|
||||
output = false;
|
||||
@ -79,39 +71,31 @@ public class CreateKeyNameFragment extends Fragment {
|
||||
return output;
|
||||
}
|
||||
|
||||
private static boolean areEditTextsEqual(Context context, EditText editText1, EditText editText2) {
|
||||
boolean output = true;
|
||||
if (!editText1.getText().toString().equals(editText2.getText().toString())) {
|
||||
editText2.setError(context.getString(R.string.create_key_passphrases_not_equal));
|
||||
editText2.requestFocus();
|
||||
output = false;
|
||||
} else {
|
||||
editText2.setError(null);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.create_key_name_fragment, container, false);
|
||||
|
||||
mNameEdit = (NameEditText) view.findViewById(R.id.create_key_name);
|
||||
mBackButton = view.findViewById(R.id.create_key_back_button);
|
||||
mNextButton = view.findViewById(R.id.create_key_next_button);
|
||||
|
||||
// initial values
|
||||
String name = getArguments().getString(ARG_NAME);
|
||||
mEmail = getArguments().getString(ARG_EMAIL);
|
||||
mNameEdit.setText(name);
|
||||
mNameEdit.setText(mCreateKeyActivity.mName);
|
||||
|
||||
// focus empty edit fields
|
||||
if (name == null) {
|
||||
if (mCreateKeyActivity.mName == null) {
|
||||
mNameEdit.requestFocus();
|
||||
}
|
||||
mBackButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
|
||||
}
|
||||
});
|
||||
mNextButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
createKeyCheck();
|
||||
nextClicked();
|
||||
}
|
||||
});
|
||||
|
||||
@ -124,16 +108,13 @@ public class CreateKeyNameFragment extends Fragment {
|
||||
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
||||
}
|
||||
|
||||
private void createKeyCheck() {
|
||||
private void nextClicked() {
|
||||
if (isEditTextNotEmpty(getActivity(), mNameEdit)) {
|
||||
// save state
|
||||
mCreateKeyActivity.mName = mNameEdit.getText().toString();
|
||||
|
||||
CreateKeyEmailFragment frag =
|
||||
CreateKeyEmailFragment.newInstance(
|
||||
mNameEdit.getText().toString(),
|
||||
mEmail
|
||||
);
|
||||
|
||||
mCreateKeyActivity.loadFragment(null, frag, FragAction.TO_RIGHT);
|
||||
CreateKeyEmailFragment frag = CreateKeyEmailFragment.newInstance();
|
||||
mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.text.Editable;
|
||||
import android.text.method.HideReturnsTransformationMethod;
|
||||
import android.text.method.PasswordTransformationMethod;
|
||||
import android.view.LayoutInflater;
|
||||
@ -34,20 +35,13 @@ import android.widget.EditText;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
|
||||
import org.sufficientlysecure.keychain.ui.widget.PassphraseEditText;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class CreateKeyPassphraseFragment extends Fragment {
|
||||
|
||||
public static final String ARG_NAME = "name";
|
||||
public static final String ARG_EMAIL = "email";
|
||||
public static final String ARG_ADDITIONAL_EMAILS = "emails";
|
||||
|
||||
// model
|
||||
String mName;
|
||||
String mEmail;
|
||||
ArrayList<String> mAdditionalEmails;
|
||||
|
||||
// view
|
||||
CreateKeyActivity mCreateKeyActivity;
|
||||
PassphraseEditText mPassphraseEdit;
|
||||
@ -59,15 +53,10 @@ public class CreateKeyPassphraseFragment extends Fragment {
|
||||
/**
|
||||
* Creates new instance of this fragment
|
||||
*/
|
||||
public static CreateKeyPassphraseFragment newInstance(String name, String email,
|
||||
ArrayList<String> additionalEmails) {
|
||||
public static CreateKeyPassphraseFragment newInstance() {
|
||||
CreateKeyPassphraseFragment frag = new CreateKeyPassphraseFragment();
|
||||
|
||||
Bundle args = new Bundle();
|
||||
args.putString(ARG_NAME, name);
|
||||
args.putString(ARG_EMAIL, email);
|
||||
args.putStringArrayList(ARG_ADDITIONAL_EMAILS, additionalEmails);
|
||||
|
||||
frag.setArguments(args);
|
||||
|
||||
return frag;
|
||||
@ -83,7 +72,7 @@ public class CreateKeyPassphraseFragment extends Fragment {
|
||||
*/
|
||||
private static boolean isEditTextNotEmpty(Context context, EditText editText) {
|
||||
boolean output = true;
|
||||
if (editText.getText().toString().length() == 0) {
|
||||
if (editText.getText().length() == 0) {
|
||||
editText.setError(context.getString(R.string.create_key_empty));
|
||||
editText.requestFocus();
|
||||
output = false;
|
||||
@ -95,11 +84,13 @@ public class CreateKeyPassphraseFragment extends Fragment {
|
||||
}
|
||||
|
||||
private static boolean areEditTextsEqual(Context context, EditText editText1, EditText editText2) {
|
||||
boolean output = true;
|
||||
if (!editText1.getText().toString().equals(editText2.getText().toString())) {
|
||||
Passphrase p1 = new Passphrase(editText1);
|
||||
Passphrase p2 = new Passphrase(editText2);
|
||||
boolean output = (p1.equals(p2));
|
||||
|
||||
if (!output) {
|
||||
editText2.setError(context.getString(R.string.create_key_passphrases_not_equal));
|
||||
editText2.requestFocus();
|
||||
output = false;
|
||||
} else {
|
||||
editText2.setError(null);
|
||||
}
|
||||
@ -118,9 +109,12 @@ public class CreateKeyPassphraseFragment extends Fragment {
|
||||
mNextButton = view.findViewById(R.id.create_key_next_button);
|
||||
|
||||
// initial values
|
||||
mName = getArguments().getString(ARG_NAME);
|
||||
mEmail = getArguments().getString(ARG_EMAIL);
|
||||
mAdditionalEmails = getArguments().getStringArrayList(ARG_ADDITIONAL_EMAILS);
|
||||
// TODO: using String here is unsafe...
|
||||
if (mCreateKeyActivity.mPassphrase != null) {
|
||||
mPassphraseEdit.setText(Arrays.toString(mCreateKeyActivity.mPassphrase.getCharArray()));
|
||||
mPassphraseEditAgain.setText(Arrays.toString(mCreateKeyActivity.mPassphrase.getCharArray()));
|
||||
}
|
||||
|
||||
mPassphraseEdit.requestFocus();
|
||||
mBackButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
@ -131,7 +125,7 @@ public class CreateKeyPassphraseFragment extends Fragment {
|
||||
mNextButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
createKeyCheck();
|
||||
nextClicked();
|
||||
}
|
||||
});
|
||||
mShowPassphrase.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@ -159,23 +153,19 @@ public class CreateKeyPassphraseFragment extends Fragment {
|
||||
|
||||
private void back() {
|
||||
hideKeyboard();
|
||||
mCreateKeyActivity.loadFragment(null, null, FragAction.TO_LEFT);
|
||||
mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
|
||||
}
|
||||
|
||||
private void createKeyCheck() {
|
||||
private void nextClicked() {
|
||||
if (isEditTextNotEmpty(getActivity(), mPassphraseEdit)
|
||||
&& areEditTextsEqual(getActivity(), mPassphraseEdit, mPassphraseEditAgain)) {
|
||||
|
||||
CreateKeyFinalFragment frag =
|
||||
CreateKeyFinalFragment.newInstance(
|
||||
mName,
|
||||
mEmail,
|
||||
mAdditionalEmails,
|
||||
mPassphraseEdit.getText().toString()
|
||||
);
|
||||
// save state
|
||||
mCreateKeyActivity.mPassphrase = new Passphrase(mPassphraseEdit);
|
||||
|
||||
CreateKeyFinalFragment frag = CreateKeyFinalFragment.newInstance();
|
||||
hideKeyboard();
|
||||
mCreateKeyActivity.loadFragment(null, frag, FragAction.TO_RIGHT);
|
||||
mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.widget.DefaultItemAnimator;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
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.widget.EmailEditText;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CreateKeyStartFragment extends Fragment {
|
||||
|
||||
CreateKeyActivity mCreateKeyActivity;
|
||||
|
||||
View mCreateKey;
|
||||
View mImportKey;
|
||||
View mYubiKey;
|
||||
TextView mCancel;
|
||||
public static final int REQUEST_CODE_CREATE_OR_IMPORT_KEY = 0x00007012;
|
||||
|
||||
/**
|
||||
* Creates new instance of this fragment
|
||||
*/
|
||||
public static CreateKeyStartFragment newInstance() {
|
||||
CreateKeyStartFragment frag = new CreateKeyStartFragment();
|
||||
|
||||
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_key_start_fragment, container, false);
|
||||
|
||||
mCreateKey = view.findViewById(R.id.create_key_create_key_button);
|
||||
mImportKey = view.findViewById(R.id.create_key_import_button);
|
||||
// mYubiKey = view.findViewById(R.id.create_key_yubikey_button);
|
||||
mCancel = (TextView) view.findViewById(R.id.create_key_cancel);
|
||||
|
||||
if (mCreateKeyActivity.mFirstTime) {
|
||||
mCancel.setText(R.string.first_time_skip);
|
||||
} else {
|
||||
mCancel.setText(R.string.btn_do_not_save);
|
||||
}
|
||||
|
||||
mCreateKey.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
CreateKeyNameFragment frag = CreateKeyNameFragment.newInstance();
|
||||
mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
|
||||
}
|
||||
});
|
||||
|
||||
mImportKey.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(mCreateKeyActivity, ImportKeysActivity.class);
|
||||
intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN);
|
||||
startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY);
|
||||
}
|
||||
});
|
||||
|
||||
mCancel.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finishSetup(null);
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
private void finishSetup(Intent srcData) {
|
||||
if (mCreateKeyActivity.mFirstTime) {
|
||||
Preferences prefs = Preferences.getPreferences(mCreateKeyActivity);
|
||||
prefs.setFirstTime(false);
|
||||
}
|
||||
Intent intent = new Intent(mCreateKeyActivity, MainActivity.class);
|
||||
// give intent through to display notify
|
||||
if (srcData != null) {
|
||||
intent.putExtras(srcData);
|
||||
}
|
||||
startActivity(intent);
|
||||
mCreateKeyActivity.finish();
|
||||
}
|
||||
|
||||
// workaround for https://code.google.com/p/android/issues/detail?id=61394
|
||||
// @Override
|
||||
// public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
// return keyCode == KeyEvent.KEYCODE_MENU || super.onKeyDown(keyCode, event);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (requestCode == REQUEST_CODE_CREATE_OR_IMPORT_KEY) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
finishSetup(data);
|
||||
}
|
||||
} else {
|
||||
Log.e(Constants.TAG, "No valid request code!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
mCreateKeyActivity = (CreateKeyActivity) getActivity();
|
||||
}
|
||||
|
||||
}
|
@ -139,7 +139,7 @@ public class DecryptFilesFragment extends DecryptFragment {
|
||||
|
||||
private void decryptAction() {
|
||||
if (mInputUri == null) {
|
||||
Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR);
|
||||
Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR).show();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -147,7 +147,9 @@ public class DecryptFilesFragment extends DecryptFragment {
|
||||
}
|
||||
|
||||
private String removeEncryptedAppend(String name) {
|
||||
if (name.endsWith(".asc") || name.endsWith(".gpg") || name.endsWith(".pgp")) {
|
||||
if (name.endsWith(Constants.FILE_EXTENSION_ASC)
|
||||
|| name.endsWith(Constants.FILE_EXTENSION_PGP_MAIN)
|
||||
|| name.endsWith(Constants.FILE_EXTENSION_PGP_ALTERNATE)) {
|
||||
return name.substring(0, name.length() - 4);
|
||||
}
|
||||
return name;
|
||||
@ -189,7 +191,7 @@ public class DecryptFilesFragment extends DecryptFragment {
|
||||
data.putInt(KeychainIntentService.TARGET, IOType.URI.ordinal());
|
||||
data.putParcelable(KeychainIntentService.ENCRYPT_DECRYPT_OUTPUT_URI, mOutputUri);
|
||||
|
||||
data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase);
|
||||
data.putParcelable(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase);
|
||||
data.putByteArray(KeychainIntentService.DECRYPT_NFC_DECRYPTED_SESSION_KEY, mNfcDecryptedSessionKey);
|
||||
|
||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||
@ -263,7 +265,7 @@ public class DecryptFilesFragment extends DecryptFragment {
|
||||
data.putInt(KeychainIntentService.TARGET, IOType.URI.ordinal());
|
||||
data.putParcelable(KeychainIntentService.ENCRYPT_DECRYPT_OUTPUT_URI, mOutputUri);
|
||||
|
||||
data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase);
|
||||
data.putParcelable(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase);
|
||||
data.putByteArray(KeychainIntentService.DECRYPT_NFC_DECRYPTED_SESSION_KEY, mNfcDecryptedSessionKey);
|
||||
|
||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||
@ -339,7 +341,7 @@ public class DecryptFilesFragment extends DecryptFragment {
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_PASSPHRASE: {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
mPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
|
||||
mPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
|
||||
decryptOriginalFilename();
|
||||
}
|
||||
return;
|
||||
|
@ -32,6 +32,7 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
public abstract class DecryptFragment extends Fragment {
|
||||
private static final int RESULT_CODE_LOOKUP_KEY = 0x00007006;
|
||||
@ -57,7 +58,7 @@ public abstract class DecryptFragment extends Fragment {
|
||||
|
||||
|
||||
// State
|
||||
protected String mPassphrase;
|
||||
protected Passphrase mPassphrase;
|
||||
protected byte[] mNfcDecryptedSessionKey;
|
||||
|
||||
@Override
|
||||
@ -100,7 +101,7 @@ public abstract class DecryptFragment extends Fragment {
|
||||
startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
|
||||
}
|
||||
|
||||
protected void startNfcDecrypt(long subKeyId, String pin, byte[] encryptedSessionKey) {
|
||||
protected void startNfcDecrypt(long subKeyId, Passphrase pin, byte[] encryptedSessionKey) {
|
||||
// build PendingIntent for Yubikey NFC operations
|
||||
Intent intent = new Intent(getActivity(), NfcActivity.class);
|
||||
intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY);
|
||||
@ -128,14 +129,14 @@ public abstract class DecryptFragment extends Fragment {
|
||||
mSignatureKeyId = signatureResult.getKeyId();
|
||||
|
||||
String userId = signatureResult.getPrimaryUserId();
|
||||
String[] userIdSplit = KeyRing.splitUserId(userId);
|
||||
if (userIdSplit[0] != null) {
|
||||
mSignatureName.setText(userIdSplit[0]);
|
||||
KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId);
|
||||
if (userIdSplit.name != null) {
|
||||
mSignatureName.setText(userIdSplit.name);
|
||||
} else {
|
||||
mSignatureName.setText(R.string.user_id_no_name);
|
||||
}
|
||||
if (userIdSplit[1] != null) {
|
||||
mSignatureEmail.setText(userIdSplit[1]);
|
||||
if (userIdSplit.email != null) {
|
||||
mSignatureEmail.setText(userIdSplit.email);
|
||||
} else {
|
||||
mSignatureEmail.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix(getActivity(), mSignatureKeyId));
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ public class DecryptTextActivity extends BaseActivity {
|
||||
if (sharedText != null) {
|
||||
loadFragment(savedInstanceState, sharedText);
|
||||
} else {
|
||||
Notify.showNotify(this, R.string.error_invalid_data, Notify.Style.ERROR);
|
||||
Notify.create(this, R.string.error_invalid_data, Notify.Style.ERROR).show();
|
||||
}
|
||||
} else {
|
||||
Log.e(Constants.TAG, "ACTION_SEND received non-plaintext, this should not happen in this activity!");
|
||||
@ -175,7 +175,7 @@ public class DecryptTextActivity extends BaseActivity {
|
||||
if (extraText != null) {
|
||||
loadFragment(savedInstanceState, extraText);
|
||||
} else {
|
||||
Notify.showNotify(this, R.string.error_invalid_data, Notify.Style.ERROR);
|
||||
Notify.create(this, R.string.error_invalid_data, Notify.Style.ERROR).show();
|
||||
}
|
||||
} else if (ACTION_DECRYPT_FROM_CLIPBOARD.equals(action)) {
|
||||
Log.d(Constants.TAG, "ACTION_DECRYPT_FROM_CLIPBOARD");
|
||||
|
@ -132,7 +132,7 @@ public class DecryptTextFragment extends DecryptFragment {
|
||||
|
||||
private void copyToClipboard(String text) {
|
||||
ClipboardReflection.copyToClipboard(getActivity(), text);
|
||||
Notify.showNotify(getActivity(), R.string.text_copied_to_clipboard, Notify.Style.INFO);
|
||||
Notify.create(getActivity(), R.string.text_copied_to_clipboard, Notify.Style.OK).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -161,7 +161,7 @@ public class DecryptTextFragment extends DecryptFragment {
|
||||
// data
|
||||
data.putInt(KeychainIntentService.TARGET, IOType.BYTES.ordinal());
|
||||
data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, mCiphertext.getBytes());
|
||||
data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase);
|
||||
data.putParcelable(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase);
|
||||
data.putByteArray(KeychainIntentService.DECRYPT_NFC_DECRYPTED_SESSION_KEY, mNfcDecryptedSessionKey);
|
||||
|
||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||
@ -247,7 +247,7 @@ public class DecryptTextFragment extends DecryptFragment {
|
||||
|
||||
case REQUEST_CODE_PASSPHRASE: {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
mPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
|
||||
mPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
|
||||
decryptStart();
|
||||
} else {
|
||||
getActivity().finish();
|
||||
|
@ -67,6 +67,7 @@ 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;
|
||||
|
||||
public class EditKeyFragment extends LoaderFragment implements
|
||||
LoaderManager.LoaderCallbacks<Cursor> {
|
||||
@ -100,7 +101,7 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
private SaveKeyringParcel mSaveKeyringParcel;
|
||||
|
||||
private String mPrimaryUserId;
|
||||
private String mCurrentPassphrase;
|
||||
private Passphrase mCurrentPassphrase;
|
||||
|
||||
/**
|
||||
* Creates new instance of this fragment
|
||||
@ -267,7 +268,7 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_PASSPHRASE: {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
mCurrentPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
|
||||
mCurrentPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
|
||||
// Prepare the loaders. Either re-connect with an existing ones,
|
||||
// or start new ones.
|
||||
getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, EditKeyFragment.this);
|
||||
@ -386,7 +387,7 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
|
||||
// cache new returned passphrase!
|
||||
mSaveKeyringParcel.mNewUnlock = new ChangeUnlockParcel(
|
||||
data.getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE),
|
||||
(Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE),
|
||||
null
|
||||
);
|
||||
}
|
||||
@ -545,7 +546,7 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
Messenger messenger = new Messenger(returnHandler);
|
||||
|
||||
// pre-fill out primary name
|
||||
String predefinedName = KeyRing.splitUserId(mPrimaryUserId)[0];
|
||||
String predefinedName = KeyRing.splitUserId(mPrimaryUserId).name;
|
||||
AddUserIdDialogFragment addUserIdDialog = AddUserIdDialogFragment.newInstance(messenger,
|
||||
predefinedName);
|
||||
|
||||
@ -576,11 +577,11 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
|
||||
private void returnKeyringParcel() {
|
||||
if (mSaveKeyringParcel.mAddUserIds.size() == 0) {
|
||||
Notify.showNotify(getActivity(), R.string.edit_key_error_add_identity, Notify.Style.ERROR);
|
||||
Notify.create(getActivity(), R.string.edit_key_error_add_identity, Notify.Style.ERROR).show();
|
||||
return;
|
||||
}
|
||||
if (mSaveKeyringParcel.mAddSubKeys.size() == 0) {
|
||||
Notify.showNotify(getActivity(), R.string.edit_key_error_add_subkey, Notify.Style.ERROR);
|
||||
Notify.create(getActivity(), R.string.edit_key_error_add_subkey, Notify.Style.ERROR).show();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -593,7 +594,7 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
getActivity().finish();
|
||||
}
|
||||
|
||||
private void saveInDatabase(String passphrase) {
|
||||
private void saveInDatabase(Passphrase passphrase) {
|
||||
Log.d(Constants.TAG, "mSaveKeyringParcel:\n" + mSaveKeyringParcel.toString());
|
||||
|
||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
|
||||
@ -640,7 +641,7 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase);
|
||||
data.putParcelable(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase);
|
||||
data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, mSaveKeyringParcel);
|
||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||
|
||||
|
@ -32,6 +32,7 @@ import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
|
||||
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@ -41,7 +42,7 @@ public abstract class EncryptActivity extends BaseActivity {
|
||||
public static final int REQUEST_CODE_NFC = 0x00008002;
|
||||
|
||||
// For NFC data
|
||||
protected String mSigningKeyPassphrase = null;
|
||||
protected Passphrase mSigningKeyPassphrase = null;
|
||||
protected Date mNfcTimestamp = null;
|
||||
protected byte[] mNfcHash = null;
|
||||
|
||||
@ -64,7 +65,7 @@ public abstract class EncryptActivity extends BaseActivity {
|
||||
startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
|
||||
}
|
||||
|
||||
protected void startNfcSign(long keyId, String pin, byte[] hashToSign, int hashAlgo) {
|
||||
protected void startNfcSign(long keyId, Passphrase pin, byte[] hashToSign, int hashAlgo) {
|
||||
// build PendingIntent for Yubikey NFC operations
|
||||
Intent intent = new Intent(this, NfcActivity.class);
|
||||
intent.setAction(NfcActivity.ACTION_SIGN_HASH);
|
||||
@ -84,7 +85,7 @@ public abstract class EncryptActivity extends BaseActivity {
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_PASSPHRASE: {
|
||||
if (resultCode == RESULT_OK && data != null) {
|
||||
mSigningKeyPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
|
||||
mSigningKeyPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
|
||||
startEncrypt();
|
||||
return;
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public interface EncryptActivityInterface {
|
||||
@ -29,6 +31,8 @@ public interface EncryptActivityInterface {
|
||||
|
||||
public boolean isUseArmor();
|
||||
public boolean isUseCompression();
|
||||
public boolean isEncryptFilenames();
|
||||
public boolean isHiddenRecipients();
|
||||
|
||||
public long getSignatureKey();
|
||||
public long[] getEncryptionKeys();
|
||||
@ -37,7 +41,7 @@ public interface EncryptActivityInterface {
|
||||
public void setEncryptionKeys(long[] encryptionKeys);
|
||||
public void setEncryptionUsers(String[] encryptionUsers);
|
||||
|
||||
public void setPassphrase(String passphrase);
|
||||
public void setPassphrase(Passphrase passphrase);
|
||||
|
||||
// ArrayList on purpose as only those are parcelable
|
||||
public ArrayList<Uri> getInputUris();
|
||||
|
@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
import org.sufficientlysecure.keychain.util.ShareHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -62,14 +63,18 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
|
||||
private static final int MODE_SYMMETRIC = 1;
|
||||
|
||||
// model used by fragments
|
||||
private long mEncryptionKeyIds[] = null;
|
||||
private String mEncryptionUserIds[] = null;
|
||||
private long mSigningKeyId = Constants.key.none;
|
||||
private String mPassphrase = "";
|
||||
private boolean mUseArmor = false;
|
||||
private boolean mUseCompression = true;
|
||||
private boolean mDeleteAfterEncrypt = false;
|
||||
private boolean mShareAfterEncrypt = false;
|
||||
private boolean mEncryptFilenames = true;
|
||||
private boolean mHiddenRecipients = false;
|
||||
|
||||
private long mEncryptionKeyIds[] = null;
|
||||
private String mEncryptionUserIds[] = null;
|
||||
private long mSigningKeyId = Constants.key.none;
|
||||
private Passphrase mPassphrase = new Passphrase();
|
||||
|
||||
private ArrayList<Uri> mInputUris;
|
||||
private ArrayList<Uri> mOutputUris;
|
||||
private String mMessage = "";
|
||||
@ -88,6 +93,16 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
|
||||
return mUseCompression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEncryptFilenames() {
|
||||
return mEncryptFilenames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHiddenRecipients() {
|
||||
return mHiddenRecipients;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSignatureKey() {
|
||||
return mSigningKeyId;
|
||||
@ -122,7 +137,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPassphrase(String passphrase) {
|
||||
public void setPassphrase(Passphrase passphrase) {
|
||||
mPassphrase = passphrase;
|
||||
}
|
||||
|
||||
@ -222,14 +237,15 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
|
||||
} else {
|
||||
data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED);
|
||||
}
|
||||
data.setHiddenRecipients(mHiddenRecipients);
|
||||
data.setEnableAsciiArmorOutput(mUseArmor);
|
||||
data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
|
||||
data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
|
||||
|
||||
if (isModeSymmetric()) {
|
||||
Log.d(Constants.TAG, "Symmetric encryption enabled!");
|
||||
String passphrase = mPassphrase;
|
||||
if (passphrase.length() == 0) {
|
||||
Passphrase passphrase = mPassphrase;
|
||||
if (passphrase.isEmpty()) {
|
||||
passphrase = null;
|
||||
}
|
||||
data.setSymmetricPassphrase(passphrase);
|
||||
@ -268,14 +284,14 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
|
||||
sendIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
|
||||
sendIntent.putExtra(Intent.EXTRA_STREAM, mOutputUris);
|
||||
}
|
||||
sendIntent.setType("application/octet-stream");
|
||||
sendIntent.setType(Constants.ENCRYPTED_FILES_MIME);
|
||||
|
||||
if (!isModeSymmetric() && mEncryptionUserIds != null) {
|
||||
Set<String> users = new HashSet<>();
|
||||
for (String user : mEncryptionUserIds) {
|
||||
String[] userId = KeyRing.splitUserId(user);
|
||||
if (userId[1] != null) {
|
||||
users.add(userId[1]);
|
||||
KeyRing.UserId userId = KeyRing.splitUserId(user);
|
||||
if (userId.email != null) {
|
||||
users.add(userId.email);
|
||||
}
|
||||
}
|
||||
sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()]));
|
||||
@ -287,7 +303,8 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
|
||||
// file checks
|
||||
|
||||
if (mInputUris.isEmpty()) {
|
||||
Notify.showNotify(this, R.string.no_file_selected, Notify.Style.ERROR);
|
||||
Notify.create(this, R.string.no_file_selected, Notify.Style.ERROR)
|
||||
.show(getSupportFragmentManager().findFragmentById(R.id.encrypt_file_fragment));
|
||||
return false;
|
||||
} else if (mInputUris.size() > 1 && !mShareAfterEncrypt) {
|
||||
// This should be impossible...
|
||||
@ -301,11 +318,13 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
|
||||
// symmetric encryption checks
|
||||
|
||||
if (mPassphrase == null) {
|
||||
Notify.showNotify(this, R.string.passphrases_do_not_match, Notify.Style.ERROR);
|
||||
Notify.create(this, R.string.passphrases_do_not_match, Notify.Style.ERROR)
|
||||
.show(getSupportFragmentManager().findFragmentById(R.id.encrypt_file_fragment));
|
||||
return false;
|
||||
}
|
||||
if (mPassphrase.isEmpty()) {
|
||||
Notify.showNotify(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR);
|
||||
Notify.create(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR)
|
||||
.show(getSupportFragmentManager().findFragmentById(R.id.encrypt_file_fragment));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -317,7 +336,8 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
|
||||
|
||||
// Files must be encrypted, only text can be signed-only right now
|
||||
if (!gotEncryptionKeys) {
|
||||
Notify.showNotify(this, R.string.select_encryption_key, Notify.Style.ERROR);
|
||||
Notify.create(this, R.string.select_encryption_key, Notify.Style.ERROR)
|
||||
.show(getSupportFragmentManager().findFragmentById(R.id.encrypt_file_fragment));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -371,6 +391,16 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
|
||||
notifyUpdate();
|
||||
break;
|
||||
}
|
||||
case R.id.check_encrypt_filenames: {
|
||||
mEncryptFilenames = item.isChecked();
|
||||
notifyUpdate();
|
||||
break;
|
||||
}
|
||||
// case R.id.check_hidden_recipients: {
|
||||
// mHiddenRecipients = item.isChecked();
|
||||
// notifyUpdate();
|
||||
// break;
|
||||
// }
|
||||
default: {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
@ -115,9 +115,9 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt
|
||||
}
|
||||
|
||||
if (mEncryptInterface.getInputUris().contains(inputUri)) {
|
||||
Notify.showNotify(getActivity(),
|
||||
Notify.create(getActivity(),
|
||||
getActivity().getString(R.string.error_file_added_already, FileHelper.getFilename(getActivity(), inputUri)),
|
||||
Notify.Style.ERROR);
|
||||
Notify.Style.ERROR).show(this);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -137,36 +137,39 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
Uri inputUri = mEncryptInterface.getInputUris().get(0);
|
||||
String targetName =
|
||||
(mEncryptInterface.isEncryptFilenames() ? "1" : FileHelper.getFilename(getActivity(), inputUri))
|
||||
+ (mEncryptInterface.isUseArmor() ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
File file = new File(inputUri.getPath());
|
||||
File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR;
|
||||
String targetName = FileHelper.getFilename(getActivity(), inputUri) +
|
||||
(mEncryptInterface.isUseArmor() ? ".asc" : ".gpg");
|
||||
File targetFile = new File(parentDir, targetName);
|
||||
FileHelper.saveFile(this, getString(R.string.title_encrypt_to_file),
|
||||
getString(R.string.specify_file_to_encrypt_to), targetFile, REQUEST_CODE_OUTPUT);
|
||||
} else {
|
||||
FileHelper.saveDocument(this, "*/*", FileHelper.getFilename(getActivity(), inputUri) +
|
||||
(mEncryptInterface.isUseArmor() ? ".asc" : ".gpg"), REQUEST_CODE_OUTPUT);
|
||||
FileHelper.saveDocument(this, "*/*", targetName, REQUEST_CODE_OUTPUT);
|
||||
}
|
||||
}
|
||||
|
||||
private void encryptClicked(boolean share) {
|
||||
if (mEncryptInterface.getInputUris().isEmpty()) {
|
||||
Notify.showNotify(getActivity(), R.string.error_no_file_selected, Notify.Style.ERROR);
|
||||
Notify.create(getActivity(), R.string.error_no_file_selected, Notify.Style.ERROR).show(this);
|
||||
return;
|
||||
}
|
||||
if (share) {
|
||||
mEncryptInterface.getOutputUris().clear();
|
||||
int filenameCounter = 1;
|
||||
for (Uri uri : mEncryptInterface.getInputUris()) {
|
||||
String targetName = FileHelper.getFilename(getActivity(), uri) +
|
||||
(mEncryptInterface.isUseArmor() ? ".asc" : ".gpg");
|
||||
String targetName =
|
||||
(mEncryptInterface.isEncryptFilenames() ? String.valueOf(filenameCounter) : FileHelper.getFilename(getActivity(), uri))
|
||||
+ (mEncryptInterface.isUseArmor() ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN);
|
||||
mEncryptInterface.getOutputUris().add(TemporaryStorageProvider.createFile(getActivity(), targetName));
|
||||
filenameCounter++;
|
||||
}
|
||||
mEncryptInterface.startEncrypt(true);
|
||||
} else {
|
||||
if (mEncryptInterface.getInputUris().size() > 1) {
|
||||
Notify.showNotify(getActivity(), R.string.error_multi_not_supported, Notify.Style.ERROR);
|
||||
Notify.create(getActivity(), R.string.error_multi_not_supported, Notify.Style.ERROR).show(this);
|
||||
return;
|
||||
}
|
||||
showOutputFileDialog();
|
||||
|
@ -28,6 +28,7 @@ import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
public class EncryptSymmetricFragment extends Fragment implements EncryptActivityInterface.UpdateListener {
|
||||
|
||||
@ -67,8 +68,13 @@ public class EncryptSymmetricFragment extends Fragment implements EncryptActivit
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
// update passphrase in EncryptActivity
|
||||
if (mPassphrase.getText().toString().equals(mPassphraseAgain.getText().toString())) {
|
||||
mEncryptInterface.setPassphrase(s.toString());
|
||||
Passphrase p1 = new Passphrase(mPassphrase.getText());
|
||||
Passphrase p2 = new Passphrase(mPassphraseAgain.getText());
|
||||
boolean passesEquals = (p1.equals(p2));
|
||||
p1.removeFromMemory();
|
||||
p2.removeFromMemory();
|
||||
if (passesEquals) {
|
||||
mEncryptInterface.setPassphrase(new Passphrase(mPassphrase.getText()));
|
||||
} else {
|
||||
mEncryptInterface.setPassphrase(null);
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ import org.sufficientlysecure.keychain.pgp.PgpConstants;
|
||||
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
import org.sufficientlysecure.keychain.util.ShareHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -63,16 +63,19 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
|
||||
private static final int MODE_SYMMETRIC = 1;
|
||||
|
||||
// model used by fragments
|
||||
private boolean mShareAfterEncrypt = false;
|
||||
private boolean mUseCompression = true;
|
||||
private boolean mHiddenRecipients = false;
|
||||
|
||||
private long mEncryptionKeyIds[] = null;
|
||||
private String mEncryptionUserIds[] = null;
|
||||
// TODO Constants.key.none? What's wrong with a null value?
|
||||
private long mSigningKeyId = Constants.key.none;
|
||||
private String mPassphrase = "";
|
||||
private boolean mShareAfterEncrypt = false;
|
||||
private Passphrase mPassphrase = new Passphrase();
|
||||
|
||||
private ArrayList<Uri> mInputUris;
|
||||
private ArrayList<Uri> mOutputUris;
|
||||
private String mMessage = "";
|
||||
private boolean mUseCompression = true;
|
||||
|
||||
public boolean isModeSymmetric() {
|
||||
return MODE_SYMMETRIC == mCurrentMode;
|
||||
@ -83,11 +86,21 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEncryptFilenames() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUseCompression() {
|
||||
return mUseCompression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHiddenRecipients() {
|
||||
return mHiddenRecipients;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSignatureKey() {
|
||||
return mSigningKeyId;
|
||||
@ -122,7 +135,8 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPassphrase(String passphrase) {
|
||||
public void setPassphrase(Passphrase passphrase) {
|
||||
mPassphrase.removeFromMemory();
|
||||
mPassphrase = passphrase;
|
||||
}
|
||||
|
||||
@ -184,8 +198,9 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
|
||||
// Copy to clipboard
|
||||
copyToClipboard(result.getResultBytes());
|
||||
result.createNotify(EncryptTextActivity.this).show();
|
||||
// Notify.showNotify(EncryptTextActivity.this,
|
||||
// R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO);
|
||||
// Notify.create(EncryptTextActivity.this,
|
||||
// R.string.encrypt_sign_clipboard_successful, Notify.Style.OK)
|
||||
// .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment));
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,6 +217,7 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
|
||||
} else {
|
||||
data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED);
|
||||
}
|
||||
data.setHiddenRecipients(mHiddenRecipients);
|
||||
data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
|
||||
data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
|
||||
|
||||
@ -210,8 +226,8 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
|
||||
|
||||
if (isModeSymmetric()) {
|
||||
Log.d(Constants.TAG, "Symmetric encryption enabled!");
|
||||
String passphrase = mPassphrase;
|
||||
if (passphrase.length() == 0) {
|
||||
Passphrase passphrase = mPassphrase;
|
||||
if (passphrase.isEmpty()) {
|
||||
passphrase = null;
|
||||
}
|
||||
data.setSymmetricPassphrase(passphrase);
|
||||
@ -247,15 +263,15 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
|
||||
private Intent createSendIntent(byte[] resultBytes) {
|
||||
Intent sendIntent;
|
||||
sendIntent = new Intent(Intent.ACTION_SEND);
|
||||
sendIntent.setType("text/plain");
|
||||
sendIntent.setType(Constants.ENCRYPTED_TEXT_MIME);
|
||||
sendIntent.putExtra(Intent.EXTRA_TEXT, new String(resultBytes));
|
||||
|
||||
if (!isModeSymmetric() && mEncryptionUserIds != null) {
|
||||
Set<String> users = new HashSet<>();
|
||||
for (String user : mEncryptionUserIds) {
|
||||
String[] userId = KeyRing.splitUserId(user);
|
||||
if (userId[1] != null) {
|
||||
users.add(userId[1]);
|
||||
KeyRing.UserId userId = KeyRing.splitUserId(user);
|
||||
if (userId.email != null) {
|
||||
users.add(userId.email);
|
||||
}
|
||||
}
|
||||
// pass trough email addresses as extra for email applications
|
||||
@ -266,7 +282,8 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
|
||||
|
||||
protected boolean inputIsValid() {
|
||||
if (mMessage == null) {
|
||||
Notify.showNotify(this, R.string.error_message, Notify.Style.ERROR);
|
||||
Notify.create(this, R.string.error_message, Notify.Style.ERROR)
|
||||
.show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -274,11 +291,13 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
|
||||
// symmetric encryption checks
|
||||
|
||||
if (mPassphrase == null) {
|
||||
Notify.showNotify(this, R.string.passphrases_do_not_match, Notify.Style.ERROR);
|
||||
Notify.create(this, R.string.passphrases_do_not_match, Notify.Style.ERROR)
|
||||
.show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment));
|
||||
return false;
|
||||
}
|
||||
if (mPassphrase.isEmpty()) {
|
||||
Notify.showNotify(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR);
|
||||
Notify.create(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR)
|
||||
.show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -289,7 +308,8 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
|
||||
&& mEncryptionKeyIds.length > 0);
|
||||
|
||||
if (!gotEncryptionKeys && mSigningKeyId == 0) {
|
||||
Notify.showNotify(this, R.string.select_encryption_or_signature_key, Notify.Style.ERROR);
|
||||
Notify.create(this, R.string.select_encryption_or_signature_key, Notify.Style.ERROR)
|
||||
.show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -353,6 +373,11 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
|
||||
notifyUpdate();
|
||||
break;
|
||||
}
|
||||
// case R.id.check_hidden_recipients: {
|
||||
// mHiddenRecipients = item.isChecked();
|
||||
// notifyUpdate();
|
||||
// break;
|
||||
// }
|
||||
default: {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
public class FirstTimeActivity extends BaseActivity {
|
||||
|
||||
View mCreateKey;
|
||||
View mImportKey;
|
||||
View mSkipSetup;
|
||||
|
||||
public static final int REQUEST_CODE_CREATE_OR_IMPORT_KEY = 0x00007012;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mCreateKey = findViewById(R.id.first_time_create_key);
|
||||
mImportKey = findViewById(R.id.first_time_import_key);
|
||||
mSkipSetup = findViewById(R.id.first_time_cancel);
|
||||
|
||||
mSkipSetup.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finishSetup(null);
|
||||
}
|
||||
});
|
||||
|
||||
mImportKey.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(FirstTimeActivity.this, ImportKeysActivity.class);
|
||||
intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN);
|
||||
startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY);
|
||||
}
|
||||
});
|
||||
|
||||
mCreateKey.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(FirstTimeActivity.this, CreateKeyActivity.class);
|
||||
startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initLayout() {
|
||||
setContentView(R.layout.first_time_activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (requestCode == REQUEST_CODE_CREATE_OR_IMPORT_KEY) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
finishSetup(data);
|
||||
}
|
||||
} else {
|
||||
Log.e(Constants.TAG, "No valid request code!");
|
||||
}
|
||||
}
|
||||
|
||||
private void finishSetup(Intent srcData) {
|
||||
Preferences prefs = Preferences.getPreferences(this);
|
||||
prefs.setFirstTime(false);
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
// give intent through to display notify
|
||||
if (srcData != null) {
|
||||
intent.putExtras(srcData);
|
||||
}
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
// workaround for https://code.google.com/p/android/issues/detail?id=61394
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
return keyCode == KeyEvent.KEYCODE_MENU || super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
}
|
@ -26,6 +26,7 @@ import android.os.Messenger;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
@ -277,7 +278,8 @@ public class ImportKeysActivity extends BaseActivity {
|
||||
|
||||
private boolean isFingerprintValid(String fingerprint) {
|
||||
if (fingerprint == null || fingerprint.length() < 40) {
|
||||
Notify.showNotify(this, R.string.import_qr_code_too_short_fingerprint, Notify.Style.ERROR);
|
||||
Notify.create(this, R.string.import_qr_code_too_short_fingerprint, Notify.Style.ERROR)
|
||||
.show((ViewGroup) findViewById(R.id.import_snackbar));
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
@ -329,7 +331,8 @@ public class ImportKeysActivity extends BaseActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
result.createNotify(ImportKeysActivity.this).show();
|
||||
result.createNotify(ImportKeysActivity.this)
|
||||
.show((ViewGroup) findViewById(R.id.import_snackbar));
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -372,7 +375,8 @@ public class ImportKeysActivity extends BaseActivity {
|
||||
startService(intent);
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "Problem writing cache file", e);
|
||||
Notify.showNotify(this, "Problem writing cache file!", Notify.Style.ERROR);
|
||||
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;
|
||||
@ -412,7 +416,8 @@ public class ImportKeysActivity extends BaseActivity {
|
||||
// start service with intent
|
||||
startService(intent);
|
||||
} else {
|
||||
Notify.showNotify(this, R.string.error_nothing_import, Notify.Style.ERROR);
|
||||
Notify.create(this, R.string.error_nothing_import, Notify.Style.ERROR)
|
||||
.show((ViewGroup) findViewById(R.id.import_snackbar));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,7 +154,7 @@ public class ImportKeysProxyActivity extends FragmentActivity {
|
||||
String fingerprint = null;
|
||||
|
||||
// example: openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282
|
||||
if (uri.getScheme().toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) {
|
||||
if (uri != null && uri.getScheme() != null && uri.getScheme().toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) {
|
||||
fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
|
@ -169,6 +169,22 @@ public class KeyListFragment extends LoaderFragment
|
||||
mStickyList.setDrawingListUnderStickyHeader(false);
|
||||
mStickyList.setFastScrollEnabled(true);
|
||||
|
||||
// Adds an empty footer view so that the Floating Action Button won't block content
|
||||
// in last few rows.
|
||||
View footer = new View(getActivity());
|
||||
|
||||
int spacing = (int) android.util.TypedValue.applyDimension(
|
||||
android.util.TypedValue.COMPLEX_UNIT_DIP, 72, getResources().getDisplayMetrics()
|
||||
);
|
||||
|
||||
android.widget.AbsListView.LayoutParams params = new android.widget.AbsListView.LayoutParams(
|
||||
android.widget.AbsListView.LayoutParams.MATCH_PARENT,
|
||||
spacing
|
||||
);
|
||||
|
||||
footer.setLayoutParams(params);
|
||||
mStickyList.addFooterView(footer, null, false);
|
||||
|
||||
/*
|
||||
* Multi-selection
|
||||
*/
|
||||
@ -369,13 +385,13 @@ public class KeyListFragment extends LoaderFragment
|
||||
/**
|
||||
* Show dialog to delete key
|
||||
*
|
||||
* @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not
|
||||
* @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not
|
||||
*/
|
||||
public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) {
|
||||
// Can only work on singular secret keys
|
||||
if (hasSecret && masterKeyIds.length > 1) {
|
||||
Notify.showNotify(getActivity(), R.string.secret_cannot_multiple,
|
||||
Notify.Style.ERROR);
|
||||
Notify.create(getActivity(), R.string.secret_cannot_multiple,
|
||||
Notify.Style.ERROR).show();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -468,28 +484,29 @@ public class KeyListFragment extends LoaderFragment
|
||||
case R.id.menu_key_list_debug_read:
|
||||
try {
|
||||
KeychainDatabase.debugBackup(getActivity(), true);
|
||||
Notify.showNotify(getActivity(), "Restored debug_backup.db", Notify.Style.INFO);
|
||||
Notify.create(getActivity(), "Restored debug_backup.db", Notify.Style.OK).show();
|
||||
getActivity().getContentResolver().notifyChange(KeychainContract.KeyRings.CONTENT_URI, null);
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "IO Error", e);
|
||||
Notify.showNotify(getActivity(), "IO Error " + e.getMessage(), Notify.Style.ERROR);
|
||||
Notify.create(getActivity(), "IO Error " + e.getMessage(), Notify.Style.ERROR).show();
|
||||
}
|
||||
return true;
|
||||
|
||||
case R.id.menu_key_list_debug_write:
|
||||
try {
|
||||
KeychainDatabase.debugBackup(getActivity(), false);
|
||||
Notify.showNotify(getActivity(), "Backup to debug_backup.db completed", Notify.Style.INFO);
|
||||
Notify.create(getActivity(), "Backup to debug_backup.db completed", Notify.Style.OK).show();
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "IO Error", e);
|
||||
Notify.showNotify(getActivity(), "IO Error: " + e.getMessage(), Notify.Style.ERROR);
|
||||
Notify.create(getActivity(), "IO Error: " + e.getMessage(), Notify.Style.ERROR).show();
|
||||
}
|
||||
return true;
|
||||
|
||||
case R.id.menu_key_list_debug_first_time:
|
||||
Preferences prefs = Preferences.getPreferences(getActivity());
|
||||
prefs.setFirstTime(true);
|
||||
Intent intent = new Intent(getActivity(), FirstTimeActivity.class);
|
||||
Intent intent = new Intent(getActivity(), CreateKeyActivity.class);
|
||||
intent.putExtra(CreateKeyActivity.EXTRA_FIRST_TIME, true);
|
||||
startActivity(intent);
|
||||
getActivity().finish();
|
||||
return true;
|
||||
@ -688,14 +705,14 @@ public class KeyListFragment extends LoaderFragment
|
||||
|
||||
{ // set name and stuff, common to both key types
|
||||
String userId = cursor.getString(INDEX_USER_ID);
|
||||
String[] userIdSplit = KeyRing.splitUserId(userId);
|
||||
if (userIdSplit[0] != null) {
|
||||
h.mMainUserId.setText(highlighter.highlight(userIdSplit[0]));
|
||||
KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId);
|
||||
if (userIdSplit.name != null) {
|
||||
h.mMainUserId.setText(highlighter.highlight(userIdSplit.name));
|
||||
} else {
|
||||
h.mMainUserId.setText(R.string.user_id_no_name);
|
||||
}
|
||||
if (userIdSplit[1] != null) {
|
||||
h.mMainUserIdRest.setText(highlighter.highlight(userIdSplit[1]));
|
||||
if (userIdSplit.email != null) {
|
||||
h.mMainUserIdRest.setText(highlighter.highlight(userIdSplit.email));
|
||||
h.mMainUserIdRest.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
h.mMainUserIdRest.setVisibility(View.GONE);
|
||||
@ -908,5 +925,4 @@ public class KeyListFragment extends LoaderFragment
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -54,7 +54,9 @@ public class MainActivity extends MaterialNavigationDrawer implements FabContain
|
||||
// if this is the first time show first time activity
|
||||
Preferences prefs = Preferences.getPreferences(this);
|
||||
if (prefs.isFirstTime()) {
|
||||
startActivity(new Intent(this, FirstTimeActivity.class));
|
||||
Intent intent = new Intent(this, CreateKeyActivity.class);
|
||||
intent.putExtra(CreateKeyActivity.EXTRA_FIRST_TIME, true);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
/**
|
||||
@ -212,9 +213,9 @@ public class PassphraseDialogActivity extends FragmentActivity {
|
||||
// the catch clause doesn't return.
|
||||
try {
|
||||
String mainUserId = mSecretRing.getPrimaryUserIdWithFallback();
|
||||
String[] mainUserIdSplit = KeyRing.splitUserId(mainUserId);
|
||||
if (mainUserIdSplit[0] != null) {
|
||||
userId = mainUserIdSplit[0];
|
||||
KeyRing.UserId mainUserIdSplit = KeyRing.splitUserId(mainUserId);
|
||||
if (mainUserIdSplit.name != null) {
|
||||
userId = mainUserIdSplit.name;
|
||||
} else {
|
||||
userId = getString(R.string.user_id_no_name);
|
||||
}
|
||||
@ -240,7 +241,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
|
||||
break;
|
||||
// special case: empty passphrase just returns the empty passphrase
|
||||
case PASSPHRASE_EMPTY:
|
||||
finishCaching("");
|
||||
finishCaching(new Passphrase(""));
|
||||
default:
|
||||
message = "This should not happen!";
|
||||
break;
|
||||
@ -322,7 +323,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
|
||||
positive.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final String passphrase = mPassphraseEditText.getText().toString();
|
||||
final Passphrase passphrase = new Passphrase(mPassphraseEditText);
|
||||
|
||||
// Early breakout if we are dealing with a symmetric key
|
||||
if (mSecretRing == null) {
|
||||
@ -399,7 +400,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
|
||||
});
|
||||
}
|
||||
|
||||
private void finishCaching(String passphrase) {
|
||||
private void finishCaching(Passphrase passphrase) {
|
||||
// any indication this isn't needed anymore, don't do it.
|
||||
if (mIsCancelled || getActivity() == null) {
|
||||
return;
|
||||
|
@ -80,7 +80,7 @@ public class QrCodeViewActivity extends BaseActivity {
|
||||
KeychainContract.KeyRings.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
|
||||
if (blob == null) {
|
||||
Log.e(Constants.TAG, "key not found!");
|
||||
Notify.showNotify(this, R.string.error_key_not_found, Style.ERROR);
|
||||
Notify.create(this, R.string.error_key_not_found, Style.ERROR).show();
|
||||
ActivityCompat.finishAfterTransition(QrCodeViewActivity.this);
|
||||
}
|
||||
|
||||
@ -102,7 +102,7 @@ public class QrCodeViewActivity extends BaseActivity {
|
||||
});
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
Log.e(Constants.TAG, "key not found!", e);
|
||||
Notify.showNotify(this, R.string.error_key_not_found, Style.ERROR);
|
||||
Notify.create(this, R.string.error_key_not_found, Style.ERROR).show();
|
||||
ActivityCompat.finishAfterTransition(QrCodeViewActivity.this);
|
||||
}
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ public class SafeSlingerActivity extends BaseActivity {
|
||||
activity.startService(intent);
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "Problem writing cache file", e);
|
||||
Notify.showNotify(activity, "Problem writing cache file!", Notify.Style.ERROR);
|
||||
Notify.create(activity, "Problem writing cache file!", Notify.Style.ERROR).show();
|
||||
}
|
||||
} else {
|
||||
// give everything else down to KeyListActivity!
|
||||
|
@ -21,18 +21,12 @@ package org.sufficientlysecure.keychain.ui;
|
||||
import android.animation.ArgbEvaluator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.nfc.NdefMessage;
|
||||
import android.nfc.NdefRecord;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.nfc.NfcEvent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@ -40,7 +34,6 @@ import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.Settings;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
@ -59,9 +52,7 @@ import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.getbase.floatingactionbutton.FloatingActionButton;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||
@ -76,6 +67,8 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||
import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler.MessageStatus;
|
||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
|
||||
@ -84,6 +77,7 @@ import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
|
||||
import org.sufficientlysecure.keychain.util.ContactHelper;
|
||||
import org.sufficientlysecure.keychain.util.ExportHelper;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.NfcHelper;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -93,8 +87,8 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
LoaderManager.LoaderCallbacks<Cursor> {
|
||||
|
||||
static final int REQUEST_QR_FINGERPRINT = 1;
|
||||
static final int REQUEST_DELETE= 2;
|
||||
static final int REQUEST_EXPORT= 3;
|
||||
static final int REQUEST_DELETE = 2;
|
||||
static final int REQUEST_EXPORT = 3;
|
||||
|
||||
ExportHelper mExportHelper;
|
||||
ProviderHelper mProviderHelper;
|
||||
@ -115,11 +109,7 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
private CardView mQrCodeLayout;
|
||||
|
||||
// NFC
|
||||
private NfcAdapter mNfcAdapter;
|
||||
private NfcAdapter.CreateNdefMessageCallback mNdefCallback;
|
||||
private NfcAdapter.OnNdefPushCompleteCallback mNdefCompleteCallback;
|
||||
private byte[] mNfcKeyringBytes;
|
||||
private static final int NFC_SENT = 1;
|
||||
private NfcHelper mNfcHelper;
|
||||
|
||||
private static final int LOADER_ID_UNIFIED = 0;
|
||||
|
||||
@ -256,7 +246,7 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
mActionNfc.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
invokeNfcBeam();
|
||||
mNfcHelper.invokeNfcBeam();
|
||||
}
|
||||
});
|
||||
|
||||
@ -264,7 +254,8 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
// or start new ones.
|
||||
getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
|
||||
|
||||
initNfc(mDataUri);
|
||||
mNfcHelper = new NfcHelper(this, mProviderHelper);
|
||||
mNfcHelper.initNfc(mDataUri);
|
||||
|
||||
}
|
||||
|
||||
@ -291,31 +282,31 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_export_file: {
|
||||
Intent mIntent = new Intent(this,PassphraseDialogActivity.class);
|
||||
long keyId=0;
|
||||
try {
|
||||
keyId = new ProviderHelper(this)
|
||||
.getCachedPublicKeyRing(mDataUri)
|
||||
.extractOrGetMasterKeyId();
|
||||
} catch (PgpKeyNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
if (PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId, mMasterKeyId) != null) {
|
||||
exportToFile(mDataUri, mExportHelper, mProviderHelper);
|
||||
return true;
|
||||
}
|
||||
|
||||
startPassphraseActivity(REQUEST_EXPORT);
|
||||
} catch (PassphraseCacheService.KeyNotFoundException e) {
|
||||
// This happens when the master key is stripped
|
||||
exportToFile(mDataUri, mExportHelper, mProviderHelper);
|
||||
}
|
||||
mIntent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID,keyId);
|
||||
startActivityForResult(mIntent,REQUEST_EXPORT);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_delete: {
|
||||
Intent mIntent = new Intent(this,PassphraseDialogActivity.class);
|
||||
long keyId=0;
|
||||
try {
|
||||
keyId = new ProviderHelper(this)
|
||||
.getCachedPublicKeyRing(mDataUri)
|
||||
.extractOrGetMasterKeyId();
|
||||
} catch (PgpKeyNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
if (PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId, mMasterKeyId) != null) {
|
||||
deleteKey();
|
||||
return true;
|
||||
}
|
||||
|
||||
startPassphraseActivity(REQUEST_DELETE);
|
||||
} catch (PassphraseCacheService.KeyNotFoundException e) {
|
||||
// This happens when the master key is stripped
|
||||
deleteKey();
|
||||
}
|
||||
mIntent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID,keyId);
|
||||
startActivityForResult(mIntent,REQUEST_DELETE);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_key_view_advanced: {
|
||||
@ -328,7 +319,7 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
try {
|
||||
updateFromKeyserver(mDataUri, mProviderHelper);
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR);
|
||||
Notify.create(this, R.string.error_key_not_found, Notify.Style.ERROR).show();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -364,41 +355,6 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
return true;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private void invokeNfcBeam() {
|
||||
// Check if device supports NFC
|
||||
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
|
||||
Notify.createNotify(this, R.string.no_nfc_support, Notify.LENGTH_LONG, Notify.Style.ERROR).show();
|
||||
return;
|
||||
}
|
||||
// Check for available NFC Adapter
|
||||
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
|
||||
if (mNfcAdapter == null || !mNfcAdapter.isEnabled()) {
|
||||
Notify.createNotify(this, R.string.error_nfc_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() {
|
||||
@Override
|
||||
public void onAction() {
|
||||
Intent intentSettings = new Intent(Settings.ACTION_NFC_SETTINGS);
|
||||
startActivity(intentSettings);
|
||||
}
|
||||
}, R.string.menu_nfc_preferences).show();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mNfcAdapter.isNdefPushEnabled()) {
|
||||
Notify.createNotify(this, R.string.error_beam_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() {
|
||||
@Override
|
||||
public void onAction() {
|
||||
Intent intentSettings = new Intent(Settings.ACTION_NFCSHARING_SETTINGS);
|
||||
startActivity(intentSettings);
|
||||
}
|
||||
}, R.string.menu_beam_preferences).show();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
mNfcAdapter.invokeBeam(this);
|
||||
}
|
||||
|
||||
private void scanQrCode() {
|
||||
Intent scanQrCode = new Intent(this, ImportKeysProxyActivity.class);
|
||||
@ -415,7 +371,7 @@ public class ViewKeyActivity extends BaseActivity 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);
|
||||
}
|
||||
@ -464,22 +420,32 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
ActivityCompat.startActivity(this, qrCodeIntent, opts);
|
||||
}
|
||||
|
||||
private void exportToFile(Uri dataUri, ExportHelper exportHelper, ProviderHelper providerHelper)
|
||||
throws ProviderHelper.NotFoundException {
|
||||
Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri);
|
||||
|
||||
HashMap<String, Object> data = providerHelper.getGenericData(
|
||||
baseUri,
|
||||
new String[]{KeychainContract.Keys.MASTER_KEY_ID, KeychainContract.KeyRings.HAS_SECRET},
|
||||
new int[]{ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_INTEGER});
|
||||
|
||||
exportHelper.showExportKeysDialog(
|
||||
new long[]{(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)},
|
||||
Constants.Path.APP_DIR_FILE, ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) != 0)
|
||||
);
|
||||
private void startPassphraseActivity(int requestCode) {
|
||||
Intent intent = new Intent(this, PassphraseDialogActivity.class);
|
||||
intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mMasterKeyId);
|
||||
startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
||||
private void deleteKey(Uri dataUri, ExportHelper exportHelper) {
|
||||
private void exportToFile(Uri dataUri, ExportHelper exportHelper, ProviderHelper providerHelper) {
|
||||
try {
|
||||
Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri);
|
||||
|
||||
HashMap<String, Object> data = providerHelper.getGenericData(
|
||||
baseUri,
|
||||
new String[] {KeychainContract.Keys.MASTER_KEY_ID, KeychainContract.KeyRings.HAS_SECRET},
|
||||
new int[] {ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_INTEGER});
|
||||
|
||||
exportHelper.showExportKeysDialog(
|
||||
new long[] {(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)},
|
||||
Constants.Path.APP_DIR_FILE, ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) != 0)
|
||||
);
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
Notify.create(this, R.string.error_key_not_found, Notify.Style.ERROR).show();
|
||||
Log.e(Constants.TAG, "Key not found", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteKey() {
|
||||
// Message is received after key is deleted
|
||||
Handler returnHandler = new Handler() {
|
||||
@Override
|
||||
@ -491,7 +457,11 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
}
|
||||
};
|
||||
|
||||
exportHelper.deleteKey(dataUri, returnHandler);
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(returnHandler);
|
||||
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
|
||||
new long[] {mMasterKeyId});
|
||||
deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -507,32 +477,27 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
|
||||
String fp = data.getStringExtra(ImportKeysProxyActivity.EXTRA_FINGERPRINT);
|
||||
if (fp == null) {
|
||||
Notify.createNotify(this, "Error scanning fingerprint!",
|
||||
Notify.create(this, "Error scanning fingerprint!",
|
||||
Notify.LENGTH_LONG, Notify.Style.ERROR).show();
|
||||
return;
|
||||
}
|
||||
if (mFingerprint.equalsIgnoreCase(fp)) {
|
||||
certifyImmediate();
|
||||
} else {
|
||||
Notify.createNotify(this, "Fingerprints did not match!",
|
||||
Notify.create(this, "Fingerprints did not match!",
|
||||
Notify.LENGTH_LONG, Notify.Style.ERROR).show();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (requestCode == REQUEST_DELETE && resultCode == Activity.RESULT_OK){
|
||||
deleteKey(mDataUri, mExportHelper);
|
||||
}
|
||||
if (requestCode == REQUEST_EXPORT && resultCode == Activity.RESULT_OK){
|
||||
try {
|
||||
exportToFile(mDataUri, mExportHelper, mProviderHelper);
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR);
|
||||
Log.e(Constants.TAG, "Key not found", e);
|
||||
}
|
||||
if (requestCode == REQUEST_DELETE && resultCode == Activity.RESULT_OK) {
|
||||
deleteKey();
|
||||
}
|
||||
|
||||
if (requestCode == REQUEST_EXPORT && resultCode == Activity.RESULT_OK) {
|
||||
exportToFile(mDataUri, mExportHelper, mProviderHelper);
|
||||
}
|
||||
|
||||
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
|
||||
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
|
||||
@ -545,14 +510,14 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
private void encrypt(Uri dataUri, boolean text) {
|
||||
// If there is no encryption key, don't bother.
|
||||
if (!mHasEncrypt) {
|
||||
Notify.showNotify(this, R.string.error_no_encrypt_subkey, Notify.Style.ERROR);
|
||||
Notify.create(this, R.string.error_no_encrypt_subkey, Notify.Style.ERROR).show();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
long keyId = new ProviderHelper(this)
|
||||
.getCachedPublicKeyRing(dataUri)
|
||||
.extractOrGetMasterKeyId();
|
||||
long[] encryptionKeyIds = new long[]{keyId};
|
||||
long[] encryptionKeyIds = new long[] {keyId};
|
||||
Intent intent;
|
||||
if (text) {
|
||||
intent = new Intent(this, EncryptTextActivity.class);
|
||||
@ -690,98 +655,9 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
loadTask.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* NFC: Initialize NFC sharing if OS and device supports it
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
private void initNfc(final Uri dataUri) {
|
||||
// check if NFC Beam is supported (>= Android 4.1)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
|
||||
// Implementation for the CreateNdefMessageCallback interface
|
||||
mNdefCallback = new NfcAdapter.CreateNdefMessageCallback() {
|
||||
@Override
|
||||
public NdefMessage createNdefMessage(NfcEvent event) {
|
||||
/*
|
||||
* When a device receives a push with an AAR in it, the application specified in the AAR is
|
||||
* guaranteed to run. The AAR overrides the tag dispatch system. You can add it back in to
|
||||
* guarantee that this activity starts when receiving a beamed message. For now, this code
|
||||
* uses the tag dispatch system.
|
||||
*/
|
||||
return new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME,
|
||||
mNfcKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME));
|
||||
}
|
||||
};
|
||||
|
||||
// Implementation for the OnNdefPushCompleteCallback interface
|
||||
mNdefCompleteCallback = new NfcAdapter.OnNdefPushCompleteCallback() {
|
||||
@Override
|
||||
public void onNdefPushComplete(NfcEvent event) {
|
||||
// A handler is needed to send messages to the activity when this
|
||||
// callback occurs, because it happens from a binder thread
|
||||
mNfcHandler.obtainMessage(NFC_SENT).sendToTarget();
|
||||
}
|
||||
};
|
||||
|
||||
// Check for available NFC Adapter
|
||||
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
|
||||
if (mNfcAdapter != null) {
|
||||
/*
|
||||
* Retrieve mNfcKeyringBytes here asynchronously (to not block the UI)
|
||||
* and init nfc adapter afterwards.
|
||||
* mNfcKeyringBytes can not be retrieved in createNdefMessage, because this process
|
||||
* has no permissions to query the Uri.
|
||||
*/
|
||||
AsyncTask<Void, Void, Void> initTask =
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
protected Void doInBackground(Void... unused) {
|
||||
try {
|
||||
Uri blobUri =
|
||||
KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri);
|
||||
mNfcKeyringBytes = (byte[]) mProviderHelper.getGenericData(
|
||||
blobUri,
|
||||
KeychainContract.KeyRingData.KEY_RING_DATA,
|
||||
ProviderHelper.FIELD_TYPE_BLOB);
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
Log.e(Constants.TAG, "key not found!", e);
|
||||
}
|
||||
|
||||
// no AsyncTask return (Void)
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void onPostExecute(Void unused) {
|
||||
// Register callback to set NDEF message
|
||||
mNfcAdapter.setNdefPushMessageCallback(mNdefCallback,
|
||||
ViewKeyActivity.this);
|
||||
// Register callback to listen for message-sent success
|
||||
mNfcAdapter.setOnNdefPushCompleteCallback(mNdefCompleteCallback,
|
||||
ViewKeyActivity.this);
|
||||
}
|
||||
};
|
||||
|
||||
initTask.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* NFC: This handler receives a message from onNdefPushComplete
|
||||
*/
|
||||
private final Handler mNfcHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case NFC_SENT:
|
||||
Notify.showNotify(
|
||||
ViewKeyActivity.this, R.string.nfc_successful, Notify.Style.INFO);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// These are the rows that we will retrieve.
|
||||
static final String[] PROJECTION = new String[]{
|
||||
static final String[] PROJECTION = new String[] {
|
||||
KeychainContract.KeyRings._ID,
|
||||
KeychainContract.KeyRings.MASTER_KEY_ID,
|
||||
KeychainContract.KeyRings.USER_ID,
|
||||
@ -849,9 +725,9 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
startFragment(mIsSecret, fpData);
|
||||
|
||||
// get name, email, and comment from USER_ID
|
||||
String[] mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID));
|
||||
if (mainUserId[0] != null) {
|
||||
mName.setText(mainUserId[0]);
|
||||
KeyRing.UserId mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID));
|
||||
if (mainUserId.name != null) {
|
||||
mName.setText(mainUserId.name);
|
||||
} else {
|
||||
mName.setText(R.string.user_id_no_name);
|
||||
}
|
||||
@ -892,6 +768,7 @@ public class ViewKeyActivity extends BaseActivity implements
|
||||
} else if (mIsExpired) {
|
||||
if (mIsSecret) {
|
||||
mStatusText.setText(R.string.view_key_expired_secret);
|
||||
mName.setText(mainUserId.name);
|
||||
} else {
|
||||
mStatusText.setText(R.string.view_key_expired);
|
||||
}
|
||||
|
@ -197,9 +197,9 @@ public class ViewKeyAdvActivity extends BaseActivity implements
|
||||
case LOADER_ID_UNIFIED: {
|
||||
if (data.moveToFirst()) {
|
||||
// get name, email, and comment from USER_ID
|
||||
String[] mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID));
|
||||
if (mainUserId[0] != null) {
|
||||
setTitle(mainUserId[0]);
|
||||
KeyRing.UserId mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID));
|
||||
if (mainUserId.name != null) {
|
||||
setTitle(mainUserId.name);
|
||||
} else {
|
||||
setTitle(R.string.user_id_no_name);
|
||||
}
|
||||
|
@ -237,9 +237,9 @@ public class ViewKeyAdvCertsFragment extends LoaderFragment implements
|
||||
TextView wSignStatus = (TextView) view.findViewById(R.id.signStatus);
|
||||
|
||||
String signerKeyId = KeyFormattingUtils.beautifyKeyIdWithPrefix(getActivity(), cursor.getLong(mIndexSignerKeyId));
|
||||
String[] userId = KeyRing.splitUserId(cursor.getString(mIndexSignerUserId));
|
||||
if (userId[0] != null) {
|
||||
wSignerName.setText(userId[0]);
|
||||
KeyRing.UserId userId = KeyRing.splitUserId(cursor.getString(mIndexSignerUserId));
|
||||
if (userId.name != null) {
|
||||
wSignerName.setText(userId.name);
|
||||
} else {
|
||||
wSignerName.setText(R.string.user_id_no_name);
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.NfcHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@ -68,10 +69,12 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
|
||||
private View mFingerprintClipboardButton;
|
||||
private View mKeyShareButton;
|
||||
private View mKeyClipboardButton;
|
||||
private View mKeyNfcButton;
|
||||
private ImageButton mKeySafeSlingerButton;
|
||||
private View mKeyUploadButton;
|
||||
|
||||
ProviderHelper mProviderHelper;
|
||||
NfcHelper mNfcHelper;
|
||||
|
||||
private static final int LOADER_ID_UNIFIED = 0;
|
||||
|
||||
@ -83,6 +86,7 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
|
||||
View view = inflater.inflate(R.layout.view_key_adv_share_fragment, getContainer());
|
||||
|
||||
mProviderHelper = new ProviderHelper(ViewKeyAdvShareFragment.this.getActivity());
|
||||
mNfcHelper = new NfcHelper(getActivity(), mProviderHelper);
|
||||
|
||||
mFingerprint = (TextView) view.findViewById(R.id.view_key_fingerprint);
|
||||
mQrCode = (ImageView) view.findViewById(R.id.view_key_qr_code);
|
||||
@ -90,6 +94,7 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
|
||||
mFingerprintShareButton = view.findViewById(R.id.view_key_action_fingerprint_share);
|
||||
mFingerprintClipboardButton = view.findViewById(R.id.view_key_action_fingerprint_clipboard);
|
||||
mKeyShareButton = view.findViewById(R.id.view_key_action_key_share);
|
||||
mKeyNfcButton = view.findViewById(R.id.view_key_action_key_nfc);
|
||||
mKeyClipboardButton = view.findViewById(R.id.view_key_action_key_clipboard);
|
||||
mKeySafeSlingerButton = (ImageButton) view.findViewById(R.id.view_key_action_key_safeslinger);
|
||||
mKeyUploadButton = view.findViewById(R.id.view_key_action_upload);
|
||||
@ -128,6 +133,14 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
|
||||
share(mDataUri, mProviderHelper, false, true);
|
||||
}
|
||||
});
|
||||
|
||||
mKeyNfcButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mNfcHelper.invokeNfcBeam();
|
||||
}
|
||||
});
|
||||
|
||||
mKeySafeSlingerButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -186,13 +199,13 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
|
||||
} else {
|
||||
message = getResources().getString(R.string.key_copied_to_clipboard);
|
||||
}
|
||||
Notify.showNotify(getActivity(), message, Notify.Style.OK);
|
||||
Notify.create(getActivity(), message, Notify.Style.OK).show();
|
||||
} else {
|
||||
// Android will fail with android.os.TransactionTooLargeException if key is too big
|
||||
// see http://www.lonestarprod.com/?p=34
|
||||
if (content.length() >= 86389) {
|
||||
Notify.showNotify(getActivity(), R.string.key_too_big_for_sharing,
|
||||
Notify.Style.ERROR);
|
||||
Notify.create(getActivity(), R.string.key_too_big_for_sharing,
|
||||
Notify.Style.ERROR).show();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -210,10 +223,10 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
|
||||
}
|
||||
} catch (PgpGeneralException | IOException e) {
|
||||
Log.e(Constants.TAG, "error processing key!", e);
|
||||
Notify.showNotify(getActivity(), R.string.error_key_processing, Notify.Style.ERROR);
|
||||
Notify.create(getActivity(), R.string.error_key_processing, Notify.Style.ERROR).show();
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
Log.e(Constants.TAG, "key not found!", e);
|
||||
Notify.showNotify(getActivity(), R.string.error_key_not_found, Notify.Style.ERROR);
|
||||
Notify.create(getActivity(), R.string.error_key_not_found, Notify.Style.ERROR).show();
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,9 +268,12 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
|
||||
// Prepare the loaders. Either re-connect with an existing ones,
|
||||
// or start new ones.
|
||||
getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
|
||||
|
||||
// Prepare the NfcHelper
|
||||
mNfcHelper.initNfc(mDataUri);
|
||||
}
|
||||
|
||||
static final String[] UNIFIED_PROJECTION = new String[]{
|
||||
static final String[] UNIFIED_PROJECTION = new String[] {
|
||||
KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.HAS_ANY_SECRET,
|
||||
KeyRings.USER_ID, KeyRings.FINGERPRINT,
|
||||
KeyRings.ALGORITHM, KeyRings.KEY_SIZE, KeyRings.CREATION, KeyRings.IS_EXPIRED,
|
||||
@ -362,4 +378,5 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
|
||||
startActivityForResult(uploadIntent, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -140,25 +140,25 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
|
||||
|
||||
// main user id
|
||||
String userId = entry.getUserIds().get(0);
|
||||
String[] userIdSplit = KeyRing.splitUserId(userId);
|
||||
KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId);
|
||||
|
||||
// name
|
||||
if (userIdSplit[0] != null) {
|
||||
if (userIdSplit.name != null) {
|
||||
// show red user id if it is a secret key
|
||||
if (entry.isSecretKey()) {
|
||||
holder.mainUserId.setText(mActivity.getString(R.string.secret_key)
|
||||
+ " " + userIdSplit[0]);
|
||||
+ " " + userIdSplit.name);
|
||||
} else {
|
||||
holder.mainUserId.setText(highlighter.highlight(userIdSplit[0]));
|
||||
holder.mainUserId.setText(highlighter.highlight(userIdSplit.name));
|
||||
}
|
||||
} else {
|
||||
holder.mainUserId.setText(R.string.user_id_no_name);
|
||||
}
|
||||
|
||||
// email
|
||||
if (userIdSplit[1] != null) {
|
||||
if (userIdSplit.email != null) {
|
||||
holder.mainUserIdRest.setVisibility(View.VISIBLE);
|
||||
holder.mainUserIdRest.setText(highlighter.highlight(userIdSplit[1]));
|
||||
holder.mainUserIdRest.setText(highlighter.highlight(userIdSplit.email));
|
||||
} else {
|
||||
holder.mainUserIdRest.setVisibility(View.GONE);
|
||||
}
|
||||
|
@ -33,7 +33,6 @@ import android.widget.TextView;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@ -83,9 +82,9 @@ public class MultiUserIdsAdapter extends CursorAdapter {
|
||||
|
||||
{ // first one
|
||||
String userId = uids.get(0);
|
||||
String[] splitUserId = KeyRing.splitUserId(userId);
|
||||
if (splitUserId[0] != null) {
|
||||
vName.setText(splitUserId[0]);
|
||||
KeyRing.UserId splitUserId = KeyRing.splitUserId(userId);
|
||||
if (splitUserId.name != null) {
|
||||
vName.setText(splitUserId.name);
|
||||
} else {
|
||||
vName.setText(R.string.user_id_no_name);
|
||||
}
|
||||
@ -93,9 +92,9 @@ public class MultiUserIdsAdapter extends CursorAdapter {
|
||||
if (isHeader == 1) {
|
||||
vHeaderId.setVisibility(View.VISIBLE);
|
||||
String message;
|
||||
if (splitUserId[0] != null) {
|
||||
if (splitUserId.name != null) {
|
||||
message = mContext.getString(R.string.section_uids_to_certify) +
|
||||
splitUserId[0];
|
||||
splitUserId.name;
|
||||
} else {
|
||||
message = mContext.getString(R.string.section_uids_to_certify) +
|
||||
context.getString(R.string.user_id_no_name);
|
||||
@ -108,13 +107,13 @@ public class MultiUserIdsAdapter extends CursorAdapter {
|
||||
|
||||
StringBuilder lines = new StringBuilder();
|
||||
for (String uid : uids) {
|
||||
String[] splitUserId = KeyRing.splitUserId(uid);
|
||||
if (splitUserId[1] == null) {
|
||||
KeyRing.UserId splitUserId = KeyRing.splitUserId(uid);
|
||||
if (splitUserId.email == null) {
|
||||
continue;
|
||||
}
|
||||
lines.append(splitUserId[1]);
|
||||
if (splitUserId[2] != null) {
|
||||
lines.append(" (").append(splitUserId[2]).append(")");
|
||||
lines.append(splitUserId.email);
|
||||
if (splitUserId.comment != null) {
|
||||
lines.append(" (").append(splitUserId.comment).append(")");
|
||||
}
|
||||
lines.append('\n');
|
||||
}
|
||||
|
@ -122,16 +122,16 @@ abstract public class SelectKeyCursorAdapter extends CursorAdapter {
|
||||
ViewHolderItem h = (ViewHolderItem) view.getTag();
|
||||
|
||||
String userId = cursor.getString(mIndexUserId);
|
||||
String[] userIdSplit = KeyRing.splitUserId(userId);
|
||||
KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId);
|
||||
|
||||
if (userIdSplit[0] != null) {
|
||||
h.mainUserId.setText(highlighter.highlight(userIdSplit[0]));
|
||||
if (userIdSplit.name != null) {
|
||||
h.mainUserId.setText(highlighter.highlight(userIdSplit.name));
|
||||
} else {
|
||||
h.mainUserId.setText(R.string.user_id_no_name);
|
||||
}
|
||||
if (userIdSplit[1] != null) {
|
||||
if (userIdSplit.email != null) {
|
||||
h.mainUserIdRest.setVisibility(View.VISIBLE);
|
||||
h.mainUserIdRest.setText(highlighter.highlight(userIdSplit[1]));
|
||||
h.mainUserIdRest.setText(highlighter.highlight(userIdSplit.email));
|
||||
} else {
|
||||
h.mainUserIdRest.setVisibility(View.GONE);
|
||||
}
|
||||
|
@ -72,20 +72,20 @@ public class UserIdsAdapter extends UserAttributesAdapter {
|
||||
vDeleteButton.setVisibility(View.GONE); // not used
|
||||
|
||||
String userId = cursor.getString(INDEX_USER_ID);
|
||||
String[] splitUserId = KeyRing.splitUserId(userId);
|
||||
if (splitUserId[0] != null) {
|
||||
vName.setText(splitUserId[0]);
|
||||
KeyRing.UserId splitUserId = KeyRing.splitUserId(userId);
|
||||
if (splitUserId.name != null) {
|
||||
vName.setText(splitUserId.name);
|
||||
} else {
|
||||
vName.setText(R.string.user_id_no_name);
|
||||
}
|
||||
if (splitUserId[1] != null) {
|
||||
vAddress.setText(splitUserId[1]);
|
||||
if (splitUserId.email != null) {
|
||||
vAddress.setText(splitUserId.email);
|
||||
vAddress.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
vAddress.setVisibility(View.GONE);
|
||||
}
|
||||
if (splitUserId[2] != null) {
|
||||
vComment.setText(splitUserId[2]);
|
||||
if (splitUserId.comment != null) {
|
||||
vComment.setText(splitUserId.comment);
|
||||
vComment.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
vComment.setVisibility(View.GONE);
|
||||
|
@ -92,20 +92,20 @@ public class UserIdsAddedAdapter extends ArrayAdapter<String> {
|
||||
// save reference to model item
|
||||
holder.mModel = getItem(position);
|
||||
|
||||
String[] splitUserId = KeyRing.splitUserId(holder.mModel);
|
||||
if (splitUserId[0] != null) {
|
||||
holder.vName.setText(splitUserId[0]);
|
||||
KeyRing.UserId splitUserId = KeyRing.splitUserId(holder.mModel);
|
||||
if (splitUserId.name != null) {
|
||||
holder.vName.setText(splitUserId.name);
|
||||
} else {
|
||||
holder.vName.setText(R.string.user_id_no_name);
|
||||
}
|
||||
if (splitUserId[1] != null) {
|
||||
holder.vAddress.setText(splitUserId[1]);
|
||||
if (splitUserId.email != null) {
|
||||
holder.vAddress.setText(splitUserId.email);
|
||||
holder.vAddress.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.vAddress.setVisibility(View.GONE);
|
||||
}
|
||||
if (splitUserId[2] != null) {
|
||||
holder.vComment.setText(splitUserId[2]);
|
||||
if (splitUserId.comment != null) {
|
||||
holder.vComment.setText(splitUserId.comment);
|
||||
holder.vComment.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.vComment.setVisibility(View.GONE);
|
||||
|
@ -100,8 +100,8 @@ public class AddUserIdDialogFragment extends DialogFragment implements OnEditorA
|
||||
|
||||
// return new user id back to activity
|
||||
Bundle data = new Bundle();
|
||||
String userId = KeyRing.createUserId(mName.getText().toString(),
|
||||
mEmail.getText().toString(), mComment.getText().toString());
|
||||
String userId = KeyRing.createUserId(new KeyRing.UserId(mName.getText().toString(),
|
||||
mEmail.getText().toString(), mComment.getText().toString()));
|
||||
data.putString(MESSAGE_DATA_USER_ID, userId);
|
||||
sendMessageToHandler(MESSAGE_OKAY, data);
|
||||
}
|
||||
|
@ -100,9 +100,9 @@ public class DeleteKeyDialogFragment extends DialogFragment {
|
||||
}
|
||||
);
|
||||
String name;
|
||||
String[] mainUserId = KeyRing.splitUserId((String) data.get(KeyRings.USER_ID));
|
||||
if (mainUserId[0] != null) {
|
||||
name = mainUserId[0];
|
||||
KeyRing.UserId mainUserId = KeyRing.splitUserId((String) data.get(KeyRings.USER_ID));
|
||||
if (mainUserId.name != null) {
|
||||
name = mainUserId.name;
|
||||
} else {
|
||||
name = getString(R.string.user_id_no_name);
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ public class FileDialogFragment extends DialogFragment {
|
||||
mFile = file;
|
||||
mFilename.setText(mFile.getName());
|
||||
} else {
|
||||
Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR);
|
||||
Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,7 @@ import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.widget.PassphraseEditText;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
public class SetPassphraseDialogFragment extends DialogFragment implements OnEditorActionListener {
|
||||
private static final String ARG_MESSENGER = "messenger";
|
||||
@ -67,12 +68,12 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
||||
* @param messenger to communicate back after setting the passphrase
|
||||
* @return
|
||||
*/
|
||||
public static SetPassphraseDialogFragment newInstance(Messenger messenger, String oldPassphrase, int title) {
|
||||
public static SetPassphraseDialogFragment newInstance(Messenger messenger, Passphrase oldPassphrase, int title) {
|
||||
SetPassphraseDialogFragment frag = new SetPassphraseDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_TITLE, title);
|
||||
args.putParcelable(ARG_MESSENGER, messenger);
|
||||
args.putString(ARG_OLD_PASSPHRASE, oldPassphrase);
|
||||
args.putParcelable(ARG_OLD_PASSPHRASE, oldPassphrase);
|
||||
|
||||
frag.setArguments(args);
|
||||
|
||||
@ -88,7 +89,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
||||
|
||||
int title = getArguments().getInt(ARG_TITLE);
|
||||
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
|
||||
String oldPassphrase = getArguments().getString(ARG_OLD_PASSPHRASE);
|
||||
Passphrase oldPassphrase = getArguments().getParcelable(ARG_OLD_PASSPHRASE);
|
||||
|
||||
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
|
||||
|
||||
@ -103,7 +104,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
||||
mNoPassphraseCheckBox = (CheckBox) view.findViewById(R.id.passphrase_no_passphrase);
|
||||
|
||||
|
||||
if (TextUtils.isEmpty(oldPassphrase)) {
|
||||
if (oldPassphrase.isEmpty()) {
|
||||
mNoPassphraseCheckBox.setChecked(true);
|
||||
mPassphraseEditText.setEnabled(false);
|
||||
mPassphraseAgainEditText.setEnabled(false);
|
||||
@ -123,12 +124,12 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dismiss();
|
||||
|
||||
String passphrase1;
|
||||
Passphrase passphrase1 = new Passphrase();
|
||||
if (mNoPassphraseCheckBox.isChecked()) {
|
||||
passphrase1 = "";
|
||||
passphrase1.setEmpty();
|
||||
} else {
|
||||
passphrase1 = mPassphraseEditText.getText().toString();
|
||||
String passphrase2 = mPassphraseAgainEditText.getText().toString();
|
||||
passphrase1 = new Passphrase(mPassphraseEditText);
|
||||
Passphrase passphrase2 = new Passphrase(mPassphraseAgainEditText);
|
||||
if (!passphrase1.equals(passphrase2)) {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
@ -139,7 +140,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
||||
return;
|
||||
}
|
||||
|
||||
if (passphrase1.equals("")) {
|
||||
if (passphrase1.isEmpty()) {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
getString(R.string.error_message,
|
||||
@ -152,7 +153,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
|
||||
|
||||
// return resulting data back to activity
|
||||
Bundle data = new Bundle();
|
||||
data.putString(MESSAGE_NEW_PASSPHRASE, passphrase1);
|
||||
data.putParcelable(MESSAGE_NEW_PASSPHRASE, passphrase1);
|
||||
|
||||
sendMessageToHandler(MESSAGE_OKAY, data);
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment
|
||||
|
||||
private void proofToClipboard() {
|
||||
ClipboardReflection.copyToClipboard(getActivity(), mResourceString);
|
||||
Notify.showNotify(getActivity(), R.string.linked_text_clipboard, Notify.Style.OK);
|
||||
Notify.create(getActivity(), R.string.linked_text_clipboard, Notify.Style.OK).show();
|
||||
}
|
||||
|
||||
private void saveFile(Uri uri) {
|
||||
@ -127,10 +127,10 @@ public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment
|
||||
new PrintWriter(getActivity().getContentResolver().openOutputStream(uri));
|
||||
out.print(mResourceString);
|
||||
if (out.checkError()) {
|
||||
Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR);
|
||||
Notify.create(getActivity(), "Error writing file!", Style.ERROR).show();
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
Notify.showNotify(getActivity(), "File could not be opened for writing!", Style.ERROR);
|
||||
Notify.create(getActivity(), "File could not be opened for writing!", Style.ERROR).show();
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment {
|
||||
private void startCertify() {
|
||||
|
||||
if (mVerifiedResource == null) {
|
||||
Notify.showNotify(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR);
|
||||
Notify.create(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR).show();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -80,14 +80,14 @@ public class LinkedIdCreateGithubStep1Fragment extends Fragment {
|
||||
super.onPostExecute(result);
|
||||
|
||||
if (result == null) {
|
||||
Notify.showNotify(getActivity(),
|
||||
"Connection error while checking username!", Notify.Style.ERROR);
|
||||
Notify.create(getActivity(),
|
||||
"Connection error while checking username!", Notify.Style.ERROR).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
Notify.showNotify(getActivity(),
|
||||
"This handle does not exist on Github!", Notify.Style.ERROR);
|
||||
Notify.create(getActivity(),
|
||||
"This handle does not exist on Github!", Notify.Style.ERROR).show();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,7 @@ public class LinkedIdCreateHttpsStep2Fragment extends LinkedIdCreateFinalFragmen
|
||||
private void proofSave () {
|
||||
String state = Environment.getExternalStorageState();
|
||||
if (!Environment.MEDIA_MOUNTED.equals(state)) {
|
||||
Notify.showNotify(getActivity(), "External storage not available!", Style.ERROR);
|
||||
Notify.create(getActivity(), "External storage not available!", Style.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -146,11 +146,10 @@ public class LinkedIdCreateHttpsStep2Fragment extends LinkedIdCreateFinalFragmen
|
||||
new PrintWriter(getActivity().getContentResolver().openOutputStream(uri));
|
||||
out.print(mResourceString);
|
||||
if (out.checkError()) {
|
||||
Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR);
|
||||
Notify.create(getActivity(), "Error writing file!", Style.ERROR).show();
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
Notify.showNotify(getActivity(), "File could not be opened for writing!", Style.ERROR);
|
||||
e.printStackTrace();
|
||||
Notify.create(getActivity(), "File could not be opened for writing!", Style.ERROR).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,13 +78,13 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment {
|
||||
super.onPostExecute(result);
|
||||
|
||||
if (result == null) {
|
||||
Notify.showNotify(getActivity(),
|
||||
Notify.create(getActivity(),
|
||||
"Connection error while checking username!", Notify.Style.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
Notify.showNotify(getActivity(),
|
||||
Notify.create(getActivity(),
|
||||
"This handle does not exist on Twitter!", Notify.Style.ERROR);
|
||||
return;
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
||||
import org.sufficientlysecure.keychain.ui.widget.CertListWidget;
|
||||
import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
|
||||
public class LinkedIdViewFragment extends Fragment implements
|
||||
@ -153,7 +154,7 @@ public class LinkedIdViewFragment extends Fragment implements
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "error parsing identity", e);
|
||||
Notify.createNotify(getActivity(), "Error parsing identity!",
|
||||
Notify.create(getActivity(), "Error parsing identity!",
|
||||
Notify.LENGTH_LONG, Style.ERROR).show();
|
||||
finishFragment();
|
||||
}
|
||||
@ -492,7 +493,7 @@ public class LinkedIdViewFragment extends Fragment implements
|
||||
}
|
||||
|
||||
// get the user's passphrase for this key (if required)
|
||||
String passphrase;
|
||||
Passphrase passphrase;
|
||||
long certifyKeyId = mViewHolder.vKeySpinner.getSelectedItemId();
|
||||
try {
|
||||
passphrase = PassphraseCacheService.getCachedPassphrase(
|
||||
|
@ -18,7 +18,9 @@
|
||||
package org.sufficientlysecure.keychain.ui.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.res.Resources;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.nispok.snackbar.Snackbar;
|
||||
import com.nispok.snackbar.Snackbar.SnackbarDuration;
|
||||
@ -35,135 +37,58 @@ import org.sufficientlysecure.keychain.util.FabContainer;
|
||||
*/
|
||||
public class Notify {
|
||||
|
||||
public static enum Style {OK, WARN, INFO, ERROR}
|
||||
public static enum Style {
|
||||
OK, WARN, ERROR;
|
||||
|
||||
public void applyToBar(Snackbar bar) {
|
||||
|
||||
switch (this) {
|
||||
case OK:
|
||||
// bar.actionColorResource(R.color.android_green_light);
|
||||
bar.lineColorResource(R.color.android_green_light);
|
||||
break;
|
||||
case WARN:
|
||||
// bar.textColorResource(R.color.android_orange_light);
|
||||
bar.lineColorResource(R.color.android_orange_light);
|
||||
break;
|
||||
case ERROR:
|
||||
// bar.textColorResource(R.color.android_red_light);
|
||||
bar.lineColorResource(R.color.android_red_light);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static final int LENGTH_INDEFINITE = 0;
|
||||
public static final int LENGTH_LONG = 3500;
|
||||
|
||||
/**
|
||||
* Shows a simple in-layout notification with the CharSequence given as parameter
|
||||
* @param text Text to show
|
||||
* @param style Notification styling
|
||||
*/
|
||||
public static void showNotify(final Activity activity, CharSequence text, Style style) {
|
||||
|
||||
Snackbar bar = getSnackbar(activity)
|
||||
public static Showable create(final Activity activity, String text, int duration, Style style,
|
||||
final ActionListener actionListener, int actionResId) {
|
||||
final Snackbar snackbar = Snackbar.with(activity)
|
||||
.type(SnackbarType.MULTI_LINE)
|
||||
.text(text);
|
||||
|
||||
switch (style) {
|
||||
case OK:
|
||||
break;
|
||||
case WARN:
|
||||
bar.textColor(activity.getResources().getColor(R.color.android_orange_light));
|
||||
break;
|
||||
case ERROR:
|
||||
bar.textColor(activity.getResources().getColor(R.color.android_red_light));
|
||||
break;
|
||||
}
|
||||
|
||||
SnackbarManager.show(bar);
|
||||
|
||||
}
|
||||
|
||||
public static Showable createNotify (Activity activity, int resId, int duration, Style style) {
|
||||
final Snackbar bar = getSnackbar(activity)
|
||||
.text(resId);
|
||||
|
||||
if (duration == LENGTH_INDEFINITE) {
|
||||
bar.duration(SnackbarDuration.LENGTH_INDEFINITE);
|
||||
snackbar.duration(SnackbarDuration.LENGTH_INDEFINITE);
|
||||
} else {
|
||||
bar.duration(duration);
|
||||
snackbar.duration(duration);
|
||||
}
|
||||
|
||||
switch (style) {
|
||||
case OK:
|
||||
bar.actionColor(activity.getResources().getColor(R.color.android_green_light));
|
||||
break;
|
||||
case WARN:
|
||||
bar.textColor(activity.getResources().getColor(R.color.android_orange_light));
|
||||
break;
|
||||
case ERROR:
|
||||
bar.textColor(activity.getResources().getColor(R.color.android_red_light));
|
||||
break;
|
||||
style.applyToBar(snackbar);
|
||||
|
||||
if (actionListener != null) {
|
||||
snackbar.actionLabel(actionResId)
|
||||
.actionListener(new ActionClickListener() {
|
||||
@Override
|
||||
public void onActionClicked(Snackbar snackbar) {
|
||||
actionListener.onAction();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return new Showable () {
|
||||
@Override
|
||||
public void show() {
|
||||
SnackbarManager.show(bar);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Showable createNotify(Activity activity, int resId, int duration, Style style,
|
||||
final ActionListener listener, int resIdAction) {
|
||||
return createNotify(activity, activity.getString(resId), duration, style, listener, resIdAction);
|
||||
}
|
||||
|
||||
public static Showable createNotify(Activity activity, String msg, int duration, Style style) {
|
||||
return createNotify(activity, msg, duration, style, null, 0);
|
||||
}
|
||||
|
||||
public static Showable createNotify(Activity activity, String msg, int duration, Style style,
|
||||
final ActionListener listener, int resIdAction) {
|
||||
|
||||
final Snackbar bar = getSnackbar(activity)
|
||||
.text(msg);
|
||||
|
||||
if (listener != null) {
|
||||
bar.actionLabel(resIdAction);
|
||||
bar.actionListener(new ActionClickListener() {
|
||||
@Override
|
||||
public void onActionClicked(Snackbar snackbar) {
|
||||
listener.onAction();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (duration == LENGTH_INDEFINITE) {
|
||||
bar.duration(SnackbarDuration.LENGTH_INDEFINITE);
|
||||
} else {
|
||||
bar.duration(duration);
|
||||
}
|
||||
|
||||
switch (style) {
|
||||
case OK:
|
||||
bar.actionColor(activity.getResources().getColor(R.color.android_green_light));
|
||||
break;
|
||||
case WARN:
|
||||
bar.textColor(activity.getResources().getColor(R.color.android_orange_light));
|
||||
break;
|
||||
case ERROR:
|
||||
bar.textColor(activity.getResources().getColor(R.color.android_red_light));
|
||||
break;
|
||||
}
|
||||
|
||||
return new Showable () {
|
||||
@Override
|
||||
public void show() {
|
||||
SnackbarManager.show(bar);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a simple in-layout notification with the resource text from given id
|
||||
* @param resId ResourceId of notification text
|
||||
* @param style Notification styling
|
||||
* @throws Resources.NotFoundException
|
||||
*/
|
||||
public static void showNotify(Activity activity, int resId, Style style) throws Resources.NotFoundException {
|
||||
showNotify(activity, activity.getResources().getText(resId), style);
|
||||
}
|
||||
|
||||
private static Snackbar getSnackbar(final Activity activity) {
|
||||
Snackbar bar = Snackbar.with(activity)
|
||||
.type(SnackbarType.MULTI_LINE)
|
||||
.duration(SnackbarDuration.LENGTH_LONG);
|
||||
|
||||
if (activity instanceof FabContainer) {
|
||||
bar.eventListener(new EventListenerAdapter() {
|
||||
snackbar.eventListener(new EventListenerAdapter() {
|
||||
@Override
|
||||
public void onShow(Snackbar snackbar) {
|
||||
((FabContainer) activity).fabMoveUp(snackbar.getHeight());
|
||||
@ -175,17 +100,84 @@ public class Notify {
|
||||
}
|
||||
});
|
||||
}
|
||||
return bar;
|
||||
|
||||
return new Showable() {
|
||||
@Override
|
||||
public void show() {
|
||||
SnackbarManager.show(snackbar, activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(Fragment fragment) {
|
||||
if (fragment != null) {
|
||||
View view = fragment.getView();
|
||||
|
||||
if (view != null && view instanceof ViewGroup) {
|
||||
SnackbarManager.show(snackbar, (ViewGroup) view);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(ViewGroup viewGroup) {
|
||||
if (viewGroup != null) {
|
||||
SnackbarManager.show(snackbar, viewGroup);
|
||||
return;
|
||||
}
|
||||
|
||||
show();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Showable create(Activity activity, String text, int duration, Style style) {
|
||||
return create(activity, text, duration, style, null, -1);
|
||||
}
|
||||
|
||||
public static Showable create(Activity activity, String text, Style style) {
|
||||
return create(activity, text, LENGTH_LONG, style);
|
||||
}
|
||||
|
||||
public static Showable create(Activity activity, int textResId, int duration, Style style,
|
||||
ActionListener actionListener, int actionResId) {
|
||||
return create(activity, activity.getString(textResId), duration, style, actionListener, actionResId);
|
||||
}
|
||||
|
||||
public static Showable create(Activity activity, int textResId, int duration, Style style) {
|
||||
return create(activity, activity.getString(textResId), duration, style);
|
||||
}
|
||||
|
||||
public static Showable create(Activity activity, int textResId, Style style) {
|
||||
return create(activity, activity.getString(textResId), style);
|
||||
}
|
||||
|
||||
public interface Showable {
|
||||
|
||||
/**
|
||||
* Shows the notification on the bottom of the Activity.
|
||||
*/
|
||||
public void show();
|
||||
|
||||
/**
|
||||
* Shows the notification on the bottom of the Fragment.
|
||||
*/
|
||||
public void show(Fragment fragment);
|
||||
|
||||
/**
|
||||
* Shows the notification on the given ViewGroup.
|
||||
* The viewGroup should be either a RelativeLayout or FrameLayout.
|
||||
*/
|
||||
public void show(ViewGroup viewGroup);
|
||||
|
||||
}
|
||||
|
||||
public interface ActionListener {
|
||||
|
||||
public void onAction();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -58,9 +58,10 @@ public class EmailEditText extends AutoCompleteTextView {
|
||||
}
|
||||
|
||||
private void init() {
|
||||
this.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
|
||||
this.addTextChangedListener(textWatcher);
|
||||
removeFlag();
|
||||
setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
|
||||
reenableKeyboardSuggestions();
|
||||
|
||||
addTextChangedListener(textWatcher);
|
||||
initAdapter();
|
||||
}
|
||||
|
||||
@ -104,7 +105,7 @@ public class EmailEditText extends AutoCompleteTextView {
|
||||
* Hack to re-enable keyboard auto correction in AutoCompleteTextView.
|
||||
* From http://stackoverflow.com/a/22512858
|
||||
*/
|
||||
private void removeFlag() {
|
||||
private void reenableKeyboardSuggestions() {
|
||||
int inputType = getInputType();
|
||||
inputType &= ~EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE;
|
||||
setRawInputType(inputType);
|
||||
|
@ -184,7 +184,7 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView {
|
||||
|
||||
public class EncryptionKey {
|
||||
private String mUserIdFull;
|
||||
private String[] mUserId;
|
||||
private KeyRing.UserId mUserId;
|
||||
private long mKeyId;
|
||||
private boolean mHasDuplicate;
|
||||
private Date mCreation;
|
||||
@ -222,23 +222,23 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView {
|
||||
}
|
||||
|
||||
public String getPrimary() {
|
||||
if (mUserId[0] != null) {
|
||||
return mUserId[0];
|
||||
if (mUserId.name != null) {
|
||||
return mUserId.name;
|
||||
} else {
|
||||
return mUserId[1];
|
||||
return mUserId.email;
|
||||
}
|
||||
}
|
||||
|
||||
public String getSecondary() {
|
||||
if (mUserId[1] != null) {
|
||||
return mUserId[1];
|
||||
if (mUserId.email != null) {
|
||||
return mUserId.email;
|
||||
} else {
|
||||
return getCreationDate();
|
||||
}
|
||||
}
|
||||
|
||||
public String getTertiary() {
|
||||
if (mUserId[0] != null) {
|
||||
if (mUserId.name != null) {
|
||||
return getCreationDate();
|
||||
} else {
|
||||
return null;
|
||||
|
@ -39,7 +39,6 @@ import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.util.Calendar;
|
||||
@ -158,9 +157,9 @@ public abstract class KeySpinner extends TintSpinner implements LoaderManager.Lo
|
||||
TextView vKeyEmail = (TextView) view.findViewById(R.id.keyspinner_key_email);
|
||||
TextView vDuplicate = (TextView) view.findViewById(R.id.keyspinner_duplicate);
|
||||
|
||||
String[] userId = KeyRing.splitUserId(cursor.getString(mIndexUserId));
|
||||
vKeyName.setText(userId[2] == null ? userId[0] : (userId[0] + " (" + userId[2] + ")"));
|
||||
vKeyEmail.setText(userId[1]);
|
||||
KeyRing.UserId userId = KeyRing.splitUserId(cursor.getString(mIndexUserId));
|
||||
vKeyName.setText(userId.name);
|
||||
vKeyEmail.setText(userId.email);
|
||||
|
||||
boolean duplicate = cursor.getLong(mIndexDuplicate) > 0;
|
||||
if (duplicate) {
|
||||
|
@ -50,7 +50,7 @@ public class NameEditText extends AutoCompleteTextView {
|
||||
}
|
||||
|
||||
private void init() {
|
||||
removeFlag();
|
||||
reenableKeyboardSuggestions();
|
||||
initAdapter();
|
||||
}
|
||||
|
||||
@ -62,10 +62,10 @@ public class NameEditText extends AutoCompleteTextView {
|
||||
}
|
||||
|
||||
/**
|
||||
* Hack to re-enable keyboard auto correction in AutoCompleteTextView.
|
||||
* Hack to re-enable keyboard suggestions in AutoCompleteTextView.
|
||||
* From http://stackoverflow.com/a/22512858
|
||||
*/
|
||||
private void removeFlag() {
|
||||
private void reenableKeyboardSuggestions() {
|
||||
int inputType = getInputType();
|
||||
inputType &= ~EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE;
|
||||
setRawInputType(inputType);
|
||||
|
@ -19,7 +19,6 @@ package org.sufficientlysecure.keychain.util;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
@ -28,7 +27,6 @@ import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.ContactsContract;
|
||||
import android.util.Patterns;
|
||||
|
||||
@ -37,7 +35,6 @@ import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
@ -303,10 +300,9 @@ public class ContactHelper {
|
||||
return new ArrayList<>(names);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
public static Uri dataUriFromContactUri(Context context, Uri contactUri) {
|
||||
Cursor contactMasterKey = context.getContentResolver().query(contactUri,
|
||||
new String[]{ContactsContract.Data.DATA2}, null, null, null, null);
|
||||
new String[]{ContactsContract.Data.DATA2}, null, null, null);
|
||||
if (contactMasterKey != null) {
|
||||
if (contactMasterKey.moveToNext()) {
|
||||
return KeychainContract.KeyRings.buildGenericKeyRingUri(contactMasterKey.getLong(0));
|
||||
@ -447,7 +443,7 @@ public class ContactHelper {
|
||||
if (cursor != null) {
|
||||
while (cursor.moveToNext()) {
|
||||
long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
|
||||
String[] userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID));
|
||||
KeyRing.UserId userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID));
|
||||
boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0;
|
||||
boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
|
||||
boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0;
|
||||
@ -470,19 +466,19 @@ public class ContactHelper {
|
||||
if (rawContactId != -1) {
|
||||
deleteRawContactById(resolver, rawContactId);
|
||||
}
|
||||
} else if (userIdSplit[0] != null) {
|
||||
} else if (userIdSplit.name != null) {
|
||||
|
||||
// Create a new rawcontact with corresponding key if it does not exist yet
|
||||
if (rawContactId == -1) {
|
||||
Log.d(Constants.TAG, "Insert new raw contact with masterKeyId " + masterKeyId);
|
||||
|
||||
insertContact(ops, context, masterKeyId);
|
||||
writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit[0]);
|
||||
writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit.name);
|
||||
}
|
||||
|
||||
// We always update the display name (which is derived from primary user id)
|
||||
// and email addresses from user id
|
||||
writeContactDisplayName(ops, rawContactId, userIdSplit[0]);
|
||||
writeContactDisplayName(ops, rawContactId, userIdSplit.name);
|
||||
writeContactEmail(ops, resolver, rawContactId, masterKeyId);
|
||||
try {
|
||||
resolver.applyBatch(ContactsContract.AUTHORITY, ops);
|
||||
@ -521,9 +517,9 @@ public class ContactHelper {
|
||||
long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
|
||||
boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0;
|
||||
boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
|
||||
String[] userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID));
|
||||
KeyRing.UserId userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID));
|
||||
|
||||
if (!isExpired && !isRevoked && userIdSplit[0] != null) {
|
||||
if (!isExpired && !isRevoked && userIdSplit.name != null) {
|
||||
// if expired or revoked will not be removed from keysToDelete or inserted
|
||||
// into main profile ("me" contact)
|
||||
boolean existsInMainProfile = keysToDelete.remove(masterKeyId);
|
||||
@ -534,7 +530,7 @@ public class ContactHelper {
|
||||
|
||||
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
|
||||
insertMainProfileRawContact(ops, masterKeyId);
|
||||
writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit[0]);
|
||||
writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit.name);
|
||||
|
||||
try {
|
||||
resolver.applyBatch(ContactsContract.AUTHORITY, ops);
|
||||
@ -715,7 +711,6 @@ public class ContactHelper {
|
||||
*
|
||||
* @return raw contact id or -1 if not found
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
private static long findRawContactId(ContentResolver resolver, long masterKeyId) {
|
||||
long rawContactId = -1;
|
||||
Cursor raw = resolver.query(ContactsContract.RawContacts.CONTENT_URI,
|
||||
@ -725,7 +720,7 @@ public class ContactHelper {
|
||||
ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + ContactsContract.RawContacts.SOURCE_ID + "=?",
|
||||
new String[]{
|
||||
Constants.ACCOUNT_TYPE, Long.toString(masterKeyId)
|
||||
}, null, null);
|
||||
}, null);
|
||||
if (raw != null) {
|
||||
if (raw.moveToNext()) {
|
||||
rawContactId = raw.getLong(0);
|
||||
@ -776,14 +771,14 @@ public class ContactHelper {
|
||||
null, null);
|
||||
if (ids != null) {
|
||||
while (ids.moveToNext()) {
|
||||
String[] userId = KeyRing.splitUserId(ids.getString(0));
|
||||
if (userId[1] != null) {
|
||||
KeyRing.UserId userId = KeyRing.splitUserId(ids.getString(0));
|
||||
if (userId.email != null) {
|
||||
ops.add(referenceRawContact(
|
||||
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI),
|
||||
rawContactId)
|
||||
.withValue(ContactsContract.Data.MIMETYPE,
|
||||
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
|
||||
.withValue(ContactsContract.CommonDataKinds.Email.DATA, userId[1])
|
||||
.withValue(ContactsContract.CommonDataKinds.Email.DATA, userId.email)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
@ -47,21 +47,6 @@ public class ExportHelper {
|
||||
this.mActivity = activity;
|
||||
}
|
||||
|
||||
public void deleteKey(Uri dataUri, Handler deleteHandler) {
|
||||
try {
|
||||
long masterKeyId = new ProviderHelper(mActivity).getCachedPublicKeyRing(dataUri)
|
||||
.extractOrGetMasterKeyId();
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(deleteHandler);
|
||||
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
|
||||
new long[]{ masterKeyId });
|
||||
deleteKeyDialog.show(mActivity.getSupportFragmentManager(), "deleteKeyDialog");
|
||||
} catch (PgpKeyNotFoundException e) {
|
||||
Log.e(Constants.TAG, "key not found!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show dialog where to export keys
|
||||
*/
|
||||
|
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2015 Kent Nguyen <kentnguyen@moneylover.me>
|
||||
*
|
||||
* 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.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.nfc.NdefMessage;
|
||||
import android.nfc.NdefRecord;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.nfc.NfcEvent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.provider.Settings;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* This class contains NFC functionality that can be shared across Fragments or Activities.
|
||||
*/
|
||||
|
||||
public class NfcHelper {
|
||||
|
||||
private Activity mActivity;
|
||||
private ProviderHelper mProviderHelper;
|
||||
|
||||
/**
|
||||
* NFC: This handler receives a message from onNdefPushComplete
|
||||
*/
|
||||
private static NfcHandler mNfcHandler;
|
||||
|
||||
private NfcAdapter mNfcAdapter;
|
||||
private NfcAdapter.CreateNdefMessageCallback mNdefCallback;
|
||||
private NfcAdapter.OnNdefPushCompleteCallback mNdefCompleteCallback;
|
||||
private byte[] mNfcKeyringBytes;
|
||||
private static final int NFC_SENT = 1;
|
||||
|
||||
/**
|
||||
* Initializes the NfcHelper.
|
||||
*/
|
||||
public NfcHelper(final Activity activity, final ProviderHelper providerHelper) {
|
||||
mActivity = activity;
|
||||
mProviderHelper = providerHelper;
|
||||
|
||||
mNfcHandler = new NfcHandler(mActivity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the NFC Adapter of this Helper has any features enabled.
|
||||
*
|
||||
* @return true if this NFC Adapter has any features enabled
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return mNfcAdapter.isEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* NFC: Initialize NFC sharing if OS and device supports it
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
public void initNfc(final Uri dataUri) {
|
||||
// check if NFC Beam is supported (>= Android 4.1)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
|
||||
// Implementation for the CreateNdefMessageCallback interface
|
||||
mNdefCallback = new NfcAdapter.CreateNdefMessageCallback() {
|
||||
@Override
|
||||
public NdefMessage createNdefMessage(NfcEvent event) {
|
||||
/*
|
||||
* When a device receives a push with an AAR in it, the application specified in the AAR is
|
||||
* guaranteed to run. The AAR overrides the tag dispatch system. You can add it back in to
|
||||
* guarantee that this activity starts when receiving a beamed message. For now, this code
|
||||
* uses the tag dispatch system.
|
||||
*/
|
||||
return new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME,
|
||||
mNfcKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME));
|
||||
}
|
||||
};
|
||||
|
||||
// Implementation for the OnNdefPushCompleteCallback interface
|
||||
mNdefCompleteCallback = new NfcAdapter.OnNdefPushCompleteCallback() {
|
||||
@Override
|
||||
public void onNdefPushComplete(NfcEvent event) {
|
||||
// A handler is needed to send messages to the activity when this
|
||||
// callback occurs, because it happens from a binder thread
|
||||
mNfcHandler.obtainMessage(NFC_SENT).sendToTarget();
|
||||
}
|
||||
};
|
||||
|
||||
// Check for available NFC Adapter
|
||||
mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity);
|
||||
if (mNfcAdapter != null) {
|
||||
/*
|
||||
* Retrieve mNfcKeyringBytes here asynchronously (to not block the UI)
|
||||
* and init nfc adapter afterwards.
|
||||
* mNfcKeyringBytes can not be retrieved in createNdefMessage, because this process
|
||||
* has no permissions to query the Uri.
|
||||
*/
|
||||
AsyncTask<Void, Void, Void> initTask =
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
protected Void doInBackground(Void... unused) {
|
||||
try {
|
||||
Uri blobUri =
|
||||
KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri);
|
||||
mNfcKeyringBytes = (byte[]) mProviderHelper.getGenericData(
|
||||
blobUri,
|
||||
KeychainContract.KeyRingData.KEY_RING_DATA,
|
||||
ProviderHelper.FIELD_TYPE_BLOB);
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
Log.e(Constants.TAG, "key not found!", e);
|
||||
}
|
||||
|
||||
// no AsyncTask return (Void)
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void onPostExecute(Void unused) {
|
||||
// Register callback to set NDEF message
|
||||
mNfcAdapter.setNdefPushMessageCallback(mNdefCallback,
|
||||
mActivity);
|
||||
// Register callback to listen for message-sent success
|
||||
mNfcAdapter.setOnNdefPushCompleteCallback(mNdefCompleteCallback,
|
||||
mActivity);
|
||||
}
|
||||
};
|
||||
|
||||
initTask.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public void invokeNfcBeam() {
|
||||
// Check if device supports NFC
|
||||
if (!mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
|
||||
Notify.create(mActivity, R.string.no_nfc_support, Notify.LENGTH_LONG, Notify.Style.ERROR).show();
|
||||
return;
|
||||
}
|
||||
// Check for available NFC Adapter
|
||||
mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity);
|
||||
if (mNfcAdapter == null || !mNfcAdapter.isEnabled()) {
|
||||
Notify.create(mActivity, R.string.error_nfc_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() {
|
||||
@Override
|
||||
public void onAction() {
|
||||
Intent intentSettings = new Intent(Settings.ACTION_NFC_SETTINGS);
|
||||
mActivity.startActivity(intentSettings);
|
||||
}
|
||||
}, R.string.menu_nfc_preferences).show();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mNfcAdapter.isNdefPushEnabled()) {
|
||||
Notify.create(mActivity, R.string.error_beam_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() {
|
||||
@Override
|
||||
public void onAction() {
|
||||
Intent intentSettings = new Intent(Settings.ACTION_NFCSHARING_SETTINGS);
|
||||
mActivity.startActivity(intentSettings);
|
||||
}
|
||||
}, R.string.menu_beam_preferences).show();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
mNfcAdapter.invokeBeam(mActivity);
|
||||
}
|
||||
|
||||
/**
|
||||
* A static subclass of {@link Handler} with a {@link WeakReference} to an {@link Activity} to avoid memory leaks.
|
||||
*/
|
||||
private static class NfcHandler extends Handler {
|
||||
private final WeakReference<Activity> mActivityReference;
|
||||
|
||||
public NfcHandler(Activity activity) {
|
||||
mActivityReference = new WeakReference<>(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
Activity activity = mActivityReference.get();
|
||||
|
||||
if (activity != null) {
|
||||
switch (msg.what) {
|
||||
case NFC_SENT:
|
||||
Notify.create(activity, R.string.nfc_successful, Notify.Style.OK).show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.Editable;
|
||||
import android.widget.EditText;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Passwords should not be stored as Strings in memory.
|
||||
* This class wraps a char[] that can be erased after it is no longer used.
|
||||
* See also:
|
||||
* <p/>
|
||||
* http://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html#PBEEx
|
||||
* https://github.com/c-a-m/passfault/blob/master/core/src/main/java/org/owasp/passfault/SecureString.java
|
||||
* http://stackoverflow.com/q/8881291
|
||||
* http://stackoverflow.com/a/15844273
|
||||
*/
|
||||
public class Passphrase implements Parcelable {
|
||||
private char[] mPassphrase;
|
||||
|
||||
/**
|
||||
* According to http://stackoverflow.com/a/15844273 EditText is not using String internally
|
||||
* but char[]. Thus, we can get the char[] directly from it.
|
||||
*/
|
||||
public Passphrase(Editable editable) {
|
||||
int pl = editable.length();
|
||||
mPassphrase = new char[pl];
|
||||
editable.getChars(0, pl, mPassphrase, 0);
|
||||
// TODO: clean up internal char[] of EditText after getting the passphrase?
|
||||
// editText.getText().replace()
|
||||
}
|
||||
|
||||
public Passphrase(EditText editText) {
|
||||
this(editText.getText());
|
||||
}
|
||||
|
||||
public Passphrase(char[] passphrase) {
|
||||
mPassphrase = passphrase;
|
||||
}
|
||||
|
||||
public Passphrase(String passphrase) {
|
||||
mPassphrase = passphrase.toCharArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a passphrase object with an empty ("") passphrase
|
||||
*/
|
||||
public Passphrase() {
|
||||
setEmpty();
|
||||
}
|
||||
|
||||
public char[] getCharArray() {
|
||||
return mPassphrase;
|
||||
}
|
||||
|
||||
public void setEmpty() {
|
||||
removeFromMemory();
|
||||
mPassphrase = new char[0];
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return (length() == 0);
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return mPassphrase.length;
|
||||
}
|
||||
|
||||
public char charAt(int index) {
|
||||
return mPassphrase[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually clear the underlying array holding the characters
|
||||
*/
|
||||
public void removeFromMemory() {
|
||||
if (mPassphrase != null) {
|
||||
Arrays.fill(mPassphrase, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finalize() throws Throwable {
|
||||
removeFromMemory();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (Constants.DEBUG) {
|
||||
return "Passphrase{" +
|
||||
"mPassphrase=" + Arrays.toString(mPassphrase) +
|
||||
'}';
|
||||
} else {
|
||||
return "Passphrase: hidden";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Passphrase that = (Passphrase) o;
|
||||
if (!Arrays.equals(mPassphrase, that.mPassphrase)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mPassphrase != null ? Arrays.hashCode(mPassphrase) : 0;
|
||||
}
|
||||
|
||||
private Passphrase(Parcel source) {
|
||||
mPassphrase = source.createCharArray();
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeCharArray(mPassphrase);
|
||||
}
|
||||
|
||||
public static final Creator<Passphrase> CREATOR = new Creator<Passphrase>() {
|
||||
public Passphrase createFromParcel(final Parcel source) {
|
||||
return new Passphrase(source);
|
||||
}
|
||||
|
||||
public Passphrase[] newArray(final int size) {
|
||||
return new Passphrase[size];
|
||||
}
|
||||
};
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
}
|
BIN
OpenKeychain/src/main/res/drawable-hdpi/ic_nfc_grey_24dp.png
Normal file
BIN
OpenKeychain/src/main/res/drawable-hdpi/ic_nfc_grey_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
BIN
OpenKeychain/src/main/res/drawable-mdpi/ic_nfc_grey_24dp.png
Normal file
BIN
OpenKeychain/src/main/res/drawable-mdpi/ic_nfc_grey_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 701 B |
BIN
OpenKeychain/src/main/res/drawable-xhdpi/ic_nfc_grey_24dp.png
Normal file
BIN
OpenKeychain/src/main/res/drawable-xhdpi/ic_nfc_grey_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user