diff options
Diffstat (limited to 'OpenKeychain/src')
211 files changed, 4239 insertions, 3352 deletions
diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index 82eb40ad3..de559fe16 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -31,7 +31,7 @@          For OI Filemanager it makes no difference, gpg files can't be associated      --> -    <!-- Specified in buid.gradle --> +    <!-- Specified in build.gradle -->      <!--<uses-sdk-->      <!--android:minSdkVersion="9"-->      <!--android:targetSdkVersion="19" />--> @@ -49,17 +49,17 @@          android:name="android.hardware.touchscreen"          android:required="false" /> -    <permission android:name="org.sufficientlysecure.keychain.WRITE_TEMPORARY_STORAGE"/> -    <uses-permission android:name="org.sufficientlysecure.keychain.WRITE_TEMPORARY_STORAGE"/> +    <permission android:name="org.sufficientlysecure.keychain.WRITE_TEMPORARY_STORAGE" /> +    <uses-permission android:name="org.sufficientlysecure.keychain.WRITE_TEMPORARY_STORAGE" />      <uses-permission android:name="android.permission.INTERNET" />      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />      <uses-permission android:name="android.permission.NFC" />      <uses-permission android:name="android.permission.GET_ACCOUNTS" /> -    <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/> -    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/> -    <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/> -    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/> +    <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" /> +    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" /> +    <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" /> +    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />      <uses-permission android:name="android.permission.READ_CONTACTS" />      <uses-permission android:name="android.permission.WRITE_CONTACTS" />      <uses-permission android:name="android.permission.READ_PROFILE" /> @@ -84,20 +84,26 @@              </intent-filter>          </activity>          <activity -            android:name=".ui.WizardActivity" +            android:name=".ui.FirstTimeActivity"              android:configChanges="orientation|screenSize|keyboardHidden|keyboard" -            android:label="@string/title_wizard" -            android:windowSoftInputMode="stateHidden" /> +            android:label="@string/app_name" +            android:windowSoftInputMode="stateAlwaysHidden" />          <activity -            android:name=".ui.EditKeyActivityOld" +            android:name=".ui.CreateKeyActivity"              android:configChanges="orientation|screenSize|keyboardHidden|keyboard" -            android:label="@string/title_edit_key" -            android:windowSoftInputMode="stateHidden" /> +            android:label="@string/title_create_key"> +            <meta-data +                android:name="android.support.PARENT_ACTIVITY" +                android:value=".ui.KeyListActivity" /> +        </activity>          <activity              android:name=".ui.EditKeyActivity"              android:configChanges="orientation|screenSize|keyboardHidden|keyboard" -            android:label="@string/title_edit_key" -            android:windowSoftInputMode="stateHidden" /> +            android:label="@string/title_edit_key" /> +        <activity +            android:name=".ui.QrCodeActivity" +            android:configChanges="orientation|screenSize|keyboardHidden|keyboard" +            android:label="@string/share_qr_code_dialog_title" />          <activity              android:name=".ui.ViewKeyActivity"              android:configChanges="orientation|screenSize|keyboardHidden|keyboard" @@ -221,8 +227,19 @@                  <data android:host="*" />                  <data android:scheme="file" />                  <data android:scheme="content" /> -                <data android:mimeType="*/*"/> -                <!-- Workaround to match files in pathes with dots in them, like /cdcard/my.folder/test.gpg --> +                 +                <!-- GnuPG ASCII data, mostly keys, but sometimes signatures and encrypted data --> +                <data android:pathPattern=".*\\.asc" /> +                <data android:pathPattern=".*\\..*\\.asc" /> +                <data android:pathPattern=".*\\..*\\..*\\.asc" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\.asc" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.asc" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.asc" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" /> +                <!-- GnuPG binary encrypted/signed data, binary format -->                  <data android:pathPattern=".*\\.gpg" />                  <data android:pathPattern=".*\\..*\\.gpg" />                  <data android:pathPattern=".*\\..*\\..*\\.gpg" /> @@ -233,7 +250,34 @@                  <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />                  <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />                  <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" /> +                <!-- PGP encrypted data, binary format --> +                <data android:pathPattern=".*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <!-- on some mail clients, PGP attachments show up as *.bin --> +                <data android:pathPattern=".*\\.bin" /> +                <data android:pathPattern=".*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />              </intent-filter> +            <!-- +                Some apps will only respect these file associations +                if the mimeType is not set, and other apps will only respect them if mimeType is set +                to */*. Therefore we have two whole copies of the same thing, besides setting the mimeType. +            -->              <intent-filter android:label="@string/intent_decrypt_file">                  <action android:name="android.intent.action.VIEW" /> @@ -243,7 +287,10 @@                  <data android:host="*" />                  <data android:scheme="file" />                  <data android:scheme="content" /> +                  <data android:mimeType="*/*" /> + +                <!-- GnuPG ASCII data, mostly keys, but sometimes signatures and encrypted data -->                  <data android:pathPattern=".*\\.asc" />                  <data android:pathPattern=".*\\..*\\.asc" />                  <data android:pathPattern=".*\\..*\\..*\\.asc" /> @@ -254,6 +301,39 @@                  <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />                  <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />                  <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" /> +                <!-- GnuPG binary encrypted/signed data, binary format --> +                <data android:pathPattern=".*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\..*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" /> +                <!-- PGP encrypted data, binary format --> +                <data android:pathPattern=".*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <!-- on some mail clients, PGP attachments show up as *.bin --> +                <data android:pathPattern=".*\\.bin" /> +                <data android:pathPattern=".*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />              </intent-filter>          </activity>          <activity @@ -323,6 +403,9 @@                  <!-- mime type as defined in http://tools.ietf.org/html/rfc3156 -->                  <data android:mimeType="application/pgp-keys" /> +                <!-- also link to text/plain, AOSP mail and K-9 mail only give mimeType text/plain +                when the key file has been manually attached --> +                <data android:mimeType="text/plain" />              </intent-filter>              <!-- NFC: Handle NFC tags detected from outside our application -->              <intent-filter> @@ -342,6 +425,19 @@                  <data android:host="*" />                  <data android:scheme="file" />                  <data android:scheme="content" /> + +                <!-- GnuPG ASCII data, mostly keys, but sometimes signatures and encrypted data --> +                <data android:pathPattern=".*\\.asc" /> +                <data android:pathPattern=".*\\..*\\.asc" /> +                <data android:pathPattern=".*\\..*\\..*\\.asc" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\.asc" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.asc" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.asc" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" /> +                <!-- GnuPG binary encrypted/signed data, binary format -->                  <data android:pathPattern=".*\\.gpg" />                  <data android:pathPattern=".*\\..*\\.gpg" />                  <data android:pathPattern=".*\\..*\\..*\\.gpg" /> @@ -352,8 +448,34 @@                  <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />                  <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />                  <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" /> +                <!-- PGP encrypted data, binary format --> +                <data android:pathPattern=".*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <!-- on some mail clients, PGP attachments show up as *.bin --> +                <data android:pathPattern=".*\\.bin" /> +                <data android:pathPattern=".*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />              </intent-filter> -            <!-- VIEW with file endings: *.asc --> +            <!-- +                Some apps will only respect these file associations +                if the mimeType is not set, and other apps will only respect them if mimeType is set +                to */*. Therefore we have two whole copies of the same thing, besides setting the mimeType. +            -->              <intent-filter android:label="@string/intent_import_key">                  <action android:name="android.intent.action.VIEW" /> @@ -363,7 +485,10 @@                  <data android:host="*" />                  <data android:scheme="file" />                  <data android:scheme="content" /> +                  <data android:mimeType="*/*" /> + +                <!-- GnuPG ASCII data, mostly keys, but sometimes signatures and encrypted data -->                  <data android:pathPattern=".*\\.asc" />                  <data android:pathPattern=".*\\..*\\.asc" />                  <data android:pathPattern=".*\\..*\\..*\\.asc" /> @@ -374,7 +499,41 @@                  <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />                  <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />                  <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" /> +                <!-- GnuPG binary encrypted/signed data, binary format --> +                <data android:pathPattern=".*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\..*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" /> +                <!-- PGP encrypted data, binary format --> +                <data android:pathPattern=".*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" /> +                <!-- on some mail clients, PGP attachments show up as *.bin --> +                <data android:pathPattern=".*\\.bin" /> +                <data android:pathPattern=".*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" /> +                <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />              </intent-filter> +              <!-- Keychain's own Actions -->              <!-- IMPORT_KEY with files TODO: does this work? -->              <intent-filter android:label="@string/intent_import_key"> @@ -469,31 +628,31 @@          <service android:name=".service.DummyAccountService">              <intent-filter> -                <action android:name="android.accounts.AccountAuthenticator"/> +                <action android:name="android.accounts.AccountAuthenticator" />              </intent-filter>              <meta-data -                    android:name="android.accounts.AccountAuthenticator" -                    android:resource="@xml/account_desc"/> +                android:name="android.accounts.AccountAuthenticator" +                android:resource="@xml/account_desc" />          </service>          <service android:name=".service.ContactSyncAdapterService">              <intent-filter> -                <action android:name="android.content.SyncAdapter"/> +                <action android:name="android.content.SyncAdapter" />              </intent-filter>              <meta-data -                    android:name="android.content.SyncAdapter" -                    android:resource="@xml/sync_adapter_desc"/> +                android:name="android.content.SyncAdapter" +                android:resource="@xml/sync_adapter_desc" />              <meta-data -                    android:name="android.provider.CONTACTS_STRUCTURE" -                    android:resource="@xml/custom_pgp_contacts_structure"/> +                android:name="android.provider.CONTACTS_STRUCTURE" +                android:resource="@xml/custom_pgp_contacts_structure" />          </service>          <provider                  android:name=".provider.TemporaryStorageProvider"                  android:authorities="org.sufficientlysecure.keychain.tempstorage"                  android:writePermission="org.sufficientlysecure.keychain.WRITE_TEMPORARY_STORAGE" -                android:exported="true"/> +                android:exported="true" />      </application> diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index 956019605..b679ef210 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -71,6 +71,8 @@ public final class Constants {          public static final String FORCE_V3_SIGNATURES = "forceV3Signatures";          public static final String KEY_SERVERS = "keyServers";          public static final String KEY_SERVERS_DEFAULT_VERSION = "keyServersDefaultVersion"; +        public static final String CONCEAL_PGP_APPLICATION = "concealPgpApplication"; +        public static final String FIRST_TIME = "firstTime";      }      public static final class Defaults { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java index 6f3d38ccd..5d765b663 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java @@ -139,6 +139,16 @@ public class Preferences {          editor.commit();      } +    public boolean isFirstTime() { +        return mSharedPreferences.getBoolean(Constants.Pref.FIRST_TIME, true); +    } + +    public void setFirstTime(boolean value) { +        SharedPreferences.Editor editor = mSharedPreferences.edit(); +        editor.putBoolean(Constants.Pref.FIRST_TIME, value); +        editor.commit(); +    } +      public String[] getKeyServers() {          String rawData = mSharedPreferences.getString(Constants.Pref.KEY_SERVERS,                  Constants.Defaults.KEY_SERVERS); @@ -187,4 +197,14 @@ public class Preferences {                      .commit();          }      } + +    public void setConcealPgpApplication(boolean conceal) { +        SharedPreferences.Editor editor = mSharedPreferences.edit(); +        editor.putBoolean(Constants.Pref.CONCEAL_PGP_APPLICATION, conceal); +        editor.commit(); +    } + +    public boolean getConcealPgpApplication() { +        return mSharedPreferences.getBoolean(Constants.Pref.CONCEAL_PGP_APPLICATION, false); +    }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FileImportCache.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FileImportCache.java new file mode 100644 index 000000000..08b8afae7 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FileImportCache.java @@ -0,0 +1,97 @@ +/* + * 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.keyimport; + +import android.content.Context; +import android.os.Bundle; +import android.os.Parcel; + +import org.sufficientlysecure.keychain.KeychainApplication; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * When sending large data (over 1MB) through Androids Binder IPC you get + * JavaBinder  E  !!! FAILED BINDER TRANSACTION !!! + * <p/> + * To overcome this problem, we cache large Parcelables into a file in our private cache directory + * instead of sending them through IPC. + */ +public class FileImportCache { + +    private Context mContext; + +    private static final String FILENAME = "key_import.pcl"; +    private static final String BUNDLE_DATA = "data"; + +    public FileImportCache(Context context) { +        this.mContext = context; +    } + +    public void writeCache(ArrayList<ParcelableKeyRing> selectedEntries) throws IOException { +        Bundle in = new Bundle(); +        in.putParcelableArrayList(BUNDLE_DATA, selectedEntries); +        File cacheDir = mContext.getCacheDir(); +        if (cacheDir == null) { +            // https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU +            throw new IOException("cache dir is null!"); +        } +        File tempFile = new File(mContext.getCacheDir(), FILENAME); + +        FileOutputStream fos = new FileOutputStream(tempFile); +        Parcel p = Parcel.obtain(); // creating empty parcel object +        in.writeToParcel(p, 0); // saving bundle as parcel +        fos.write(p.marshall()); // writing parcel to file +        fos.flush(); +        fos.close(); +    } + +    public List<ParcelableKeyRing> readCache() throws IOException { +        Parcel parcel = Parcel.obtain(); // creating empty parcel object +        Bundle out; +        File cacheDir = mContext.getCacheDir(); +        if (cacheDir == null) { +            // https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU +            throw new IOException("cache dir is null!"); +        } + +        File tempFile = new File(cacheDir, FILENAME); +        try { +            FileInputStream fis = new FileInputStream(tempFile); +            byte[] array = new byte[(int) fis.getChannel().size()]; +            fis.read(array, 0, array.length); +            fis.close(); + +            parcel.unmarshall(array, 0, array.length); +            parcel.setDataPosition(0); +            out = parcel.readBundle(KeychainApplication.class.getClassLoader()); +            out.putAll(out); + +            return out.getParcelableArrayList(BUNDLE_DATA); +        } finally { +            parcel.recycle(); +            // delete temp file +            tempFile.delete(); +        } +    } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java index 44679ba18..43bed8397 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java @@ -22,6 +22,7 @@ import de.measite.minidns.Client;  import de.measite.minidns.Question;  import de.measite.minidns.Record;  import de.measite.minidns.record.SRV; +  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.helper.TlsHelper;  import org.sufficientlysecure.keychain.pgp.PgpHelper; @@ -102,8 +103,9 @@ public class HkpKeyserver extends Keyserver {       */      public static final Pattern PUB_KEY_LINE = Pattern              .compile("pub:([0-9a-fA-F]+):([0-9]+):([0-9]+):([0-9]+):([0-9]*):([rde]*)[ \n\r]*" // pub line -                    + "((uid:([^:]*):([0-9]+):([0-9]*):([rde]*)[ \n\r]*)+)", // one or more uid lines -                    Pattern.CASE_INSENSITIVE); +                            + "((uid:([^:]*):([0-9]+):([0-9]*):([rde]*)[ \n\r]*)+)", // one or more uid lines +                    Pattern.CASE_INSENSITIVE +            );      /**       * uid:%escaped uid string%:%creationdate%:%expirationdate%:%flags% @@ -215,10 +217,18 @@ public class HkpKeyserver extends Keyserver {                  throw new HttpError(response, data);              }          } catch (IOException e) { -            throw new QueryFailedException("querying server(s) for '" + mHost + "' failed"); +            throw new QueryFailedException("Keyserver '" + mHost + "' is unavailable. Check your Internet connection!");          }      } +    /** +     * Results are sorted by creation date of key! +     * +     * @param query +     * @return +     * @throws QueryFailedException +     * @throws QueryNeedsRepairException +     */      @Override      public ArrayList<ImportKeysListEntry> search(String query) throws QueryFailedException,              QueryNeedsRepairException { @@ -240,18 +250,26 @@ public class HkpKeyserver extends Keyserver {          try {              data = query(request);          } catch (HttpError e) { -            if (e.getCode() == 404) { -                return results; -            } else { +            if (e.getData() != null) { +                Log.d(Constants.TAG, "returned error data: " + e.getData().toLowerCase(Locale.US)); +                  if (e.getData().toLowerCase(Locale.US).contains("no keys found")) { +                    // NOTE: This is also a 404 error for some keyservers!                      return results;                  } else if (e.getData().toLowerCase(Locale.US).contains("too many")) {                      throw new TooManyResponsesException();                  } else if (e.getData().toLowerCase(Locale.US).contains("insufficient")) {                      throw new QueryTooShortException(); +                } else if (e.getCode() == 404) { +                    // NOTE: handle this 404 at last, maybe it was a "no keys found" error +                    throw new QueryFailedException("Keyserver '" + mHost + "' not found. Error 404"); +                } else { +                    // NOTE: some keyserver do not provide a more detailed error response +                    throw new QueryTooShortOrTooManyResponsesException();                  }              } -            throw new QueryFailedException("querying server(s) for '" + mHost + "' failed"); + +            throw new QueryFailedException("Querying server(s) for '" + mHost + "' failed.");          }          final Matcher matcher = PUB_KEY_LINE.matcher(data); @@ -291,7 +309,7 @@ public class HkpKeyserver extends Keyserver {              while (uidMatcher.find()) {                  String tmp = uidMatcher.group(1).trim();                  if (tmp.contains("%")) { -                    if(tmp.contains("%%")) { +                    if (tmp.contains("%%")) {                          // This is a fix for issue #683                          // The server encodes a percent sign as %%, so it is swapped out with its                          // urlencoded counterpart to prevent errors diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java index 0a49cb629..30e93f957 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -250,7 +250,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {          mHashCode = key.hashCode(); -        mPrimaryUserId = key.getPrimaryUserId(); +        mPrimaryUserId = key.getPrimaryUserIdWithFallback();          mUserIds = key.getUnorderedUserIds();          // if there was no user id flagged as primary, use the first one diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java index 842e7d922..b726529f8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java @@ -44,6 +44,13 @@ public abstract class Keyserver {          private static final long serialVersionUID = 2703768928624654514L;      } +    /** +     * query too short _or_ too many responses +     */ +    public static class QueryTooShortOrTooManyResponsesException extends QueryNeedsRepairException { +        private static final long serialVersionUID = 2703768928624654514L; +    } +      public static class AddKeyException extends Exception {          private static final long serialVersionUID = -507574859137295530L;      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java index 6f3068261..ee0dfefa4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java @@ -16,13 +16,11 @@ import java.io.OutputStream;   * getter method.   *   */ -public abstract class WrappedKeyRing extends KeyRing { +public abstract class CanonicalizedKeyRing extends KeyRing { -    private final boolean mHasAnySecret;      private final int mVerified; -    WrappedKeyRing(boolean hasAnySecret, int verified) { -        mHasAnySecret = hasAnySecret; +    CanonicalizedKeyRing(int verified) {          mVerified = verified;      } @@ -30,17 +28,17 @@ public abstract class WrappedKeyRing extends KeyRing {          return getRing().getPublicKey().getKeyID();      } -    public boolean hasAnySecret() { -        return mHasAnySecret; -    } -      public int getVerified() {          return mVerified;      }      public String getPrimaryUserId() throws PgpGeneralException { -        return (String) getRing().getPublicKey().getUserIDs().next(); -    }; +        return getPublicKey().getPrimaryUserId(); +    } + +    public String getPrimaryUserIdWithFallback() throws PgpGeneralException { +        return getPublicKey().getPrimaryUserIdWithFallback(); +    }      public boolean isRevoked() throws PgpGeneralException {          // Is the master key revoked? @@ -52,7 +50,7 @@ public abstract class WrappedKeyRing extends KeyRing {      }      public long getEncryptId() throws PgpGeneralException { -        for(WrappedPublicKey key : publicKeyIterator()) { +        for(CanonicalizedPublicKey key : publicKeyIterator()) {              if(key.canEncrypt()) {                  return key.getKeyId();              } @@ -70,7 +68,7 @@ public abstract class WrappedKeyRing extends KeyRing {      }      public long getSignId() throws PgpGeneralException { -        for(WrappedPublicKey key : publicKeyIterator()) { +        for(CanonicalizedPublicKey key : publicKeyIterator()) {              if(key.canSign()) {                  return key.getKeyId();              } @@ -99,10 +97,18 @@ public abstract class WrappedKeyRing extends KeyRing {      abstract PGPKeyRing getRing(); -    abstract public IterableIterator<WrappedPublicKey> publicKeyIterator(); +    abstract public IterableIterator<CanonicalizedPublicKey> publicKeyIterator(); -    public UncachedKeyRing getUncached() { -        return new UncachedKeyRing(getRing()); +    public CanonicalizedPublicKey getPublicKey() { +        return new CanonicalizedPublicKey(this, getRing().getPublicKey()); +    } + +    public CanonicalizedPublicKey getPublicKey(long id) { +        return new CanonicalizedPublicKey(this, getRing().getPublicKey(id)); +    } + +    public byte[] getEncoded() throws IOException { +        return getRing().getEncoded();      }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java index 69a4fbdee..981caad49 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java @@ -14,12 +14,12 @@ import org.sufficientlysecure.keychain.util.IterableIterator;   * stored in the database.   *   */ -public class WrappedPublicKey extends UncachedPublicKey { +public class CanonicalizedPublicKey extends UncachedPublicKey {      // this is the parent key ring      final KeyRing mRing; -    WrappedPublicKey(KeyRing ring, PGPPublicKey key) { +    CanonicalizedPublicKey(KeyRing ring, PGPPublicKey key) {          super(key);          mRing = ring;      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java index b2abf15a4..70288dceb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java @@ -1,42 +1,45 @@  package org.sufficientlysecure.keychain.pgp;  import org.spongycastle.bcpg.ArmoredOutputStream; -import org.spongycastle.openpgp.PGPKeyRing;  import org.spongycastle.openpgp.PGPObjectFactory;  import org.spongycastle.openpgp.PGPPublicKey;  import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.util.IterableIterator; -import org.sufficientlysecure.keychain.util.Log;  import java.io.IOException;  import java.util.Iterator; -public class WrappedPublicKeyRing extends WrappedKeyRing { +public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {      private PGPPublicKeyRing mRing; -    private final byte[] mPubKey; -    public WrappedPublicKeyRing(byte[] blob, boolean hasAnySecret, int verified) { -        super(hasAnySecret, verified); -        mPubKey = blob; +    CanonicalizedPublicKeyRing(PGPPublicKeyRing ring, int verified) { +        super(verified); +        mRing = ring;      } -    PGPPublicKeyRing getRing() { +    public CanonicalizedPublicKeyRing(byte[] blob, int verified) { +        super(verified);          if(mRing == null) { -            PGPObjectFactory factory = new PGPObjectFactory(mPubKey); -            PGPKeyRing keyRing = null; +            // get first object in block +            PGPObjectFactory factory = new PGPObjectFactory(blob);              try { -                if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) { -                    Log.e(Constants.TAG, "No keys given!"); +                Object obj = factory.nextObject(); +                if (! (obj instanceof PGPPublicKeyRing)) { +                    throw new RuntimeException("Error constructing CanonicalizedPublicKeyRing, should never happen!"); +                } +                mRing = (PGPPublicKeyRing) obj; +                if (factory.nextObject() != null) { +                    throw new RuntimeException("Encountered trailing data after keyring, should never happen!");                  }              } catch (IOException e) { -                Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e); +                throw new RuntimeException("IO Error constructing CanonicalizedPublicKeyRing, should never happen!");              } - -            mRing = (PGPPublicKeyRing) keyRing;          } +    } + +    PGPPublicKeyRing getRing() {          return mRing;      } @@ -44,19 +47,11 @@ public class WrappedPublicKeyRing extends WrappedKeyRing {          getRing().encode(stream);      } -    public WrappedPublicKey getSubkey() { -        return new WrappedPublicKey(this, getRing().getPublicKey()); -    } - -    public WrappedPublicKey getSubkey(long id) { -        return new WrappedPublicKey(this, getRing().getPublicKey(id)); -    } -      /** Getter that returns the subkey that should be used for signing. */ -    WrappedPublicKey getEncryptionSubKey() throws PgpGeneralException { +    CanonicalizedPublicKey getEncryptionSubKey() throws PgpGeneralException {          PGPPublicKey key = getRing().getPublicKey(getEncryptId());          if(key != null) { -            WrappedPublicKey cKey = new WrappedPublicKey(this, key); +            CanonicalizedPublicKey cKey = new CanonicalizedPublicKey(this, key);              if(!cKey.canEncrypt()) {                  throw new PgpGeneralException("key error");              } @@ -65,18 +60,18 @@ public class WrappedPublicKeyRing extends WrappedKeyRing {          throw new PgpGeneralException("no encryption key available");      } -    public IterableIterator<WrappedPublicKey> publicKeyIterator() { +    public IterableIterator<CanonicalizedPublicKey> publicKeyIterator() {          @SuppressWarnings("unchecked")          final Iterator<PGPPublicKey> it = getRing().getPublicKeys(); -        return new IterableIterator<WrappedPublicKey>(new Iterator<WrappedPublicKey>() { +        return new IterableIterator<CanonicalizedPublicKey>(new Iterator<CanonicalizedPublicKey>() {              @Override              public boolean hasNext() {                  return it.hasNext();              }              @Override -            public WrappedPublicKey next() { -                return new WrappedPublicKey(WrappedPublicKeyRing.this, it.next()); +            public CanonicalizedPublicKey next() { +                return new CanonicalizedPublicKey(CanonicalizedPublicKeyRing.this, it.next());              }              @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java index ef8044a9b..2eb517697 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java @@ -37,26 +37,18 @@ import java.util.List;   * properly imported secret keys only.   *   */ -public class WrappedSecretKey extends WrappedPublicKey { +public class CanonicalizedSecretKey extends CanonicalizedPublicKey {      private final PGPSecretKey mSecretKey;      private PGPPrivateKey mPrivateKey = null; -    WrappedSecretKey(WrappedSecretKeyRing ring, PGPSecretKey key) { +    CanonicalizedSecretKey(CanonicalizedSecretKeyRing ring, PGPSecretKey key) {          super(ring, key.getPublicKey());          mSecretKey = key;      } -    public WrappedSecretKeyRing getRing() { -        return (WrappedSecretKeyRing) mRing; -    } - -    /** Returns the wrapped PGPSecretKeyRing. -     * This function is for compatibility only, should not be used anymore and will be removed -     */ -    @Deprecated -    public PGPSecretKey getKeyExternal() { -        return mSecretKey; +    public CanonicalizedSecretKeyRing getRing() { +        return (CanonicalizedSecretKeyRing) mRing;      }      public boolean unlock(String passphrase) throws PgpGeneralException { @@ -97,7 +89,7 @@ public class WrappedSecretKey extends WrappedPublicKey {              signatureGenerator.init(signatureType, mPrivateKey);              PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); -            spGen.setSignerUserID(false, mRing.getPrimaryUserId()); +            spGen.setSignerUserID(false, mRing.getPrimaryUserIdWithFallback());              signatureGenerator.setHashedSubpackets(spGen.generate());              return signatureGenerator;          } catch(PGPException e) { @@ -148,7 +140,7 @@ public class WrappedSecretKey extends WrappedPublicKey {       * @param userIds          User IDs to certify, must not be null or empty       * @return A keyring with added certifications       */ -    public UncachedKeyRing certifyUserIds(WrappedPublicKeyRing publicKeyRing, List<String> userIds) +    public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing, List<String> userIds)              throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,              PGPException, SignatureException { @@ -175,7 +167,7 @@ public class WrappedSecretKey extends WrappedPublicKey {          }          // get the master subkey (which we certify for) -        PGPPublicKey publicKey = publicKeyRing.getSubkey().getPublicKey(); +        PGPPublicKey publicKey = publicKeyRing.getPublicKey().getPublicKey();          // fetch public key ring, add the certification and return it          for (String userId : new IterableIterator<String>(userIds.iterator())) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java index c737b7c46..e48fe5020 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java @@ -1,10 +1,12 @@  package org.sufficientlysecure.keychain.pgp; +import org.spongycastle.bcpg.S2K;  import org.spongycastle.openpgp.PGPException;  import org.spongycastle.openpgp.PGPKeyRing;  import org.spongycastle.openpgp.PGPObjectFactory;  import org.spongycastle.openpgp.PGPPrivateKey;  import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing;  import org.spongycastle.openpgp.PGPSecretKey;  import org.spongycastle.openpgp.PGPSecretKeyRing;  import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; @@ -15,15 +17,21 @@ import org.sufficientlysecure.keychain.util.IterableIterator;  import org.sufficientlysecure.keychain.util.Log;  import java.io.IOException; +import java.util.HashSet;  import java.util.Iterator; -public class WrappedSecretKeyRing extends WrappedKeyRing { +public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {      private PGPSecretKeyRing mRing; -    public WrappedSecretKeyRing(byte[] blob, boolean isRevoked, int verified) +    CanonicalizedSecretKeyRing(PGPSecretKeyRing ring, int verified) { +        super(verified); +        mRing = ring; +    } + +    public CanonicalizedSecretKeyRing(byte[] blob, boolean isRevoked, int verified)      { -        super(isRevoked, verified); +        super(verified);          PGPObjectFactory factory = new PGPObjectFactory(blob);          PGPKeyRing keyRing = null;          try { @@ -41,19 +49,32 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {          return mRing;      } -    public WrappedSecretKey getSubKey() { -        return new WrappedSecretKey(this, mRing.getSecretKey()); +    public CanonicalizedSecretKey getSecretKey() { +        return new CanonicalizedSecretKey(this, mRing.getSecretKey()); +    } + +    public CanonicalizedSecretKey getSecretKey(long id) { +        return new CanonicalizedSecretKey(this, mRing.getSecretKey(id));      } -    public WrappedSecretKey getSubKey(long id) { -        return new WrappedSecretKey(this, mRing.getSecretKey(id)); +    public HashSet<Long> getAvailableSubkeys() { +        HashSet<Long> result = new HashSet<Long>(); +        // then, mark exactly the keys we have available +        for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(getRing().getSecretKeys())) { +            S2K s2k = sub.getS2K(); +            // Set to 1, except if the encryption type is GNU_DUMMY_S2K +            if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) { +                result.add(sub.getKeyID()); +            } +        } +        return result;      }      /** Getter that returns the subkey that should be used for signing. */ -    WrappedSecretKey getSigningSubKey() throws PgpGeneralException { +    CanonicalizedSecretKey getSigningSubKey() throws PgpGeneralException {          PGPSecretKey key = mRing.getSecretKey(getSignId());          if(key != null) { -            WrappedSecretKey cKey = new WrappedSecretKey(this, key); +            CanonicalizedSecretKey cKey = new CanonicalizedSecretKey(this, key);              if(!cKey.canSign()) {                  throw new PgpGeneralException("key error");              } @@ -88,17 +109,17 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {          }      } -    public IterableIterator<WrappedSecretKey> secretKeyIterator() { +    public IterableIterator<CanonicalizedSecretKey> secretKeyIterator() {          final Iterator<PGPSecretKey> it = mRing.getSecretKeys(); -        return new IterableIterator<WrappedSecretKey>(new Iterator<WrappedSecretKey>() { +        return new IterableIterator<CanonicalizedSecretKey>(new Iterator<CanonicalizedSecretKey>() {              @Override              public boolean hasNext() {                  return it.hasNext();              }              @Override -            public WrappedSecretKey next() { -                return new WrappedSecretKey(WrappedSecretKeyRing.this, it.next()); +            public CanonicalizedSecretKey next() { +                return new CanonicalizedSecretKey(CanonicalizedSecretKeyRing.this, it.next());              }              @Override @@ -108,17 +129,17 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {          });      } -    public IterableIterator<WrappedPublicKey> publicKeyIterator() { +    public IterableIterator<CanonicalizedPublicKey> publicKeyIterator() {          final Iterator<PGPPublicKey> it = getRing().getPublicKeys(); -        return new IterableIterator<WrappedPublicKey>(new Iterator<WrappedPublicKey>() { +        return new IterableIterator<CanonicalizedPublicKey>(new Iterator<CanonicalizedPublicKey>() {              @Override              public boolean hasNext() {                  return it.hasNext();              }              @Override -            public WrappedPublicKey next() { -                return new WrappedPublicKey(WrappedSecretKeyRing.this, it.next()); +            public CanonicalizedPublicKey next() { +                return new CanonicalizedPublicKey(CanonicalizedSecretKeyRing.this, it.next());              }              @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java index 47b827677..ebc49ab05 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java @@ -12,7 +12,7 @@ import java.util.regex.Pattern;   * keyring should in all cases agree on the output of all methods described   * here.   * - * @see org.sufficientlysecure.keychain.pgp.WrappedKeyRing + * @see CanonicalizedKeyRing   * @see org.sufficientlysecure.keychain.provider.CachedPublicKeyRing   *   */ @@ -22,8 +22,10 @@ public abstract class KeyRing {      abstract public String getPrimaryUserId() throws PgpGeneralException; -    public String[] getSplitPrimaryUserId() throws PgpGeneralException { -        return splitUserId(getPrimaryUserId()); +    abstract public String getPrimaryUserIdWithFallback() throws PgpGeneralException; + +    public String[] getSplitPrimaryUserIdWithFallback() throws PgpGeneralException { +        return splitUserId(getPrimaryUserIdWithFallback());      }      abstract public boolean isRevoked() throws PgpGeneralException; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java index a5ccfbd3b..7f2d971ed 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -231,7 +231,7 @@ public class PgpDecryptVerify {          PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;          PGPPBEEncryptedData encryptedDataSymmetric = null; -        WrappedSecretKey secretEncryptionKey = null; +        CanonicalizedSecretKey secretEncryptionKey = null;          Iterator<?> it = enc.getEncryptedDataObjects();          boolean asymmetricPacketFound = false;          boolean symmetricPacketFound = false; @@ -243,10 +243,10 @@ public class PgpDecryptVerify {                  PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; -                WrappedSecretKeyRing secretKeyRing; +                CanonicalizedSecretKeyRing secretKeyRing;                  try {                      // get actual keyring object based on master key id -                    secretKeyRing = mProviderHelper.getWrappedSecretKeyRing( +                    secretKeyRing = mProviderHelper.getCanonicalizedSecretKeyRing(                              KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(encData.getKeyID())                      );                  } catch (ProviderHelper.NotFoundException e) { @@ -258,7 +258,7 @@ public class PgpDecryptVerify {                      continue;                  }                  // get subkey which has been used for this encryption packet -                secretEncryptionKey = secretKeyRing.getSubKey(encData.getKeyID()); +                secretEncryptionKey = secretKeyRing.getSecretKey(encData.getKeyID());                  if (secretEncryptionKey == null) {                      // continue with the next packet in the while loop                      continue; @@ -365,8 +365,8 @@ public class PgpDecryptVerify {          Object dataChunk = plainFact.nextObject();          OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();          int signatureIndex = -1; -        WrappedPublicKeyRing signingRing = null; -        WrappedPublicKey signingKey = null; +        CanonicalizedPublicKeyRing signingRing = null; +        CanonicalizedPublicKey signingKey = null;          if (dataChunk instanceof PGPCompressedData) {              updateProgress(R.string.progress_decompressing_data, currentProgress, 100); @@ -390,10 +390,10 @@ public class PgpDecryptVerify {              for (int i = 0; i < sigList.size(); ++i) {                  try {                      long sigKeyId = sigList.get(i).getKeyID(); -                    signingRing = mProviderHelper.getWrappedPublicKeyRing( +                    signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(                              KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)                      ); -                    signingKey = signingRing.getSubkey(sigKeyId); +                    signingKey = signingRing.getPublicKey(sigKeyId);                      signatureIndex = i;                  } catch (ProviderHelper.NotFoundException e) {                      Log.d(Constants.TAG, "key not found!"); @@ -409,7 +409,7 @@ public class PgpDecryptVerify {                  signatureResultBuilder.knownKey(true);                  signatureResultBuilder.keyId(signingRing.getMasterKeyId());                  try { -                    signatureResultBuilder.userId(signingRing.getPrimaryUserId()); +                    signatureResultBuilder.userId(signingRing.getPrimaryUserIdWithFallback());                  } catch(PgpGeneralException e) {                      Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());                  } @@ -566,8 +566,8 @@ public class PgpDecryptVerify {              throw new InvalidDataException();          } -        WrappedPublicKeyRing signingRing = null; -        WrappedPublicKey signingKey = null; +        CanonicalizedPublicKeyRing signingRing = null; +        CanonicalizedPublicKey signingKey = null;          int signatureIndex = -1;          // go through all signatures @@ -575,10 +575,10 @@ public class PgpDecryptVerify {          for (int i = 0; i < sigList.size(); ++i) {              try {                  long sigKeyId = sigList.get(i).getKeyID(); -                signingRing = mProviderHelper.getWrappedPublicKeyRing( +                signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(                          KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)                  ); -                signingKey = signingRing.getSubkey(sigKeyId); +                signingKey = signingRing.getPublicKey(sigKeyId);                  signatureIndex = i;              } catch (ProviderHelper.NotFoundException e) {                  Log.d(Constants.TAG, "key not found!"); @@ -596,7 +596,7 @@ public class PgpDecryptVerify {              signatureResultBuilder.knownKey(true);              signatureResultBuilder.keyId(signingRing.getMasterKeyId());              try { -                signatureResultBuilder.userId(signingRing.getPrimaryUserId()); +                signatureResultBuilder.userId(signingRing.getPrimaryUserIdWithFallback());              } catch(PgpGeneralException e) {                  Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());              } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java index 969ff1de8..0ceefc4d9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java @@ -24,6 +24,7 @@ import android.content.pm.PackageManager.NameNotFoundException;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.Preferences;  import org.sufficientlysecure.keychain.util.Log;  import java.io.File; @@ -60,7 +61,11 @@ public class PgpHelper {      }      public static String getFullVersion(Context context) { -        return "OpenKeychain v" + getVersion(context); +        if(Preferences.getPreferences(context).getConcealPgpApplication()){ +            return ""; +        } else { +            return "OpenKeychain v" + getVersion(context); +        }      }  //    public static final class content { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index 6fc55cfb8..846b00ef2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -34,7 +34,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; -import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; +import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult;  import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult;  import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.ProgressScaler; @@ -93,7 +93,7 @@ public class PgpImportExport {          }      } -    public boolean uploadKeyRingToServer(HkpKeyserver server, WrappedPublicKeyRing keyring) { +    public boolean uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring) {          ByteArrayOutputStream bos = new ByteArrayOutputStream();          ArmoredOutputStream aos = null;          try { @@ -123,14 +123,14 @@ public class PgpImportExport {      }      /** Imports keys from given data. If keyIds is given only those are imported */ -    public ImportResult importKeyRings(List<ParcelableKeyRing> entries) { +    public ImportKeyResult importKeyRings(List<ParcelableKeyRing> entries) {          updateProgress(R.string.progress_importing, 0, 100);          // If there aren't even any keys, do nothing here.          if (entries == null || entries.size() == 0) { -            return new ImportResult( -                    ImportResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0); +            return new ImportKeyResult( +                    ImportKeyResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0);          }          int newKeys = 0, oldKeys = 0, badKeys = 0; @@ -185,26 +185,26 @@ public class PgpImportExport {          int resultType = 0;          // special return case: no new keys at all          if (badKeys == 0 && newKeys == 0 && oldKeys == 0) { -            resultType = ImportResult.RESULT_FAIL_NOTHING; +            resultType = ImportKeyResult.RESULT_FAIL_NOTHING;          } else {              if (newKeys > 0) { -                resultType |= ImportResult.RESULT_OK_NEWKEYS; +                resultType |= ImportKeyResult.RESULT_OK_NEWKEYS;              }              if (oldKeys > 0) { -                resultType |= ImportResult.RESULT_OK_UPDATED; +                resultType |= ImportKeyResult.RESULT_OK_UPDATED;              }              if (badKeys > 0) { -                resultType |= ImportResult.RESULT_WITH_ERRORS; +                resultType |= ImportKeyResult.RESULT_WITH_ERRORS;                  if (newKeys == 0 && oldKeys == 0) { -                    resultType |= ImportResult.RESULT_ERROR; +                    resultType |= ImportKeyResult.RESULT_ERROR;                  }              }              if (log.containsWarnings()) { -                resultType |= ImportResult.RESULT_WITH_WARNINGS; +                resultType |= ImportKeyResult.RESULT_WITH_WARNINGS;              }          } -        return new ImportResult(resultType, log, newKeys, oldKeys, badKeys); +        return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys);      } @@ -235,7 +235,7 @@ public class PgpImportExport {              updateProgress(progress * 100 / masterKeyIdsSize, 100);              try { -                WrappedPublicKeyRing ring = mProviderHelper.getWrappedPublicKeyRing( +                CanonicalizedPublicKeyRing ring = mProviderHelper.getCanonicalizedPublicKeyRing(                          KeychainContract.KeyRings.buildUnifiedKeyRingUri(pubKeyMasterId)                  ); @@ -263,8 +263,8 @@ public class PgpImportExport {              updateProgress(progress * 100 / masterKeyIdsSize, 100);              try { -                WrappedSecretKeyRing secretKeyRing = -                        mProviderHelper.getWrappedSecretKeyRing(secretKeyMasterId); +                CanonicalizedSecretKeyRing secretKeyRing = +                        mProviderHelper.getCanonicalizedSecretKeyRing(secretKeyMasterId);                  secretKeyRing.encode(arOutStream);              } catch (ProviderHelper.NotFoundException e) {                  Log.e(Constants.TAG, "key not found!", e); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 9a08290e4..b1bd6a77a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -46,15 +46,17 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;  import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; +import org.sufficientlysecure.keychain.service.OperationResultParcel;  import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel;  import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;  import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; +import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;  import org.sufficientlysecure.keychain.service.SaveKeyringParcel;  import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;  import org.sufficientlysecure.keychain.util.IterableIterator;  import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.Primes; +import org.sufficientlysecure.keychain.util.ProgressScaler;  import java.io.IOException;  import java.math.BigInteger; @@ -65,10 +67,9 @@ import java.security.NoSuchProviderException;  import java.security.SecureRandom;  import java.security.SignatureException;  import java.util.Arrays; -import java.util.Calendar;  import java.util.Date;  import java.util.Iterator; -import java.util.TimeZone; +import java.util.Stack;  /**   * This class is the single place where ALL operations that actually modify a PGP public or secret @@ -80,7 +81,7 @@ import java.util.TimeZone;   * This indicator may be null.   */  public class PgpKeyOperation { -    private Progressable mProgress; +    private Stack<Progressable> mProgress;      private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{              SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192, @@ -94,21 +95,43 @@ public class PgpKeyOperation {      public PgpKeyOperation(Progressable progress) {          super(); -        this.mProgress = progress; +        if (progress != null) { +            mProgress = new Stack<Progressable>(); +            mProgress.push(progress); +        } +    } + +    private void subProgressPush(int from, int to) { +        if (mProgress == null) { +            return; +        } +        mProgress.push(new ProgressScaler(mProgress.peek(), from, to, 100)); +    } +    private void subProgressPop() { +        if (mProgress == null) { +            return; +        } +        if (mProgress.size() == 1) { +            throw new RuntimeException("Tried to pop progressable without prior push! " +                    + "This is a programming error, please file a bug report."); +        } +        mProgress.pop();      } -    void updateProgress(int message, int current, int total) { -        if (mProgress != null) { -            mProgress.setProgress(message, current, total); +    private void progress(int message, int current) { +        if (mProgress == null) { +            return;          } +        mProgress.peek().setProgress(message, current, 100);      }      /** Creates new secret key. */ -    private PGPKeyPair createKey(int algorithmChoice, int keySize) throws PgpGeneralMsgIdException { +    private PGPKeyPair createKey(int algorithmChoice, int keySize, OperationLog log, int indent) {          try {              if (keySize < 512) { -                throw new PgpGeneralMsgIdException(R.string.error_key_size_minimum512bit); +                log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_KEYSIZE_512, indent); +                return null;              }              int algorithm; @@ -116,6 +139,7 @@ public class PgpKeyOperation {              switch (algorithmChoice) {                  case Constants.choice.algorithm.dsa: { +                    progress(R.string.progress_generating_dsa, 30);                      keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);                      keyGen.initialize(keySize, new SecureRandom());                      algorithm = PGPPublicKey.DSA; @@ -123,6 +147,7 @@ public class PgpKeyOperation {                  }                  case Constants.choice.algorithm.elgamal: { +                    progress(R.string.progress_generating_elgamal, 30);                      keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);                      BigInteger p = Primes.getBestPrime(keySize);                      BigInteger g = new BigInteger("2"); @@ -135,6 +160,7 @@ public class PgpKeyOperation {                  }                  case Constants.choice.algorithm.rsa: { +                    progress(R.string.progress_generating_rsa, 30);                      keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);                      keyGen.initialize(keySize, new SecureRandom()); @@ -143,7 +169,8 @@ public class PgpKeyOperation {                  }                  default: { -                    throw new PgpGeneralMsgIdException(R.string.error_unknown_algorithm_choice); +                    log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent); +                    return null;                  }              } @@ -157,31 +184,55 @@ public class PgpKeyOperation {          } catch(InvalidAlgorithmParameterException e) {              throw new RuntimeException(e);          } catch(PGPException e) { -            throw new PgpGeneralMsgIdException(R.string.msg_mf_error_pgp, e); +            Log.e(Constants.TAG, "internal pgp error", e); +            log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_INTERNAL_PGP, indent); +            return null;          }      } -    public UncachedKeyRing createSecretKeyRing(SaveKeyringParcel saveParcel, OperationLog log, -                                               int indent) { +    public EditKeyResult createSecretKeyRing(SaveKeyringParcel saveParcel) { + +        OperationLog log = new OperationLog(); +        int indent = 0;          try { -            log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_KEYID, indent); +            log.add(LogLevel.START, LogType.MSG_CR, indent); +            progress(R.string.progress_building_key, 0);              indent += 1; -            updateProgress(R.string.progress_building_key, 0, 100); -            if (saveParcel.addSubKeys == null || saveParcel.addSubKeys.isEmpty()) { +            if (saveParcel.mAddSubKeys.isEmpty()) {                  log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_MASTER, indent); -                return null; +                return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); +            } + +            if (saveParcel.mAddUserIds.isEmpty()) { +                log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_USER_ID, indent); +                return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);              } -            SubkeyAdd add = saveParcel.addSubKeys.remove(0); -            PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize); +            SubkeyAdd add = saveParcel.mAddSubKeys.remove(0); +            if ((add.mFlags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) { +                log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_CERTIFY, indent); +                return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); +            }              if (add.mAlgorithm == Constants.choice.algorithm.elgamal) { -                throw new PgpGeneralMsgIdException(R.string.error_master_key_must_not_be_el_gamal); +                log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_MASTER_ELGAMAL, indent); +                return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);              } +            subProgressPush(10, 30); +            PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent); +            subProgressPop(); + +            // return null if this failed (an error will already have been logged by createKey) +            if (keyPair == null) { +                return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); +            } + +            progress(R.string.progress_building_master_key, 40); +              // define hashing and signing algos              PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()                      .build().get(HashAlgorithmTags.SHA1); @@ -195,17 +246,16 @@ public class PgpKeyOperation {              PGPSecretKeyRing sKR = new PGPSecretKeyRing(                      masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator()); -            return internal(sKR, masterSecretKey, saveParcel, "", log, indent); +            subProgressPush(50, 100); +            return internal(sKR, masterSecretKey, add.mFlags, saveParcel, "", log);          } catch (PGPException e) { +            log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);              Log.e(Constants.TAG, "pgp error encoding key", e); -            return null; +            return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);          } catch (IOException e) {              Log.e(Constants.TAG, "io error encoding key", e); -            return null; -        } catch (PgpGeneralMsgIdException e) { -            Log.e(Constants.TAG, "pgp msg id error", e); -            return null; +            return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);          }      } @@ -221,8 +271,11 @@ public class PgpKeyOperation {       * are changed by adding new certificates, which implicitly override older certificates.       *       */ -    public UncachedKeyRing modifySecretKeyRing(WrappedSecretKeyRing wsKR, SaveKeyringParcel saveParcel, -                                               String passphrase, OperationLog log, int indent) { +    public EditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel, +                                               String passphrase) { + +        OperationLog log = new OperationLog(); +        int indent = 0;          /*           * 1. Unlock private key @@ -235,14 +288,15 @@ public class PgpKeyOperation {           * 6. If requested, change passphrase           */ -        log.add(LogLevel.START, LogType.MSG_MF, indent); +        log.add(LogLevel.START, LogType.MSG_MF, indent, +                PgpKeyHelper.convertKeyIdToHex(wsKR.getMasterKeyId()));          indent += 1; -        updateProgress(R.string.progress_building_key, 0, 100); +        progress(R.string.progress_building_key, 0);          // Make sure this is called with a proper SaveKeyringParcel          if (saveParcel.mMasterKeyId == null || saveParcel.mMasterKeyId != wsKR.getMasterKeyId()) {              log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_KEYID, indent); -            return null; +            return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);          }          // We work on bouncycastle object level here @@ -254,22 +308,30 @@ public class PgpKeyOperation {                  || !Arrays.equals(saveParcel.mFingerprint,                                      masterSecretKey.getPublicKey().getFingerprint())) {              log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_FINGERPRINT, indent); -            return null; +            return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);          } -        return internal(sKR, masterSecretKey, saveParcel, passphrase, log, indent); +        // read masterKeyFlags, and use the same as before. +        // since this is the master key, this contains at least CERTIFY_OTHER +        int masterKeyFlags = readKeyFlags(masterSecretKey.getPublicKey()) | KeyFlags.CERTIFY_OTHER; + +        return internal(sKR, masterSecretKey, masterKeyFlags, saveParcel, passphrase, log);      } -    private UncachedKeyRing internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey, +    private EditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey, +                                     int masterKeyFlags,                                       SaveKeyringParcel saveParcel, String passphrase, -                                     OperationLog log, int indent) { +                                     OperationLog log) { + +        int indent = 1; -        updateProgress(R.string.progress_certifying_master_key, 20, 100); +        progress(R.string.progress_modify, 0);          PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();          // 1. Unlock private key +        progress(R.string.progress_modify_unlock, 10);          log.add(LogLevel.DEBUG, LogType.MSG_MF_UNLOCK, indent);          PGPPrivateKey masterPrivateKey;          { @@ -279,7 +341,7 @@ public class PgpKeyOperation {                  masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor);              } catch (PGPException e) {                  log.add(LogLevel.ERROR, LogType.MSG_MF_UNLOCK_ERROR, indent + 1); -                return null; +                return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);              }          } @@ -289,18 +351,28 @@ public class PgpKeyOperation {              PGPPublicKey modifiedPublicKey = masterPublicKey;              // 2a. Add certificates for new user ids -            for (String userId : saveParcel.addUserIds) { +            subProgressPush(15, 25); +            for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) { + +                progress(R.string.progress_modify_adduid, (i-1) * (100 / saveParcel.mAddSubKeys.size())); +                String userId = saveParcel.mAddUserIds.get(i);                  log.add(LogLevel.INFO, LogType.MSG_MF_UID_ADD, indent); +                if (userId.equals("")) { +                    log.add(LogLevel.ERROR, LogType.MSG_MF_UID_ERROR_EMPTY, indent+1); +                    return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); +                } +                  // this operation supersedes all previous binding and revocation certificates,                  // so remove those to retain assertions from canonicalization for later operations                  @SuppressWarnings("unchecked")                  Iterator<PGPSignature> it = modifiedPublicKey.getSignaturesForID(userId);                  if (it != null) {                      for (PGPSignature cert : new IterableIterator<PGPSignature>(it)) { -                        // if it's not a self cert, never mind                          if (cert.getKeyID() != masterPublicKey.getKeyID()) { -                            continue; +                            // foreign certificate?! error error error +                            log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent); +                            return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);                          }                          if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION                                  || cert.getSignatureType() == PGPSignature.NO_CERTIFICATION @@ -314,27 +386,39 @@ public class PgpKeyOperation {                  }                  // if it's supposed to be primary, we can do that here as well -                boolean isPrimary = saveParcel.changePrimaryUserId != null -                        && userId.equals(saveParcel.changePrimaryUserId); +                boolean isPrimary = saveParcel.mChangePrimaryUserId != null +                        && userId.equals(saveParcel.mChangePrimaryUserId);                  // generate and add new certificate                  PGPSignature cert = generateUserIdSignature(masterPrivateKey, -                        masterPublicKey, userId, isPrimary); -                modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert); +                        masterPublicKey, userId, isPrimary, masterKeyFlags); +                modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);              } +            subProgressPop();              // 2b. Add revocations for revoked user ids -            for (String userId : saveParcel.revokeUserIds) { -                log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent); -                // a duplicate revocatin will be removed during canonicalization, so no need to +            subProgressPush(25, 40); +            for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) { + +                progress(R.string.progress_modify_revokeuid, (i-1) * (100 / saveParcel.mAddSubKeys.size())); +                String userId = saveParcel.mRevokeUserIds.get(i); +                log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent, userId); + +                // a duplicate revocation will be removed during canonicalization, so no need to                  // take care of that here.                  PGPSignature cert = generateRevocationSignature(masterPrivateKey,                          masterPublicKey, userId); -                modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert); +                modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);              } +            subProgressPop();              // 3. If primary user id changed, generate new certificates for both old and new -            if (saveParcel.changePrimaryUserId != null) { +            if (saveParcel.mChangePrimaryUserId != null) { +                progress(R.string.progress_modify_primaryuid, 40); + +                // keep track if we actually changed one +                boolean ok = false;                  log.add(LogLevel.INFO, LogType.MSG_MF_UID_PRIMARY, indent); +                indent += 1;                  // we work on the modifiedPublicKey here, to respect new or newly revoked uids                  // noinspection unchecked @@ -343,10 +427,11 @@ public class PgpKeyOperation {                      PGPSignature currentCert = null;                      // noinspection unchecked                      for (PGPSignature cert : new IterableIterator<PGPSignature>( -                            masterPublicKey.getSignaturesForID(userId))) { -                        // if it's not a self cert, never mind +                            modifiedPublicKey.getSignaturesForID(userId))) {                          if (cert.getKeyID() != masterPublicKey.getKeyID()) { -                            continue; +                            // foreign certificate?! error error error +                            log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent); +                            return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);                          }                          // we know from canonicalization that if there is any revocation here, it                          // is valid and not superseded by a newer certification. @@ -367,30 +452,33 @@ public class PgpKeyOperation {                      if (currentCert == null) {                          // no certificate found?! error error error                          log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent); -                        return null; +                        return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);                      }                      // we definitely should not update certifications of revoked keys, so just leave it.                      if (isRevoked) {                          // revoked user ids cannot be primary! -                        if (userId.equals(saveParcel.changePrimaryUserId)) { +                        if (userId.equals(saveParcel.mChangePrimaryUserId)) {                              log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent); -                            return null; +                            return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);                          }                          continue;                      }                      // if this is~ the/a primary user id -                    if (currentCert.hasSubpackets() && currentCert.getHashedSubPackets().isPrimaryUserID()) { +                    if (currentCert.getHashedSubPackets() != null +                            && currentCert.getHashedSubPackets().isPrimaryUserID()) {                          // if it's the one we want, just leave it as is -                        if (userId.equals(saveParcel.changePrimaryUserId)) { +                        if (userId.equals(saveParcel.mChangePrimaryUserId)) { +                            ok = true;                              continue;                          }                          // otherwise, generate new non-primary certification +                        log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_REPLACE_OLD, indent);                          modifiedPublicKey = PGPPublicKey.removeCertification(                                  modifiedPublicKey, userId, currentCert);                          PGPSignature newCert = generateUserIdSignature( -                                masterPrivateKey, masterPublicKey, userId, false); +                                masterPrivateKey, masterPublicKey, userId, false, masterKeyFlags);                          modifiedPublicKey = PGPPublicKey.addCertification(                                  modifiedPublicKey, userId, newCert);                          continue; @@ -399,20 +487,28 @@ public class PgpKeyOperation {                      // if we are here, this is not currently a primary user id                      // if it should be -                    if (userId.equals(saveParcel.changePrimaryUserId)) { +                    if (userId.equals(saveParcel.mChangePrimaryUserId)) {                          // add shiny new primary user id certificate +                        log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_NEW, indent);                          modifiedPublicKey = PGPPublicKey.removeCertification(                                  modifiedPublicKey, userId, currentCert);                          PGPSignature newCert = generateUserIdSignature( -                                masterPrivateKey, masterPublicKey, userId, true); +                                masterPrivateKey, masterPublicKey, userId, true, masterKeyFlags);                          modifiedPublicKey = PGPPublicKey.addCertification(                                  modifiedPublicKey, userId, newCert); +                        ok = true;                      }                      // user id is not primary and is not supposed to be - nothing to do here.                  } +                indent -= 1; + +                if (!ok) { +                    log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_PRIMARY, indent); +                    return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); +                }              }              // Update the secret key ring @@ -423,40 +519,80 @@ public class PgpKeyOperation {              }              // 4a. For each subkey change, generate new subkey binding certificate -            for (SaveKeyringParcel.SubkeyChange change : saveParcel.changeSubKeys) { +            subProgressPush(50, 60); +            for (int i = 0; i < saveParcel.mChangeSubKeys.size(); i++) { + +                progress(R.string.progress_modify_subkeychange, (i-1) * (100 / saveParcel.mAddSubKeys.size())); +                SaveKeyringParcel.SubkeyChange change = saveParcel.mChangeSubKeys.get(i);                  log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_CHANGE,                          indent, PgpKeyHelper.convertKeyIdToHex(change.mKeyId)); + +                // TODO allow changes in master key? this implies generating new user id certs... +                if (change.mKeyId == masterPublicKey.getKeyID()) { +                    Log.e(Constants.TAG, "changing the master key not supported"); +                    return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); +                } +                  PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId);                  if (sKey == null) {                      log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING,                              indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId)); -                    return null; +                    return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);                  }                  PGPPublicKey pKey = sKey.getPublicKey(); -                if (change.mExpiry != null && new Date(change.mExpiry).before(new Date())) { +                // expiry must not be in the past +                if (change.mExpiry != null && new Date(change.mExpiry*1000).before(new Date())) {                      log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY,                              indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId)); -                    return null; +                    return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); +                } + +                // keep old flags, or replace with new ones +                int flags = change.mFlags == null ? readKeyFlags(pKey) : change.mFlags; +                long expiry; +                if (change.mExpiry == null) { +                    long valid = pKey.getValidSeconds(); +                    expiry = valid == 0 +                            ? 0 +                            : pKey.getCreationTime().getTime() / 1000 + pKey.getValidSeconds(); +                } else { +                    expiry = change.mExpiry;                  } -                // generate and add new signature. we can be sloppy here and just leave the old one, -                // it will be removed during canonicalization +                // drop all old signatures, they will be superseded by the new one +                //noinspection unchecked +                for (PGPSignature sig : new IterableIterator<PGPSignature>(pKey.getSignatures())) { +                    // special case: if there is a revocation, don't use expiry from before +                    if (change.mExpiry == null +                            && sig.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) { +                        expiry = 0; +                    } +                    pKey = PGPPublicKey.removeCertification(pKey, sig); +                } + +                // generate and add new signature                  PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, -                        sKey, pKey, change.mFlags, change.mExpiry, passphrase); +                        sKey, pKey, flags, expiry, passphrase);                  pKey = PGPPublicKey.addCertification(pKey, sig);                  sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));              } +            subProgressPop();              // 4b. For each subkey revocation, generate new subkey revocation certificate -            for (long revocation : saveParcel.revokeSubKeys) { +            subProgressPush(60, 70); +            for (int i = 0; i < saveParcel.mRevokeSubKeys.size(); i++) { + +                progress(R.string.progress_modify_subkeyrevoke, (i-1) * (100 / saveParcel.mAddSubKeys.size())); +                long revocation = saveParcel.mRevokeSubKeys.get(i);                  log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_REVOKE,                          indent, PgpKeyHelper.convertKeyIdToHex(revocation)); +                  PGPSecretKey sKey = sKR.getSecretKey(revocation);                  if (sKey == null) {                      log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING,                              indent+1, PgpKeyHelper.convertKeyIdToHex(revocation)); -                    return null; +                    return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);                  }                  PGPPublicKey pKey = sKey.getPublicKey(); @@ -466,54 +602,64 @@ public class PgpKeyOperation {                  pKey = PGPPublicKey.addCertification(pKey, sig);                  sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));              } +            subProgressPop();              // 5. Generate and add new subkeys -            for (SaveKeyringParcel.SubkeyAdd add : saveParcel.addSubKeys) { -                try { - -                    if (add.mExpiry != null && new Date(add.mExpiry).before(new Date())) { -                        log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY, indent +1); -                        return null; -                    } - -                    log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent); - -                    // generate a new secret key (privkey only for now) -                    PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize); +            subProgressPush(70, 90); +            for (int i = 0; i < saveParcel.mAddSubKeys.size(); i++) { -                    // add subkey binding signature (making this a sub rather than master key) -                    PGPPublicKey pKey = keyPair.getPublicKey(); -                    PGPSignature cert = generateSubkeyBindingSignature( -                            masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey, -                            add.mFlags, add.mExpiry); -                    pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert); +                progress(R.string.progress_modify_subkeyadd, (i-1) * (100 / saveParcel.mAddSubKeys.size())); +                SaveKeyringParcel.SubkeyAdd add = saveParcel.mAddSubKeys.get(0); +                log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent); -                    PGPSecretKey sKey; { -                        // define hashing and signing algos -                        PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder() -                                .build().get(HashAlgorithmTags.SHA1); +                if (add.mExpiry != null && new Date(add.mExpiry*1000).before(new Date())) { +                    log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY, indent +1); +                    return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); +                } -                        // Build key encrypter and decrypter based on passphrase -                        PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( -                                PGPEncryptedData.CAST5, sha1Calc) -                                .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray()); +                // generate a new secret key (privkey only for now) +                subProgressPush( +                    (i-1) * (100 / saveParcel.mAddSubKeys.size()), +                    i * (100 / saveParcel.mAddSubKeys.size()) +                ); +                PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent); +                subProgressPop(); +                if(keyPair == null) { +                    return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); +                } -                        sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey, -                                sha1Calc, false, keyEncryptor); -                    } +                // add subkey binding signature (making this a sub rather than master key) +                PGPPublicKey pKey = keyPair.getPublicKey(); +                PGPSignature cert = generateSubkeyBindingSignature( +                        masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey, +                        add.mFlags, add.mExpiry == null ? 0 : add.mExpiry); +                pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert); + +                PGPSecretKey sKey; { +                    // define hashing and signing algos +                    PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder() +                            .build().get(HashAlgorithmTags.SHA1); + +                    // Build key encrypter and decrypter based on passphrase +                    PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( +                            PGPEncryptedData.CAST5, sha1Calc) +                            .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray()); + +                    sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey, +                            sha1Calc, false, keyEncryptor); +                } -                    log.add(LogLevel.DEBUG, LogType.MSG_MF_SUBKEY_NEW_ID, -                            indent+1, PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID())); +                log.add(LogLevel.DEBUG, LogType.MSG_MF_SUBKEY_NEW_ID, +                        indent+1, PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID())); -                    sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey); +                sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey); -                } catch (PgpGeneralMsgIdException e) { -                    return null; -                }              } +            subProgressPop();              // 6. If requested, change passphrase -            if (saveParcel.newPassphrase != null) { +            if (saveParcel.mNewPassphrase != null) { +                progress(R.string.progress_modify_passphrase, 90);                  log.add(LogLevel.INFO, LogType.MSG_MF_PASSPHRASE, indent);                  PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build()                          .get(HashAlgorithmTags.SHA1); @@ -523,7 +669,7 @@ public class PgpKeyOperation {                  PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(                          PGPEncryptedData.CAST5, sha1Calc)                          .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( -                                saveParcel.newPassphrase.toCharArray()); +                                saveParcel.mNewPassphrase.toCharArray());                  sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew);              } @@ -531,22 +677,24 @@ public class PgpKeyOperation {              // This one must only be thrown by          } catch (IOException e) {              log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_ENCODE, indent+1); -            return null; +            return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);          } catch (PGPException e) { +            Log.e(Constants.TAG, "encountered pgp error while modifying key", e);              log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PGP, indent+1); -            return null; +            return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);          } catch (SignatureException e) {              log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_SIG, indent+1); -            return null; +            return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);          } +        progress(R.string.progress_done, 100);          log.add(LogLevel.OK, LogType.MSG_MF_SUCCESS, indent); -        return new UncachedKeyRing(sKR); +        return new EditKeyResult(OperationResultParcel.RESULT_OK, log, new UncachedKeyRing(sKR));      }      private static PGPSignature generateUserIdSignature( -            PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary) +            PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary, int flags)              throws IOException, PGPException, SignatureException {          PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(                  pKey.getAlgorithm(), PGPUtil.SHA1) @@ -558,6 +706,7 @@ public class PgpKeyOperation {          subHashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);          subHashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);          subHashedPacketsGen.setPrimaryUserID(false, primary); +        subHashedPacketsGen.setKeyFlags(false, flags);          sGen.setHashedSubpackets(subHashedPacketsGen.generate());          sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);          return sGen.generateCertification(userId, pKey); @@ -599,7 +748,7 @@ 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, String passphrase)              throws IOException, PGPException, SignatureException {          PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()                  .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( @@ -611,7 +760,7 @@ public class PgpKeyOperation {      private static PGPSignature generateSubkeyBindingSignature(              PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, -            PGPPrivateKey subPrivateKey, PGPPublicKey pKey, int flags, Long expiry) +            PGPPrivateKey subPrivateKey, PGPPublicKey pKey, int flags, long expiry)              throws IOException, PGPException, SignatureException {          // date for signing @@ -640,17 +789,9 @@ public class PgpKeyOperation {              hashedPacketsGen.setKeyFlags(false, flags);          } -        if (expiry != null) { -            Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); -            creationDate.setTime(pKey.getCreationTime()); - -            // (Just making sure there's no programming error here, this MUST have been checked above!) -            if (new Date(expiry).before(todayDate)) { -                throw new RuntimeException("Bad subkey creation date, this is a bug!"); -            } -            hashedPacketsGen.setKeyExpirationTime(false, expiry - creationDate.getTimeInMillis()); -        } else { -            hashedPacketsGen.setKeyExpirationTime(false, 0); +        if (expiry > 0) { +            long creationTime = pKey.getCreationTime().getTime() / 1000; +            hashedPacketsGen.setKeyExpirationTime(false, expiry - creationTime);          }          PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( @@ -665,4 +806,22 @@ public class PgpKeyOperation {      } +    /** Returns all flags valid for this key. +     * +     * This method does not do any validity checks on the signature, so it should not be used on +     * a non-canonicalized key! +     * +     */ +    private static int readKeyFlags(PGPPublicKey key) { +        int flags = 0; +        //noinspection unchecked +        for(PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) { +            if (sig.getHashedSubPackets() == null) { +                continue; +            } +            flags |= sig.getHashedSubPackets().getKeyFlags(); +        } +        return flags; +    } +  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java index 4cb92c368..f0403e625 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java @@ -266,11 +266,11 @@ public class PgpSignEncrypt {          }          /* Get keys for signature generation for later usage */ -        WrappedSecretKey signingKey = null; +        CanonicalizedSecretKey signingKey = null;          if (enableSignature) { -            WrappedSecretKeyRing signingKeyRing; +            CanonicalizedSecretKeyRing signingKeyRing;              try { -                signingKeyRing = mProviderHelper.getWrappedSecretKeyRing(mSignatureMasterKeyId); +                signingKeyRing = mProviderHelper.getCanonicalizedSecretKeyRing(mSignatureMasterKeyId);              } catch (ProviderHelper.NotFoundException e) {                  throw new NoSigningKeyException();              } @@ -316,9 +316,9 @@ public class PgpSignEncrypt {                  // Asymmetric encryption                  for (long id : mEncryptionMasterKeyIds) {                      try { -                        WrappedPublicKeyRing keyRing = mProviderHelper.getWrappedPublicKeyRing( +                        CanonicalizedPublicKeyRing keyRing = mProviderHelper.getCanonicalizedPublicKeyRing(                                  KeyRings.buildUnifiedKeyRingUri(id)); -                        WrappedPublicKey key = keyRing.getEncryptionSubKey(); +                        CanonicalizedPublicKey key = keyRing.getEncryptionSubKey();                          cPk.addMethod(key.getPubKeyEncryptionGenerator());                      } catch (PgpGeneralException e) {                          Log.e(Constants.TAG, "key not found!", e); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 441e2762a..502e3a70c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -1,7 +1,6 @@  package org.sufficientlysecure.keychain.pgp;  import org.spongycastle.bcpg.ArmoredOutputStream; -import org.spongycastle.bcpg.S2K;  import org.spongycastle.bcpg.SignatureSubpacketTags;  import org.spongycastle.bcpg.sig.KeyFlags;  import org.spongycastle.openpgp.PGPKeyFlags; @@ -14,6 +13,7 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;  import org.spongycastle.openpgp.PGPSignature;  import org.spongycastle.openpgp.PGPSignatureList;  import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; @@ -22,20 +22,18 @@ import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;  import org.sufficientlysecure.keychain.util.IterableIterator;  import org.sufficientlysecure.keychain.util.Log; -import java.io.BufferedInputStream;  import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream;  import java.io.IOException;  import java.io.InputStream;  import java.io.OutputStream;  import java.util.ArrayList;  import java.util.Comparator;  import java.util.Date; -import java.util.HashSet;  import java.util.Iterator;  import java.util.List;  import java.util.Set;  import java.util.TreeSet; -import java.util.Vector;  /** Wrapper around PGPKeyRing class, to be constructed from bytes.   * @@ -49,7 +47,7 @@ import java.util.Vector;   * treated equally for most purposes in UI code. It is up to the programmer to   * take care of the differences.   * - * @see org.sufficientlysecure.keychain.pgp.WrappedKeyRing + * @see CanonicalizedKeyRing   * @see org.sufficientlysecure.keychain.pgp.UncachedPublicKey   * @see org.sufficientlysecure.keychain.pgp.UncachedSecretKey   * @@ -59,18 +57,10 @@ public class UncachedKeyRing {      final PGPKeyRing mRing;      final boolean mIsSecret; -    final boolean mIsCanonicalized;      UncachedKeyRing(PGPKeyRing ring) {          mRing = ring;          mIsSecret = ring instanceof PGPSecretKeyRing; -        mIsCanonicalized = false; -    } - -    private UncachedKeyRing(PGPKeyRing ring, boolean canonicalized) { -        mRing = ring; -        mIsSecret = ring instanceof PGPSecretKeyRing; -        mIsCanonicalized = canonicalized;      }      public long getMasterKeyId() { @@ -81,11 +71,15 @@ public class UncachedKeyRing {          return new UncachedPublicKey(mRing.getPublicKey());      } +    public UncachedPublicKey getPublicKey(long keyId) { +        return new UncachedPublicKey(mRing.getPublicKey(keyId)); +    } +      public Iterator<UncachedPublicKey> getPublicKeys() {          final Iterator<PGPPublicKey> it = mRing.getPublicKeys();          return new Iterator<UncachedPublicKey>() {              public void remove() { -                it.remove(); +                throw new UnsupportedOperationException();              }              public UncachedPublicKey next() {                  return new UncachedPublicKey(it.next()); @@ -101,10 +95,6 @@ public class UncachedKeyRing {          return mIsSecret;      } -    public boolean isCanonicalized() { -        return mIsCanonicalized; -    } -      public byte[] getEncoded() throws IOException {          return mRing.getEncoded();      } @@ -115,43 +105,86 @@ public class UncachedKeyRing {      public static UncachedKeyRing decodeFromData(byte[] data)              throws PgpGeneralException, IOException { -        BufferedInputStream bufferedInput = -                new BufferedInputStream(new ByteArrayInputStream(data)); -        if (bufferedInput.available() > 0) { -            InputStream in = PGPUtil.getDecoderStream(bufferedInput); -            PGPObjectFactory objectFactory = new PGPObjectFactory(in); - -            // get first object in block -            Object obj; -            if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPKeyRing) { -                return new UncachedKeyRing((PGPKeyRing) obj); -            } else { -                throw new PgpGeneralException("Object not recognized as PGPKeyRing!"); -            } -        } else { + +        Iterator<UncachedKeyRing> parsed = fromStream(new ByteArrayInputStream(data)); + +        if ( ! parsed.hasNext()) {              throw new PgpGeneralException("Object not recognized as PGPKeyRing!");          } + +        UncachedKeyRing ring = parsed.next(); + +        if (parsed.hasNext()) { +            throw new PgpGeneralException("Expected single keyring in stream, found at least two"); +        } + +        return ring; +      } -    public static List<UncachedKeyRing> fromStream(InputStream stream) -            throws PgpGeneralException, IOException { +    public static Iterator<UncachedKeyRing> fromStream(final InputStream stream) throws IOException { + +        return new Iterator<UncachedKeyRing>() { -        PGPObjectFactory objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(stream)); +            UncachedKeyRing mNext = null; +            PGPObjectFactory mObjectFactory = null; -        List<UncachedKeyRing> result = new Vector<UncachedKeyRing>(); +            private void cacheNext() { +                if (mNext != null) { +                    return; +                } -        // go through all objects in this block -        Object obj; -        while ((obj = objectFactory.nextObject()) != null) { -            Log.d(Constants.TAG, "Found class: " + obj.getClass()); +                try { +                    while(stream.available() > 0) { +                        // if there are no objects left from the last factory, create a new one +                        if (mObjectFactory == null) { +                            mObjectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(stream)); +                        } + +                        // go through all objects in this block +                        Object obj; +                        while ((obj = mObjectFactory.nextObject()) != null) { +                            Log.d(Constants.TAG, "Found class: " + obj.getClass()); +                            if (!(obj instanceof PGPKeyRing)) { +                                Log.i(Constants.TAG, +                                        "Skipping object of bad type " + obj.getClass().getName() + " in stream"); +                                // skip object +                                continue; +                            } +                            mNext = new UncachedKeyRing((PGPKeyRing) obj); +                            return; +                        } +                        // if we are past the while loop, that means the objectFactory had no next +                        mObjectFactory = null; +                    } +                } catch (IOException e) { +                    Log.e(Constants.TAG, "IOException while processing stream", e); +                } -            if (obj instanceof PGPKeyRing) { -                result.add(new UncachedKeyRing((PGPKeyRing) obj)); -            } else { -                Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");              } -        } -        return result; + +            @Override +            public boolean hasNext() { +                cacheNext(); +                return mNext != null; +            } + +            @Override +            public UncachedKeyRing next() { +                try { +                    cacheNext(); +                    return mNext; +                } finally { +                    mNext = null; +                } +            } + +            @Override +            public void remove() { +                throw new UnsupportedOperationException(); +            } +        }; +      }      public void encodeArmored(OutputStream out, String version) throws IOException { @@ -161,25 +194,6 @@ public class UncachedKeyRing {          aos.close();      } -    public HashSet<Long> getAvailableSubkeys() { -        if(!isSecret()) { -            throw new RuntimeException("Tried to find available subkeys from non-secret keys. " + -                    "This is a programming error and should never happen!"); -        } - -        HashSet<Long> result = new HashSet<Long>(); -        // then, mark exactly the keys we have available -        for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>( -                ((PGPSecretKeyRing) mRing).getSecretKeys())) { -            S2K s2k = sub.getS2K(); -            // Set to 1, except if the encryption type is GNU_DUMMY_S2K -            if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) { -                result.add(sub.getKeyID()); -            } -        } -        return result; -    } -      /** "Canonicalizes" a public key, removing inconsistencies in the process. This variant can be       * applied to public keyrings only.       * @@ -189,7 +203,7 @@ public class UncachedKeyRing {       *  - Remove all certificates flagged as "local"       *  - Remove all certificates which are superseded by a newer one on the same target,       *      including revocations with later re-certifications. -     *  - Remove all certificates of unknown type: +     *  - Remove all certificates in other positions if not of known type:       *   - key revocation signatures on the master key       *   - subkey binding signatures for subkeys       *   - certifications and certification revocations for user ids @@ -204,7 +218,7 @@ public class UncachedKeyRing {       *       */      @SuppressWarnings("ConstantConditions") -    public UncachedKeyRing canonicalize(OperationLog log, int indent) { +    public CanonicalizedKeyRing canonicalize(OperationLog log, int indent) {          log.add(LogLevel.START, isSecret() ? LogType.MSG_KC_SECRET : LogType.MSG_KC_PUBLIC,                  indent, PgpKeyHelper.convertKeyIdToHex(getMasterKeyId())); @@ -225,7 +239,7 @@ public class UncachedKeyRing {              PGPPublicKey modified = masterKey;              PGPSignature revocation = null; -            for (PGPSignature zert : new IterableIterator<PGPSignature>(masterKey.getSignatures())) { +            for (PGPSignature zert : new IterableIterator<PGPSignature>(masterKey.getKeySignatures())) {                  int type = zert.getSignatureType();                  // Disregard certifications on user ids, we will deal with those later @@ -234,6 +248,10 @@ public class UncachedKeyRing {                          || type == PGPSignature.CASUAL_CERTIFICATION                          || type == PGPSignature.POSITIVE_CERTIFICATION                          || type == PGPSignature.CERTIFICATION_REVOCATION) { +                    // These should not be here... +                    log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TYPE_UID, indent); +                    modified = PGPPublicKey.removeCertification(modified, zert); +                    badCerts += 1;                      continue;                  }                  WrappedSignature cert = new WrappedSignature(zert); @@ -317,7 +335,7 @@ public class UncachedKeyRing {                      if (cert.getCreationTime().after(now)) {                          // Creation date in the future? No way! -                        log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, indent); +                        log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_TIME, indent);                          modified = PGPPublicKey.removeCertification(modified, zert);                          badCerts += 1;                          continue; @@ -325,7 +343,7 @@ public class UncachedKeyRing {                      if (cert.isLocal()) {                          // Creation date in the future? No way! -                        log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, indent); +                        log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_LOCAL, indent);                          modified = PGPPublicKey.removeCertification(modified, zert);                          badCerts += 1;                          continue; @@ -425,7 +443,7 @@ public class UncachedKeyRing {                  // If no valid certificate (if only a revocation) remains, drop it                  if (selfCert == null && revocation == null) {                      modified = PGPPublicKey.removeCertification(modified, userId); -                    log.add(LogLevel.ERROR, LogType.MSG_KC_UID_REVOKE_DUP, +                    log.add(LogLevel.ERROR, LogType.MSG_KC_UID_REMOVE,                              indent, userId);                  }              } @@ -505,14 +523,17 @@ public class UncachedKeyRing {                          continue;                      } -                    if (zert.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) { +                    // if this certificate says it allows signing for the key +                    if (zert.getHashedSubPackets() != null && +                            zert.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) { +                          int flags = ((KeyFlags) zert.getHashedSubPackets()                                  .getSubpacket(SignatureSubpacketTags.KEY_FLAGS)).getFlags(); -                        // If this subkey is allowed to sign data,                          if ((flags & PGPKeyFlags.CAN_SIGN) == PGPKeyFlags.CAN_SIGN) { +                            boolean ok = false; +                            // it MUST have an embedded primary key binding signature                              try {                                  PGPSignatureList list = zert.getUnhashedSubPackets().getEmbeddedSignatures(); -                                boolean ok = false;                                  for (int i = 0; i < list.size(); i++) {                                      WrappedSignature subsig = new WrappedSignature(list.get(i));                                      if (subsig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) { @@ -526,17 +547,19 @@ public class UncachedKeyRing {                                          }                                      }                                  } -                                if (!ok) { -                                    log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_NONE, indent); -                                    badCerts += 1; -                                    continue; -                                }                              } catch (Exception e) {                                  log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD_ERR, indent);                                  badCerts += 1;                                  continue;                              } +                            // if it doesn't, get rid of this! +                            if (!ok) { +                                log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_NONE, indent); +                                badCerts += 1; +                                continue; +                            }                          } +                      }                      // if we already have a cert, and this one is not newer: skip it @@ -549,6 +572,8 @@ public class UncachedKeyRing {                      selfCert = zert;                      // if this is newer than a possibly existing revocation, drop that one                      if (revocation != null && selfCert.getCreationTime().after(revocation.getCreationTime())) { +                        log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_REVOKE_DUP, indent); +                        redundantCerts += 1;                          revocation = null;                      } @@ -558,7 +583,7 @@ public class UncachedKeyRing {                      // make sure the certificate checks out                      try {                          cert.init(masterKey); -                        if (!cert.verifySignature(key)) { +                        if (!cert.verifySignature(masterKey, key)) {                              log.add(LogLevel.WARN, LogType.MSG_KC_SUB_REVOKE_BAD, indent);                              badCerts += 1;                              continue; @@ -582,7 +607,7 @@ public class UncachedKeyRing {              // it is not properly bound? error!              if (selfCert == null) { -                ring = replacePublicKey(ring, modified); +                ring = removeSubKey(ring, key);                  log.add(LogLevel.ERROR, LogType.MSG_KC_SUB_NO_CERT,                          indent, PgpKeyHelper.convertKeyIdToHex(key.getKeyID())); @@ -615,7 +640,8 @@ public class UncachedKeyRing {              log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, indent);          } -        return new UncachedKeyRing(ring, true); +        return isSecret() ? new CanonicalizedSecretKeyRing((PGPSecretKeyRing) ring, 1) +                          : new CanonicalizedPublicKeyRing((PGPPublicKeyRing) ring, 0);      }      /** This operation merges information from a different keyring, returning a combined @@ -650,7 +676,7 @@ public class UncachedKeyRing {                      return left.length - right.length;                  }                  // compare byte-by-byte -                for (int i = 0; i < left.length && i < right.length; i++) { +                for (int i = 0; i < left.length; i++) {                      if (left[i] != right[i]) {                          return (left[i] & 0xff) - (right[i] & 0xff);                      } @@ -678,7 +704,14 @@ public class UncachedKeyRing {                  final PGPPublicKey resultKey = result.getPublicKey(key.getKeyID());                  if (resultKey == null) {                      log.add(LogLevel.DEBUG, LogType.MSG_MG_NEW_SUBKEY, indent); -                    result = replacePublicKey(result, key); +                    // special case: if both rings are secret, copy over the secret key +                    if (isSecret() && other.isSecret()) { +                        PGPSecretKey sKey = ((PGPSecretKeyRing) candidate).getSecretKey(key.getKeyID()); +                        result = PGPSecretKeyRing.insertSecretKey((PGPSecretKeyRing) result, sKey); +                    } else { +                        // otherwise, just insert the public key +                        result = replacePublicKey(result, key); +                    }                      continue;                  } @@ -686,17 +719,7 @@ public class UncachedKeyRing {                  PGPPublicKey modified = resultKey;                  // Iterate certifications -                for (PGPSignature cert : new IterableIterator<PGPSignature>(key.getSignatures())) { -                    int type = cert.getSignatureType(); -                    // Disregard certifications on user ids, we will deal with those later -                    if (type == PGPSignature.NO_CERTIFICATION -                            || type == PGPSignature.DEFAULT_CERTIFICATION -                            || type == PGPSignature.CASUAL_CERTIFICATION -                            || type == PGPSignature.POSITIVE_CERTIFICATION -                            || type == PGPSignature.CERTIFICATION_REVOCATION) { -                        continue; -                    } - +                for (PGPSignature cert : new IterableIterator<PGPSignature>(key.getKeySignatures())) {                      // Don't merge foreign stuff into secret keys                      if (cert.getKeyID() != masterKeyId && isSecret()) {                          continue; @@ -744,8 +767,12 @@ public class UncachedKeyRing {              } -            log.add(LogLevel.DEBUG, LogType.MSG_MG_FOUND_NEW, -                    indent, Integer.toString(newCerts)); +            if (newCerts > 0) { +                log.add(LogLevel.DEBUG, LogType.MSG_MG_FOUND_NEW, indent, +                        Integer.toString(newCerts)); +            } else { +                log.add(LogLevel.DEBUG, LogType.MSG_MG_UNCHANGED, indent); +            }              return new UncachedKeyRing(result); @@ -756,19 +783,20 @@ public class UncachedKeyRing {      } -    public UncachedKeyRing extractPublicKeyRing() { +    public UncachedKeyRing extractPublicKeyRing() throws IOException {          if(!isSecret()) {              throw new RuntimeException("Tried to extract public keyring from non-secret keyring. " +                      "This is a programming error and should never happen!");          } -        ArrayList<PGPPublicKey> keys = new ArrayList();          Iterator<PGPPublicKey> it = mRing.getPublicKeys(); +        ByteArrayOutputStream stream = new ByteArrayOutputStream(2048);          while (it.hasNext()) { -            keys.add(it.next()); +            stream.write(it.next().getEncoded());          } -        return new UncachedKeyRing(new PGPPublicKeyRing(keys)); +        return new UncachedKeyRing( +                new PGPPublicKeyRing(stream.toByteArray(), new JcaKeyFingerprintCalculator()));      }      /** This method replaces a public key in a keyring. @@ -792,4 +820,20 @@ public class UncachedKeyRing {          return PGPSecretKeyRing.insertSecretKey(secRing, sKey);      } +    /** This method removes a subkey in a keyring. +     * +     * This method essentially wraps PGP*KeyRing.remove*Key, where the keyring may be of either +     * the secret or public subclass. +     * +     * @return the resulting PGPKeyRing of the same type as the input +     */ +    private static PGPKeyRing removeSubKey(PGPKeyRing ring, PGPPublicKey key) { +        if (ring instanceof PGPPublicKeyRing) { +            return PGPPublicKeyRing.removePublicKey((PGPPublicKeyRing) ring, key); +        } else { +            PGPSecretKey sKey = ((PGPSecretKeyRing) ring).getSecretKey(key.getKeyID()); +            return PGPSecretKeyRing.removeSecretKey((PGPSecretKeyRing) ring, sKey); +        } +    } +  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java index 33db7771b..358b1c552 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java @@ -1,16 +1,14 @@  package org.sufficientlysecure.keychain.pgp; -import org.spongycastle.bcpg.SignatureSubpacketTags;  import org.spongycastle.bcpg.sig.KeyFlags; -import org.spongycastle.openpgp.PGPException;  import org.spongycastle.openpgp.PGPPublicKey;  import org.spongycastle.openpgp.PGPSignature;  import org.spongycastle.openpgp.PGPSignatureSubpacketVector;  import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.util.IterableIterator; +import org.sufficientlysecure.keychain.util.Log; -import java.security.SignatureException;  import java.util.ArrayList;  import java.util.Calendar;  import java.util.Date; @@ -44,14 +42,19 @@ public class UncachedPublicKey {      }      public Date getExpiryTime() { -        Date creationDate = getCreationTime(); -        if (mPublicKey.getValidDays() == 0) { +        long seconds = mPublicKey.getValidSeconds(); +        if (seconds > Integer.MAX_VALUE) { +            Log.e(Constants.TAG, "error, expiry time too large"); +            return null; +        } +        if (seconds == 0) {              // no expiry              return null;          } +        Date creationDate = getCreationTime();          Calendar calendar = GregorianCalendar.getInstance();          calendar.setTime(creationDate); -        calendar.add(Calendar.DATE, mPublicKey.getValidDays()); +        calendar.add(Calendar.SECOND, (int) seconds);          return calendar.getTime();      } @@ -77,26 +80,76 @@ public class UncachedPublicKey {          return mPublicKey.getBitStrength();      } +    /** Returns the primary user id, as indicated by the public key's self certificates. +     * +     * This is an expensive operation, since potentially a lot of certificates (and revocations) +     * have to be checked, and even then the result is NOT guaranteed to be constant through a +     * canonicalization operation. +     * +     * Returns null if there is no primary user id (as indicated by certificates) +     * +     */      public String getPrimaryUserId() { +        String found = null; +        PGPSignature foundSig = null;          for (String userId : new IterableIterator<String>(mPublicKey.getUserIDs())) { +            PGPSignature revocation = null; +              for (PGPSignature sig : new IterableIterator<PGPSignature>(mPublicKey.getSignaturesForID(userId))) { -                if (sig.getHashedSubPackets() != null -                        && sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.PRIMARY_USER_ID)) { -                    try { +                try { + +                    // if this is a revocation, this is not the user id +                    if (sig.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) { +                        // make sure it's actually valid +                        sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider( +                                Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey); +                        if (!sig.verifyCertification(userId, mPublicKey)) { +                            continue; +                        } +                        if (found != null && found.equals(userId)) { +                            found = null; +                        } +                        revocation = sig; +                        // this revocation may still be overridden by a newer cert +                        continue; +                    } + +                    if (sig.getHashedSubPackets() != null && sig.getHashedSubPackets().isPrimaryUserID()) { +                        if (foundSig != null && sig.getCreationTime().before(foundSig.getCreationTime())) { +                            continue; +                        } +                        // ignore if there is a newer revocation for this user id +                        if (revocation != null && sig.getCreationTime().before(revocation.getCreationTime())) { +                            continue; +                        }                          // make sure it's actually valid                          sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(                                  Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey);                          if (sig.verifyCertification(userId, mPublicKey)) { -                            return userId; +                            found = userId; +                            foundSig = sig; +                            // this one can't be relevant anymore at this point +                            revocation = null;                          } -                    } catch (Exception e) { -                        // nothing bad happens, the key is just not considered the primary key id                      } -                } +                } catch (Exception e) { +                    // nothing bad happens, the key is just not considered the primary key id +                }              }          } -        return null; +        return found; +    } + +    /** +     * Returns primary user id if existing. If not, return first encountered user id. +     */ +    public String getPrimaryUserIdWithFallback()  { +        String userId = getPrimaryUserId(); +        if (userId == null) { +            userId = (String) mPublicKey.getUserIDs().next(); +        } +        return userId;      }      public ArrayList<String> getUnorderedUserIds() { @@ -186,6 +239,21 @@ public class UncachedPublicKey {          return mPublicKey;      } +    public Iterator<WrappedSignature> getSignatures() { +        final Iterator<PGPSignature> it = mPublicKey.getSignatures(); +        return new Iterator<WrappedSignature>() { +            public void remove() { +                it.remove(); +            } +            public WrappedSignature next() { +                return new WrappedSignature(it.next()); +            } +            public boolean hasNext() { +                return it.hasNext(); +            } +        }; +    } +      public Iterator<WrappedSignature> getSignaturesForId(String userId) {          final Iterator<PGPSignature> it = mPublicKey.getSignaturesForID(userId);          return new Iterator<WrappedSignature>() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java index 196ac1dee..07fb4fb9e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java @@ -15,6 +15,7 @@ import org.sufficientlysecure.keychain.util.Log;  import java.io.IOException;  import java.security.SignatureException; +import java.util.ArrayList;  import java.util.Date;  /** OpenKeychain wrapper around PGPSignature objects. @@ -55,12 +56,41 @@ public class WrappedSignature {          return mSig.getCreationTime();      } +    public ArrayList<WrappedSignature> getEmbeddedSignatures() { +        ArrayList<WrappedSignature> sigs = new ArrayList<WrappedSignature>(); +        if (!mSig.hasSubpackets()) { +            return sigs; +        } +        try { +            PGPSignatureList list; +            if (mSig.getHashedSubPackets() != null) { +                list = mSig.getHashedSubPackets().getEmbeddedSignatures(); +                for (int i = 0; i < list.size(); i++) { +                    sigs.add(new WrappedSignature(list.get(i))); +                } +            } +            if (mSig.getUnhashedSubPackets() != null) { +                list = mSig.getUnhashedSubPackets().getEmbeddedSignatures(); +                for (int i = 0; i < list.size(); i++) { +                    sigs.add(new WrappedSignature(list.get(i))); +                } +            } +        } catch (PGPException e) { +            // no matter +            Log.e(Constants.TAG, "exception reading embedded signatures", e); +        } catch (IOException e) { +            // no matter +            Log.e(Constants.TAG, "exception reading embedded signatures", e); +        } +        return sigs; +    } +      public byte[] getEncoded() throws IOException {          return mSig.getEncoded();      }      public boolean isRevocation() { -        return mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON); +        return mSig.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION;      }      public boolean isPrimaryUserId() { @@ -71,6 +101,9 @@ public class WrappedSignature {          if(!isRevocation()) {              throw new PgpGeneralException("Not a revocation signature.");          } +        if (mSig.getHashedSubPackets() == null) { +            return null; +        }          SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(                  SignatureSubpacketTags.REVOCATION_REASON);          // For some reason, this is missing in SignatureSubpacketInputStream:146 @@ -80,7 +113,7 @@ public class WrappedSignature {          return ((RevocationReason) p).getRevocationDescription();      } -    public void init(WrappedPublicKey key) throws PgpGeneralException { +    public void init(CanonicalizedPublicKey key) throws PgpGeneralException {          init(key.getPublicKey());      } @@ -158,7 +191,7 @@ public class WrappedSignature {      public boolean verifySignature(UncachedPublicKey key, String uid) throws PgpGeneralException {          return verifySignature(key.getPublicKey(), uid);      } -    public boolean verifySignature(WrappedPublicKey key, String uid) throws PgpGeneralException { +    public boolean verifySignature(CanonicalizedPublicKey key, String uid) throws PgpGeneralException {          return verifySignature(key.getPublicKey(), uid);      } @@ -179,7 +212,7 @@ public class WrappedSignature {      }      public boolean isLocal() { -        if (!mSig.hasSubpackets() +        if (mSig.getHashedSubPackets() == null                  || !mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.EXPORTABLE)) {              return false;          } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java index bc7221d13..aa0207a6a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java @@ -83,6 +83,10 @@ public class CachedPublicKeyRing extends KeyRing {          }      } +    public String getPrimaryUserIdWithFallback() throws PgpGeneralException { +        return getPrimaryUserId(); +    } +      @Override      public boolean isRevoked() throws PgpGeneralException {          try { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 81e218ccf..aa85577e0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -28,10 +28,12 @@ import android.os.RemoteException;  import android.support.v4.util.LongSparseArray;  import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;  import org.sufficientlysecure.keychain.pgp.KeyRing;  import org.sufficientlysecure.keychain.pgp.NullProgressable;  import org.sufficientlysecure.keychain.pgp.Progressable; -import org.sufficientlysecure.keychain.pgp.WrappedPublicKey; +import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;  import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;  import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel;  import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; @@ -39,8 +41,6 @@ import org.sufficientlysecure.keychain.pgp.PgpHelper;  import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;  import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;  import org.sufficientlysecure.keychain.pgp.UncachedPublicKey; -import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing; -import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;  import org.sufficientlysecure.keychain.pgp.WrappedSignature;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; @@ -180,7 +180,7 @@ public class ProviderHelper {          return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types);      } -    private LongSparseArray<WrappedPublicKey> getTrustedMasterKeys() { +    private LongSparseArray<CanonicalizedPublicKey> getTrustedMasterKeys() {          Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[] {                  KeyRings.MASTER_KEY_ID,                  // we pick from cache only information that is not easily available from keyrings @@ -190,16 +190,15 @@ public class ProviderHelper {              }, KeyRings.HAS_ANY_SECRET + " = 1", null, null);          try { -            LongSparseArray<WrappedPublicKey> result = new LongSparseArray<WrappedPublicKey>(); +            LongSparseArray<CanonicalizedPublicKey> result = new LongSparseArray<CanonicalizedPublicKey>();              if (cursor != null && cursor.moveToFirst()) do {                  long masterKeyId = cursor.getLong(0); -                boolean hasAnySecret = cursor.getInt(1) > 0;                  int verified = cursor.getInt(2);                  byte[] blob = cursor.getBlob(3);                  if (blob != null) {                      result.put(masterKeyId, -                            new WrappedPublicKeyRing(blob, hasAnySecret, verified).getSubkey()); +                            new CanonicalizedPublicKeyRing(blob, verified).getPublicKey());                  }              } while (cursor.moveToNext()); @@ -217,23 +216,23 @@ public class ProviderHelper {          return new CachedPublicKeyRing(this, queryUri);      } -    public WrappedPublicKeyRing getWrappedPublicKeyRing(long id) throws NotFoundException { -        return (WrappedPublicKeyRing) getWrappedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), false); +    public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(long id) throws NotFoundException { +        return (CanonicalizedPublicKeyRing) getCanonicalizedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), false);      } -    public WrappedPublicKeyRing getWrappedPublicKeyRing(Uri queryUri) throws NotFoundException { -        return (WrappedPublicKeyRing) getWrappedKeyRing(queryUri, false); +    public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(Uri queryUri) throws NotFoundException { +        return (CanonicalizedPublicKeyRing) getCanonicalizedKeyRing(queryUri, false);      } -    public WrappedSecretKeyRing getWrappedSecretKeyRing(long id) throws NotFoundException { -        return (WrappedSecretKeyRing) getWrappedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), true); +    public CanonicalizedSecretKeyRing getCanonicalizedSecretKeyRing(long id) throws NotFoundException { +        return (CanonicalizedSecretKeyRing) getCanonicalizedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), true);      } -    public WrappedSecretKeyRing getWrappedSecretKeyRing(Uri queryUri) throws NotFoundException { -        return (WrappedSecretKeyRing) getWrappedKeyRing(queryUri, true); +    public CanonicalizedSecretKeyRing getCanonicalizedSecretKeyRing(Uri queryUri) throws NotFoundException { +        return (CanonicalizedSecretKeyRing) getCanonicalizedKeyRing(queryUri, true);      } -    private KeyRing getWrappedKeyRing(Uri queryUri, boolean secret) throws NotFoundException { +    private KeyRing getCanonicalizedKeyRing(Uri queryUri, boolean secret) throws NotFoundException {          Cursor cursor = mContentResolver.query(queryUri,                  new String[]{                          // we pick from cache only information that is not easily available from keyrings @@ -252,8 +251,8 @@ public class ProviderHelper {                      throw new NotFoundException("Secret key not available!");                  }                  return secret -                        ? new WrappedSecretKeyRing(blob, true, verified) -                        : new WrappedPublicKeyRing(blob, hasAnySecret, verified); +                        ? new CanonicalizedSecretKeyRing(blob, true, verified) +                        : new CanonicalizedPublicKeyRing(blob, verified);              } else {                  throw new NotFoundException("Key not found!");              } @@ -271,16 +270,8 @@ public class ProviderHelper {       * and need to be saved externally to be preserved past the operation.       */      @SuppressWarnings("unchecked") -    private int internalSavePublicKeyRing(UncachedKeyRing keyRing, -                Progressable progress, boolean selfCertsAreTrusted) { -        if (keyRing.isSecret()) { -            log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET); -            return SaveKeyringResult.RESULT_ERROR; -        } -        if (!keyRing.isCanonicalized()) { -            log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET); -            return SaveKeyringResult.RESULT_ERROR; -        } +    private int saveCanonicalizedPublicKeyRing(CanonicalizedPublicKeyRing keyRing, +                                               Progressable progress, boolean selfCertsAreTrusted) {          // start with ok result          int result = SaveKeyringResult.SAVED_PUBLIC; @@ -318,7 +309,7 @@ public class ProviderHelper {              { // insert subkeys                  Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));                  int rank = 0; -                for (UncachedPublicKey key : new IterableIterator<UncachedPublicKey>(keyRing.getPublicKeys())) { +                for (CanonicalizedPublicKey key : keyRing.publicKeyIterator()) {                      long keyId = key.getKeyId();                      log(LogLevel.DEBUG, keyId == masterKeyId ? LogType.MSG_IP_MASTER : LogType.MSG_IP_SUBKEY,                              PgpKeyHelper.convertKeyIdToHex(keyId) @@ -401,7 +392,7 @@ public class ProviderHelper {              mIndent -= 1;              // get a list of owned secret keys, for verification filtering -            LongSparseArray<WrappedPublicKey> trustedKeys = getTrustedMasterKeys(); +            LongSparseArray<CanonicalizedPublicKey> trustedKeys = getTrustedMasterKeys();              // classify and order user ids. primary are moved to the front, revoked to the back,              // otherwise the order in the keyfile is preserved. @@ -445,13 +436,12 @@ public class ProviderHelper {                          // verify signatures from known private keys                          if (trustedKeys.indexOfKey(certId) >= 0) { -                            WrappedPublicKey trustedKey = trustedKeys.get(certId); +                            CanonicalizedPublicKey trustedKey = trustedKeys.get(certId);                              cert.init(trustedKey);                              if (cert.verifySignature(masterKey, userId)) {                                  item.trustedCerts.add(cert);                                  log(LogLevel.INFO, LogType.MSG_IP_UID_CERT_GOOD, -                                        PgpKeyHelper.convertKeyIdToHexShort(trustedKey.getKeyId()), -                                        trustedKey.getPrimaryUserId() +                                        PgpKeyHelper.convertKeyIdToHexShort(trustedKey.getKeyId())                                  );                              } else {                                  log(LogLevel.WARN, LogType.MSG_IP_UID_CERT_BAD); @@ -560,28 +550,13 @@ public class ProviderHelper {      /** Saves an UncachedKeyRing of the secret variant into the db.       * This method will fail if no corresponding public keyring is in the database!       */ -    private int internalSaveSecretKeyRing(UncachedKeyRing keyRing) { - -        if (!keyRing.isSecret()) { -            log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); -            return SaveKeyringResult.RESULT_ERROR; -        } - -        if (!keyRing.isCanonicalized()) { -            log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); -            return SaveKeyringResult.RESULT_ERROR; -        } +    private int saveCanonicalizedSecretKeyRing(CanonicalizedSecretKeyRing keyRing) {          long masterKeyId = keyRing.getMasterKeyId();          log(LogLevel.START, LogType.MSG_IS, PgpKeyHelper.convertKeyIdToHex(masterKeyId));          mIndent += 1; -        try { -            // Canonicalize this key, to assert a number of assumptions made about it. -            keyRing = keyRing.canonicalize(mLog, mIndent); -            if (keyRing == null) { -                return SaveKeyringResult.RESULT_ERROR; -            } +        try {              // IF this is successful, it's a secret key              int result = SaveKeyringResult.SAVED_SECRET; @@ -616,8 +591,7 @@ public class ProviderHelper {                  log(LogLevel.INFO, LogType.MSG_IS_IMPORTING_SUBKEYS);                  mIndent += 1;                  Set<Long> available = keyRing.getAvailableSubkeys(); -                for (UncachedPublicKey sub : -                        new IterableIterator<UncachedPublicKey>(keyRing.getPublicKeys())) { +                for (UncachedPublicKey sub : keyRing.publicKeyIterator()) {                      long id = sub.getKeyId();                      if (available.contains(id)) {                          int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", @@ -668,9 +642,16 @@ public class ProviderHelper {              log(LogLevel.START, LogType.MSG_IP, PgpKeyHelper.convertKeyIdToHex(masterKeyId));              mIndent += 1; +            if (publicRing.isSecret()) { +                log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET); +                return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); +            } + +            CanonicalizedPublicKeyRing canPublicRing; +              // If there is an old keyring, merge it              try { -                UncachedKeyRing oldPublicRing = getWrappedPublicKeyRing(masterKeyId).getUncached(); +                UncachedKeyRing oldPublicRing = getCanonicalizedPublicKeyRing(masterKeyId).getUncachedKeyRing();                  // Merge data from new public ring into the old one                  publicRing = oldPublicRing.merge(publicRing, mLog, mIndent); @@ -681,8 +662,8 @@ public class ProviderHelper {                  }                  // Canonicalize this keyring, to assert a number of assumptions made about it. -                publicRing = publicRing.canonicalize(mLog, mIndent); -                if (publicRing == null) { +                canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent); +                if (canPublicRing == null) {                      return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);                  } @@ -696,39 +677,40 @@ public class ProviderHelper {                  // Not an issue, just means we are dealing with a new keyring.                  // Canonicalize this keyring, to assert a number of assumptions made about it. -                publicRing = publicRing.canonicalize(mLog, mIndent); -                if (publicRing == null) { +                canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent); +                if (canPublicRing == null) {                      return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);                  }              }              // If there is a secret key, merge new data (if any) and save the key for later -            UncachedKeyRing secretRing; +            CanonicalizedSecretKeyRing canSecretRing;              try { -                secretRing = getWrappedSecretKeyRing(publicRing.getMasterKeyId()).getUncached(); +                UncachedKeyRing secretRing = getCanonicalizedSecretKeyRing(publicRing.getMasterKeyId()).getUncachedKeyRing();                  // Merge data from new public ring into secret one                  secretRing = secretRing.merge(publicRing, mLog, mIndent);                  if (secretRing == null) {                      return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);                  } -                secretRing = secretRing.canonicalize(mLog, mIndent); -                if (secretRing == null) { +                // This has always been a secret key ring, this is a safe cast +                canSecretRing = (CanonicalizedSecretKeyRing) secretRing.canonicalize(mLog, mIndent); +                if (canSecretRing == null) {                      return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);                  }              } catch (NotFoundException e) {                  // No secret key available (this is what happens most of the time) -                secretRing = null; +                canSecretRing = null;              } -            int result = internalSavePublicKeyRing(publicRing, progress, secretRing != null); +            int result = saveCanonicalizedPublicKeyRing(canPublicRing, progress, canSecretRing != null);              // Save the saved keyring (if any) -            if (secretRing != null) { +            if (canSecretRing != null) {                  progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100); -                int secretResult = internalSaveSecretKeyRing(secretRing); +                int secretResult = saveCanonicalizedSecretKeyRing(canSecretRing);                  if ((secretResult & SaveKeyringResult.RESULT_ERROR) != SaveKeyringResult.RESULT_ERROR) {                      result |= SaveKeyringResult.SAVED_SECRET;                  } @@ -752,12 +734,19 @@ public class ProviderHelper {              log(LogLevel.START, LogType.MSG_IS, PgpKeyHelper.convertKeyIdToHex(masterKeyId));              mIndent += 1; +            if ( ! secretRing.isSecret()) { +                log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); +                return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); +            } + +            CanonicalizedSecretKeyRing canSecretRing; +              // If there is an old secret key, merge it.              try { -                UncachedKeyRing oldSecretRing = getWrappedSecretKeyRing(masterKeyId).getUncached(); +                UncachedKeyRing oldSecretRing = getCanonicalizedSecretKeyRing(masterKeyId).getUncachedKeyRing();                  // Merge data from new secret ring into old one -                secretRing = oldSecretRing.merge(secretRing, mLog, mIndent); +                secretRing = secretRing.merge(oldSecretRing, mLog, mIndent);                  // If this is null, there is an error in the log so we can just return                  if (secretRing == null) { @@ -765,8 +754,9 @@ public class ProviderHelper {                  }                  // Canonicalize this keyring, to assert a number of assumptions made about it. -                secretRing = secretRing.canonicalize(mLog, mIndent); -                if (secretRing == null) { +                // This is a safe cast, because we made sure this is a secret ring above +                canSecretRing = (CanonicalizedSecretKeyRing) secretRing.canonicalize(mLog, mIndent); +                if (canSecretRing == null) {                      return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);                  } @@ -781,8 +771,9 @@ public class ProviderHelper {                  // Not an issue, just means we are dealing with a new keyring                  // Canonicalize this keyring, to assert a number of assumptions made about it. -                secretRing = secretRing.canonicalize(mLog, mIndent); -                if (secretRing == null) { +                // This is a safe cast, because we made sure this is a secret ring above +                canSecretRing = (CanonicalizedSecretKeyRing) secretRing.canonicalize(mLog, mIndent); +                if (canSecretRing == null) {                      return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);                  } @@ -791,39 +782,34 @@ public class ProviderHelper {              // Merge new data into public keyring as well, if there is any              UncachedKeyRing publicRing;              try { -                UncachedKeyRing oldPublicRing = getWrappedPublicKeyRing(masterKeyId).getUncached(); +                UncachedKeyRing oldPublicRing = getCanonicalizedPublicKeyRing(masterKeyId).getUncachedKeyRing(); -                // Merge data from new public ring into secret one +                // Merge data from new secret ring into public one                  publicRing = oldPublicRing.merge(secretRing, mLog, mIndent);                  if (publicRing == null) {                      return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);                  } -                // If nothing changed, never mind -                if (Arrays.hashCode(publicRing.getEncoded()) -                        == Arrays.hashCode(oldPublicRing.getEncoded())) { -                    publicRing = null; -                } -              } catch (NotFoundException e) {                  log(LogLevel.DEBUG, LogType.MSG_IS_PUBRING_GENERATE);                  publicRing = secretRing.extractPublicKeyRing();              } -            if (publicRing != null) { -                publicRing = publicRing.canonicalize(mLog, mIndent); -                if (publicRing == null) { -                    return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); -                } +            CanonicalizedPublicKeyRing canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent); +            if (canPublicRing == null) { +                return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); +            } -                int result = internalSavePublicKeyRing(publicRing, progress, true); -                if ((result & SaveKeyringResult.RESULT_ERROR) == SaveKeyringResult.RESULT_ERROR) { -                    return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); -                } +            int result; + +            result = saveCanonicalizedPublicKeyRing(canPublicRing, progress, true); +            if ((result & SaveKeyringResult.RESULT_ERROR) == SaveKeyringResult.RESULT_ERROR) { +                return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);              }              progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100); -            int result = internalSaveSecretKeyRing(secretRing); +            result = saveCanonicalizedSecretKeyRing(canSecretRing); +              return new SaveKeyringResult(result, mLog);          } catch (IOException e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index 17c277026..5ed95acb3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -53,6 +53,12 @@ import java.util.Set;  public class OpenPgpService extends RemoteService { +    static final String[] KEYRING_PROJECTION = +            new String[]{ +                    KeyRings._ID, +                    KeyRings.MASTER_KEY_ID, +            }; +      /**       * Search database for key ids based on emails.       * @@ -70,7 +76,7 @@ public class OpenPgpService extends RemoteService {          for (String email : encryptionUserIds) {              Uri uri = KeyRings.buildUnifiedKeyRingsFindByEmailUri(email); -            Cursor cursor = getContentResolver().query(uri, null, null, null, null); +            Cursor cursor = getContentResolver().query(uri, KEYRING_PROJECTION, null, null, null);              try {                  if (cursor != null && cursor.moveToFirst()) {                      long id = cursor.getLong(cursor.getColumnIndex(KeyRings.MASTER_KEY_ID)); @@ -419,7 +425,7 @@ public class OpenPgpService extends RemoteService {              try {                  // try to find key, throws NotFoundException if not in db! -                mProviderHelper.getWrappedPublicKeyRing(masterKeyId); +                mProviderHelper.getCanonicalizedPublicKeyRing(masterKeyId);                  Intent result = new Intent();                  result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java index 0b1d521ad..2ba792f9a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java @@ -32,10 +32,11 @@ import android.widget.Button;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.KeyRing;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.remote.AccountSettings; -import org.sufficientlysecure.keychain.ui.EditKeyActivityOld; +import org.sufficientlysecure.keychain.ui.CreateKeyActivity;  import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment;  import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter;  import org.sufficientlysecure.keychain.util.AlgorithmNames; @@ -163,11 +164,11 @@ public class AccountSettingsFragment extends Fragment implements      }      private void createKey() { -        Intent intent = new Intent(getActivity(), EditKeyActivityOld.class); -        intent.setAction(EditKeyActivityOld.ACTION_CREATE_KEY); -        intent.putExtra(EditKeyActivityOld.EXTRA_GENERATE_DEFAULT_KEYS, true); -        // set default user id to account name -        intent.putExtra(EditKeyActivityOld.EXTRA_USER_IDS, mAccSettings.getAccountName()); +        String[] 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]);          startActivityForResult(intent, REQUEST_CODE_CREATE_KEY);      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 00f210cc1..7318833e1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -31,11 +31,15 @@ import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.helper.FileHelper;  import org.sufficientlysecure.keychain.helper.OtherHelper;  import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.keyimport.FileImportCache;  import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; -import org.sufficientlysecure.keychain.keyimport.Keyserver;  import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;  import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver; +import org.sufficientlysecure.keychain.keyimport.Keyserver;  import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; +import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;  import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;  import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;  import org.sufficientlysecure.keychain.pgp.PgpHelper; @@ -44,15 +48,13 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;  import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;  import org.sufficientlysecure.keychain.pgp.Progressable;  import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; -import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing; -import org.sufficientlysecure.keychain.pgp.WrappedSecretKey; -import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;  import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;  import org.sufficientlysecure.keychain.provider.KeychainDatabase;  import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; +import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult; +import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult;  import org.sufficientlysecure.keychain.util.InputData;  import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.ProgressScaler; @@ -138,9 +140,6 @@ public class KeychainIntentService extends IntentService      // delete file securely      public static final String DELETE_FILE = "deleteFile"; -    // import key -    public static final String IMPORT_KEY_LIST = "import_key_list"; -      // export key      public static final String EXPORT_OUTPUT_STREAM = "export_output_stream";      public static final String EXPORT_FILENAME = "export_filename"; @@ -175,7 +174,7 @@ public class KeychainIntentService extends IntentService      // export      public static final String RESULT_EXPORT = "exported"; -    public static final String RESULT = "result"; +    public static final String RESULT_IMPORT = "result";      Messenger mMessenger; @@ -342,39 +341,37 @@ public class KeychainIntentService extends IntentService                  /* Operation */                  ProviderHelper providerHelper = new ProviderHelper(this); -                PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 10, 50, 100)); -                try { -                    OperationLog log = new OperationLog(); -                    UncachedKeyRing ring; -                    if (saveParcel.mMasterKeyId != null) { -                        String passphrase = data.getString(SAVE_KEYRING_PASSPHRASE); -                        WrappedSecretKeyRing secRing = -                                providerHelper.getWrappedSecretKeyRing(saveParcel.mMasterKeyId); - -                        ring = keyOperations.modifySecretKeyRing(secRing, saveParcel, -                                passphrase, log, 0); -                    } else { -                        ring = keyOperations.createSecretKeyRing(saveParcel, log, 0); -                    } +                PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 10, 60, 100)); +                EditKeyResult result; -                    providerHelper.saveSecretKeyRing(ring, new ProgressScaler(this, 10, 95, 100)); +                if (saveParcel.mMasterKeyId != null) { +                    String passphrase = data.getString(SAVE_KEYRING_PASSPHRASE); +                    CanonicalizedSecretKeyRing secRing = +                            providerHelper.getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId); -                    // cache new passphrase -                    if (saveParcel.newPassphrase != null) { -                        PassphraseCacheService.addCachedPassphrase(this, ring.getMasterKeyId(), -                                saveParcel.newPassphrase); -                    } -                } catch (ProviderHelper.NotFoundException e) { -                    sendErrorToHandler(e); +                    result = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase); +                } else { +                    result = keyOperations.createSecretKeyRing(saveParcel); +                } + +                UncachedKeyRing ring = result.getRing(); + +                providerHelper.saveSecretKeyRing(ring,  new ProgressScaler(this, 60, 95, 100)); + +                // cache new passphrase +                if (saveParcel.mNewPassphrase != null) { +                    PassphraseCacheService.addCachedPassphrase(this, ring.getMasterKeyId(), +                            saveParcel.mNewPassphrase, ring.getPublicKey().getPrimaryUserIdWithFallback());                  }                  setProgress(R.string.progress_done, 100, 100);                  /* Output */ -                sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); +                sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);              } catch (Exception e) {                  sendErrorToHandler(e);              } +          } else if (ACTION_DELETE_FILE_SECURELY.equals(action)) {              try {                  /* Input */ @@ -398,13 +395,15 @@ public class KeychainIntentService extends IntentService              }          } else if (ACTION_IMPORT_KEYRING.equals(action)) {              try { -                List<ParcelableKeyRing> entries = data.getParcelableArrayList(IMPORT_KEY_LIST); +                // get entries from cached file +                FileImportCache cache = new FileImportCache(this); +                List<ParcelableKeyRing> entries = cache.readCache();                  PgpImportExport pgpImportExport = new PgpImportExport(this, this); -                OperationResults.ImportResult result = pgpImportExport.importKeyRings(entries); +                ImportKeyResult result = pgpImportExport.importKeyRings(entries);                  Bundle resultData = new Bundle(); -                resultData.putParcelable(RESULT, result); +                resultData.putParcelable(RESULT_IMPORT, result);                  sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);              } catch (Exception e) { @@ -488,7 +487,7 @@ public class KeychainIntentService extends IntentService                  HkpKeyserver server = new HkpKeyserver(keyServer);                  ProviderHelper providerHelper = new ProviderHelper(this); -                WrappedPublicKeyRing keyring = providerHelper.getWrappedPublicKeyRing(dataUri); +                CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri);                  PgpImportExport pgpImportExport = new PgpImportExport(this, null);                  boolean uploaded = pgpImportExport.uploadKeyRingToServer(server, keyring); @@ -537,7 +536,6 @@ public class KeychainIntentService extends IntentService                  Intent importIntent = new Intent(this, KeychainIntentService.class);                  importIntent.setAction(ACTION_IMPORT_KEYRING);                  Bundle importData = new Bundle(); -                importData.putParcelableArrayList(IMPORT_KEY_LIST, keyRings);                  importIntent.putExtra(EXTRA_DATA, importData);                  importIntent.putExtra(EXTRA_MESSENGER, mMessenger); @@ -564,9 +562,9 @@ public class KeychainIntentService extends IntentService                  }                  ProviderHelper providerHelper = new ProviderHelper(this); -                WrappedPublicKeyRing publicRing = providerHelper.getWrappedPublicKeyRing(pubKeyId); -                WrappedSecretKeyRing secretKeyRing = providerHelper.getWrappedSecretKeyRing(masterKeyId); -                WrappedSecretKey certificationKey = secretKeyRing.getSubKey(); +                CanonicalizedPublicKeyRing publicRing = providerHelper.getCanonicalizedPublicKeyRing(pubKeyId); +                CanonicalizedSecretKeyRing secretKeyRing = providerHelper.getCanonicalizedSecretKeyRing(masterKeyId); +                CanonicalizedSecretKey certificationKey = secretKeyRing.getSecretKey();                  if(!certificationKey.unlock(signaturePassphrase)) {                      throw new PgpGeneralException("Error extracting key (bad passphrase?)");                  } @@ -644,6 +642,12 @@ public class KeychainIntentService extends IntentService          }      } +    private void sendMessageToHandler(Integer arg1, OperationResultParcel data) { +        Bundle bundle = new Bundle(); +        bundle.putParcelable(OperationResultParcel.EXTRA_RESULT, data); +        sendMessageToHandler(arg1, null, bundle); +    } +      private void sendMessageToHandler(Integer arg1, Bundle data) {          sendMessageToHandler(arg1, null, data);      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java index 755827482..0cdbe708e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java @@ -25,12 +25,11 @@ import android.os.Message;  import android.support.v4.app.FragmentActivity;  import android.support.v4.app.FragmentManager; -import com.devspark.appmsg.AppMsg; -  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;  import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Notify;  public class KeychainIntentServiceHandler extends Handler { @@ -102,9 +101,9 @@ public class KeychainIntentServiceHandler extends Handler {                  // show error from service                  if (data.containsKey(DATA_ERROR)) { -                    AppMsg.makeText(mActivity, +                    Notify.showNotify(mActivity,                              mActivity.getString(R.string.error_message, data.getString(DATA_ERROR)), -                            AppMsg.STYLE_ALERT).show(); +                            Notify.Style.ERROR);                  }                  break; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 6bf6b655d..c27b3f6da 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -1,14 +1,27 @@  package org.sufficientlysecure.keychain.service; +import android.app.Activity; +import android.content.Intent;  import android.os.Parcel;  import android.os.Parcelable; +import android.view.View; + +import com.github.johnpersano.supertoasts.SuperCardToast; +import com.github.johnpersano.supertoasts.SuperToast; +import com.github.johnpersano.supertoasts.util.OnClickWrapper; +import com.github.johnpersano.supertoasts.util.Style;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.LogDisplayActivity; +import org.sufficientlysecure.keychain.ui.LogDisplayFragment;  import org.sufficientlysecure.keychain.util.IterableIterator;  import org.sufficientlysecure.keychain.util.Log;  import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List;  /** Represent the result of an operation.   * @@ -21,6 +34,9 @@ import java.util.ArrayList;   *   */  public class OperationResultParcel implements Parcelable { + +    public static final String EXTRA_RESULT = "operation_result"; +      /** Holds the overall result, the number specifying varying degrees of success. The first bit       * is 0 on overall success, 1 on overall failure. All other bits may be used for more specific       * conditions. */ @@ -67,9 +83,7 @@ public class OperationResultParcel implements Parcelable {              mType = type;              mParameters = parameters;              mIndent = indent; -        } -        public LogEntryParcel(LogLevel level, LogType type, Object... parameters) { -            this(level, type, 0, parameters); +            Log.v(Constants.TAG, "log: " + this.toString());          }          public LogEntryParcel(Parcel source) { @@ -102,6 +116,77 @@ public class OperationResultParcel implements Parcelable {              }          }; +        @Override +        public String toString() { +            return "LogEntryParcel{" + +                    "mLevel=" + mLevel + +                    ", mType=" + mType + +                    ", mParameters=" + Arrays.toString(mParameters) + +                    ", mIndent=" + mIndent + +                    '}'; +        } +    } + +    public SuperCardToast createNotify(final Activity activity) { + +        int resultType = getResult(); + +        String str; +        int duration, color; + +        // Not an overall failure +        if ((resultType & OperationResultParcel.RESULT_ERROR) == 0) { + +            if (getLog().containsWarnings()) { +                duration = 0; +                color = Style.ORANGE; +            } else { +                duration = SuperToast.Duration.LONG; +                color = Style.GREEN; +            } + +            str = "operation succeeded!"; +            // str = activity.getString(R.string.import_error); + +        } else { + +            duration = 0; +            color = Style.RED; + +            str = "operation failed"; +            // str = activity.getString(R.string.import_error); + +        } + +        boolean button = getLog() != null && !getLog().isEmpty(); +        SuperCardToast toast = new SuperCardToast(activity, +                button ? SuperToast.Type.BUTTON : SuperToast.Type.STANDARD, +                Style.getStyle(color, SuperToast.Animations.POPUP)); +        toast.setText(str); +        toast.setDuration(duration); +        toast.setIndeterminate(duration == 0); +        toast.setSwipeToDismiss(true); +        // If we have a log and it's non-empty, show a View Log button +        if (button) { +            toast.setButtonIcon(R.drawable.ic_action_view_as_list, +                    activity.getResources().getString(R.string.view_log)); +            toast.setButtonTextColor(activity.getResources().getColor(R.color.black)); +            toast.setTextColor(activity.getResources().getColor(R.color.black)); +            toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", +                    new SuperToast.OnClickListener() { +                        @Override +                        public void onClick(View view, Parcelable token) { +                            Intent intent = new Intent( +                                    activity, LogDisplayActivity.class); +                            intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResultParcel.this); +                            activity.startActivity(intent); +                        } +                    } +            )); +        } + +        return toast; +      }      /** This is an enum of all possible log events. @@ -123,6 +208,8 @@ public class OperationResultParcel implements Parcelable {       */      public static enum LogType { +        INTERNAL_ERROR (R.string.internal_error), +          // import public          MSG_IP(R.string.msg_ip),          MSG_IP_APPLY_BATCH (R.string.msg_ip_apply_batch), @@ -193,6 +280,7 @@ public class OperationResultParcel implements Parcelable {          MSG_KC_REVOKE_BAD_LOCAL (R.string.msg_kc_revoke_bad_local),          MSG_KC_REVOKE_BAD_TIME (R.string.msg_kc_revoke_bad_time),          MSG_KC_REVOKE_BAD_TYPE (R.string.msg_kc_revoke_bad_type), +        MSG_KC_REVOKE_BAD_TYPE_UID (R.string.msg_kc_revoke_bad_type_uid),          MSG_KC_REVOKE_BAD (R.string.msg_kc_revoke_bad),          MSG_KC_REVOKE_DUP (R.string.msg_kc_revoke_dup),          MSG_KC_SUB (R.string.msg_kc_sub), @@ -224,6 +312,7 @@ public class OperationResultParcel implements Parcelable {          MSG_KC_UID_NO_CERT (R.string.msg_kc_uid_no_cert),          MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup),          MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), +        MSG_KC_UID_REMOVE (R.string.msg_kc_uid_remove),          // keyring consolidation @@ -233,9 +322,17 @@ public class OperationResultParcel implements Parcelable {          MSG_MG_HETEROGENEOUS (R.string.msg_mg_heterogeneous),          MSG_MG_NEW_SUBKEY (R.string.msg_mg_new_subkey),          MSG_MG_FOUND_NEW (R.string.msg_mg_found_new), +        MSG_MG_UNCHANGED (R.string.msg_mg_unchanged),          // secret key create -        MSG_CR_ERROR_NO_MASTER (R.string.msg_mr), +        MSG_CR (R.string.msg_cr), +        MSG_CR_ERROR_NO_MASTER (R.string.msg_cr_error_no_master), +        MSG_CR_ERROR_NO_USER_ID (R.string.msg_cr_error_no_user_id), +        MSG_CR_ERROR_NO_CERTIFY (R.string.msg_cr_error_no_certify), +        MSG_CR_ERROR_KEYSIZE_512 (R.string.msg_cr_error_keysize_512), +        MSG_CR_ERROR_UNKNOWN_ALGO (R.string.msg_cr_error_unknown_algo), +        MSG_CR_ERROR_INTERNAL_PGP (R.string.msg_cr_error_internal_pgp), +        MSG_CR_ERROR_MASTER_ELGAMAL (R.string.msg_cr_error_master_elgamal),          // secret key modify          MSG_MF (R.string.msg_mr), @@ -243,10 +340,13 @@ public class OperationResultParcel implements Parcelable {          MSG_MF_ERROR_FINGERPRINT (R.string.msg_mf_error_fingerprint),          MSG_MF_ERROR_KEYID (R.string.msg_mf_error_keyid),          MSG_MF_ERROR_INTEGRITY (R.string.msg_mf_error_integrity), +        MSG_MF_ERROR_NOEXIST_PRIMARY (R.string.msg_mf_error_noexist_primary),          MSG_MF_ERROR_REVOKED_PRIMARY (R.string.msg_mf_error_revoked_primary),          MSG_MF_ERROR_PGP (R.string.msg_mf_error_pgp),          MSG_MF_ERROR_SIG (R.string.msg_mf_error_sig),          MSG_MF_PASSPHRASE (R.string.msg_mf_passphrase), +        MSG_MF_PRIMARY_REPLACE_OLD (R.string.msg_mf_primary_replace_old), +        MSG_MF_PRIMARY_NEW (R.string.msg_mf_primary_new),          MSG_MF_SUBKEY_CHANGE (R.string.msg_mf_subkey_change),          MSG_MF_SUBKEY_MISSING (R.string.msg_mf_subkey_missing),          MSG_MF_SUBKEY_NEW_ID (R.string.msg_mf_subkey_new_id), @@ -257,6 +357,7 @@ public class OperationResultParcel implements Parcelable {          MSG_MF_UID_ADD (R.string.msg_mf_uid_add),          MSG_MF_UID_PRIMARY (R.string.msg_mf_uid_primary),          MSG_MF_UID_REVOKE (R.string.msg_mf_uid_revoke), +        MSG_MF_UID_ERROR_EMPTY (R.string.msg_mf_uid_error_empty),          MSG_MF_UNLOCK_ERROR (R.string.msg_mf_unlock_error),          MSG_MF_UNLOCK (R.string.msg_mf_unlock),          ; @@ -288,7 +389,7 @@ public class OperationResultParcel implements Parcelable {      @Override      public void writeToParcel(Parcel dest, int flags) {          dest.writeInt(mResult); -        dest.writeTypedList(mLog); +        dest.writeTypedList(mLog.toList());      }      public static final Creator<OperationResultParcel> CREATOR = new Creator<OperationResultParcel>() { @@ -301,20 +402,21 @@ public class OperationResultParcel implements Parcelable {          }      }; -    public static class OperationLog extends ArrayList<LogEntryParcel> { +    public static class OperationLog implements Iterable<LogEntryParcel> { + +        private final List<LogEntryParcel> mParcels = new ArrayList<LogEntryParcel>();          /// Simple convenience method          public void add(LogLevel level, LogType type, int indent, Object... parameters) { -            Log.d(Constants.TAG, type.toString()); -            add(new OperationResultParcel.LogEntryParcel(level, type, indent, parameters)); +            mParcels.add(new OperationResultParcel.LogEntryParcel(level, type, indent, parameters));          }          public void add(LogLevel level, LogType type, int indent) { -            add(new OperationResultParcel.LogEntryParcel(level, type, indent, (Object[]) null)); +            mParcels.add(new OperationResultParcel.LogEntryParcel(level, type, indent, (Object[]) null));          }          public boolean containsWarnings() { -            for(LogEntryParcel entry : new IterableIterator<LogEntryParcel>(iterator())) { +            for(LogEntryParcel entry : new IterableIterator<LogEntryParcel>(mParcels.iterator())) {                  if (entry.mLevel == LogLevel.WARN || entry.mLevel == LogLevel.ERROR) {                      return true;                  } @@ -322,6 +424,22 @@ public class OperationResultParcel implements Parcelable {              return false;          } +        public void addAll(List<LogEntryParcel> parcels) { +            mParcels.addAll(parcels); +        } + +        public List<LogEntryParcel> toList() { +            return mParcels; +        } + +        public boolean isEmpty() { +            return mParcels.isEmpty(); +        } + +        @Override +        public Iterator<LogEntryParcel> iterator() { +            return mParcels.iterator(); +        }      }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java index fd3d4ed00..11829e7b8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java @@ -12,12 +12,13 @@ import com.github.johnpersano.supertoasts.util.OnClickWrapper;  import com.github.johnpersano.supertoasts.util.Style;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;  import org.sufficientlysecure.keychain.ui.LogDisplayActivity;  import org.sufficientlysecure.keychain.ui.LogDisplayFragment;  public abstract class OperationResults { -    public static class ImportResult extends OperationResultParcel { +    public static class ImportKeyResult extends OperationResultParcel {          public final int mNewKeys, mUpdatedKeys, mBadKeys; @@ -47,15 +48,15 @@ public abstract class OperationResults {              return (mResult & RESULT_FAIL_NOTHING) == RESULT_FAIL_NOTHING;          } -        public ImportResult(Parcel source) { +        public ImportKeyResult(Parcel source) {              super(source);              mNewKeys = source.readInt();              mUpdatedKeys = source.readInt();              mBadKeys = source.readInt();          } -        public ImportResult(int result, OperationLog log, -                            int newKeys, int updatedKeys, int badKeys) { +        public ImportKeyResult(int result, OperationLog log, +                               int newKeys, int updatedKeys, int badKeys) {              super(result, log);              mNewKeys = newKeys;              mUpdatedKeys = updatedKeys; @@ -70,17 +71,17 @@ public abstract class OperationResults {              dest.writeInt(mBadKeys);          } -        public static Creator<ImportResult> CREATOR = new Creator<ImportResult>() { -            public ImportResult createFromParcel(final Parcel source) { -                return new ImportResult(source); +        public static Creator<ImportKeyResult> CREATOR = new Creator<ImportKeyResult>() { +            public ImportKeyResult createFromParcel(final Parcel source) { +                return new ImportKeyResult(source);              } -            public ImportResult[] newArray(final int size) { -                return new ImportResult[size]; +            public ImportKeyResult[] newArray(final int size) { +                return new ImportKeyResult[size];              }          }; -        public void displayNotify(final Activity activity) { +        public SuperCardToast createNotify(final Activity activity) {              int resultType = getResult(); @@ -88,11 +89,11 @@ public abstract class OperationResults {              int duration, color;              // Not an overall failure -            if ((resultType & ImportResult.RESULT_ERROR) == 0) { +            if ((resultType & OperationResultParcel.RESULT_ERROR) == 0) {                  String withWarnings;                  // Any warnings? -                if ((resultType & ImportResult.RESULT_WITH_WARNINGS) > 0) { +                if ((resultType & ImportKeyResult.RESULT_WITH_WARNINGS) > 0) {                      duration = 0;                      color = Style.ORANGE;                      withWarnings = activity.getResources().getString(R.string.import_with_warnings); @@ -106,7 +107,7 @@ public abstract class OperationResults {                  if (this.isOkBoth()) {                      str = activity.getResources().getQuantityString(                              R.plurals.import_keys_added_and_updated_1, mNewKeys, mNewKeys); -                    str += activity.getResources().getQuantityString( +                    str += " "+ activity.getResources().getQuantityString(                              R.plurals.import_keys_added_and_updated_2, mUpdatedKeys, mUpdatedKeys, withWarnings);                  } else if (isOkUpdated()) {                      str = activity.getResources().getQuantityString( @@ -142,7 +143,7 @@ public abstract class OperationResults {              // If we have a log and it's non-empty, show a View Log button              if (button) {                  toast.setButtonIcon(R.drawable.ic_action_view_as_list, -                        activity.getResources().getString(R.string.import_view_log)); +                        activity.getResources().getString(R.string.view_log));                  toast.setButtonTextColor(activity.getResources().getColor(R.color.black));                  toast.setTextColor(activity.getResources().getColor(R.color.black));                  toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", @@ -151,18 +152,59 @@ public abstract class OperationResults {                              public void onClick(View view, Parcelable token) {                                  Intent intent = new Intent(                                          activity, LogDisplayActivity.class); -                                intent.putExtra(LogDisplayFragment.EXTRA_RESULT, ImportResult.this); +                                intent.putExtra(LogDisplayFragment.EXTRA_RESULT, ImportKeyResult.this);                                  activity.startActivity(intent);                              }                          }                  ));              } -            toast.show(); +            return toast; + +        } + +    } + +    public static class EditKeyResult extends OperationResultParcel { + +        private transient UncachedKeyRing mRing; +        public final Long mRingMasterKeyId; + +        public EditKeyResult(int result, OperationLog log, +                               UncachedKeyRing ring) { +            super(result, log); +            mRing = ring; +            mRingMasterKeyId = ring != null ? ring.getMasterKeyId() : null; +        } + +        public UncachedKeyRing getRing() { +            return mRing; +        } + +        public EditKeyResult(Parcel source) { +            super(source); +            mRingMasterKeyId = source.readLong(); +        } + +        @Override +        public void writeToParcel(Parcel dest, int flags) { +            super.writeToParcel(dest, flags); +            dest.writeLong(mRingMasterKeyId);          } +        public static Creator<EditKeyResult> CREATOR = new Creator<EditKeyResult>() { +            public EditKeyResult createFromParcel(final Parcel source) { +                return new EditKeyResult(source); +            } + +            public EditKeyResult[] newArray(final int size) { +                return new EditKeyResult[size]; +            } +        }; +      } +      public static class SaveKeyringResult extends OperationResultParcel {          public SaveKeyringResult(int result, OperationLog log) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java index 28f230f71..c4ecfdec5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -18,6 +18,7 @@  package org.sufficientlysecure.keychain.service;  import android.app.AlarmManager; +import android.app.Notification;  import android.app.PendingIntent;  import android.app.Service;  import android.content.BroadcastReceiver; @@ -25,6 +26,7 @@ import android.content.Context;  import android.content.Intent;  import android.content.IntentFilter;  import android.os.Binder; +import android.os.Build;  import android.os.Bundle;  import android.os.Handler;  import android.os.HandlerThread; @@ -32,11 +34,15 @@ import android.os.IBinder;  import android.os.Message;  import android.os.Messenger;  import android.os.RemoteException; +  import android.support.v4.util.LongSparseArray; +import android.support.v4.app.NotificationCompat;  import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.helper.Preferences; -import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; +import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.util.Log; @@ -54,6 +60,8 @@ public class PassphraseCacheService extends Service {              + "PASSPHRASE_CACHE_ADD";      public static final String ACTION_PASSPHRASE_CACHE_GET = Constants.INTENT_PREFIX              + "PASSPHRASE_CACHE_GET"; +    public static final String ACTION_PASSPHRASE_CACHE_CLEAR = Constants.INTENT_PREFIX +            + "PASSPHRASE_CACHE_CLEAR";      public static final String BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE = Constants.INTENT_PREFIX              + "PASSPHRASE_CACHE_BROADCAST"; @@ -62,13 +70,16 @@ public class PassphraseCacheService extends Service {      public static final String EXTRA_KEY_ID = "key_id";      public static final String EXTRA_PASSPHRASE = "passphrase";      public static final String EXTRA_MESSENGER = "messenger"; +    public static final String EXTRA_USER_ID = "user_id";      private static final int REQUEST_ID = 0;      private static final long DEFAULT_TTL = 15; +    private static final int NOTIFICATION_ID = 1; +      private BroadcastReceiver mIntentReceiver; -    private LongSparseArray<String> mPassphraseCache = new LongSparseArray<String>(); +    private LongSparseArray<CachedPassphrase> mPassphraseCache = new LongSparseArray<CachedPassphrase>();      Context mContext; @@ -81,14 +92,17 @@ public class PassphraseCacheService extends Service {       * @param keyId       * @param passphrase       */ -    public static void addCachedPassphrase(Context context, long keyId, String passphrase) { +    public static void addCachedPassphrase(Context context, long keyId, String passphrase, +                                           String primaryUserId) {          Log.d(Constants.TAG, "PassphraseCacheService.cacheNewPassphrase() for " + keyId);          Intent intent = new Intent(context, PassphraseCacheService.class);          intent.setAction(ACTION_PASSPHRASE_CACHE_ADD); +          intent.putExtra(EXTRA_TTL, Preferences.getPreferences(context).getPassphraseCacheTtl());          intent.putExtra(EXTRA_PASSPHRASE, passphrase);          intent.putExtra(EXTRA_KEY_ID, keyId); +        intent.putExtra(EXTRA_USER_ID, primaryUserId);          context.startService(intent);      } @@ -159,42 +173,46 @@ public class PassphraseCacheService extends Service {          // passphrase for symmetric encryption?          if (keyId == Constants.key.symmetric) {              Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for symmetric encryption"); -            String cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric); +            String cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric).getPassphrase();              if (cachedPassphrase == null) {                  return null;              } -            addCachedPassphrase(this, Constants.key.symmetric, cachedPassphrase); +            addCachedPassphrase(this, Constants.key.symmetric, cachedPassphrase, getString(R.string.passp_cache_notif_pwd));              return cachedPassphrase;          }          // try to get master key id which is used as an identifier for cached passphrases          try {              Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for masterKeyId " + keyId); -            WrappedSecretKeyRing key = new ProviderHelper(this).getWrappedSecretKeyRing( +            CanonicalizedSecretKeyRing key = new ProviderHelper(this).getCanonicalizedSecretKeyRing(                      KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId));              // no passphrase needed? just add empty string and return it, then              if (!key.hasPassphrase()) {                  Log.d(Constants.TAG, "Key has no passphrase! Caches and returns empty passphrase!"); -                addCachedPassphrase(this, keyId, ""); +                try { +                    addCachedPassphrase(this, keyId, "", key.getPrimaryUserIdWithFallback()); +                } catch (PgpGeneralException e) { +                    Log.d(Constants.TAG, "PgpGeneralException occured"); +                }                  return "";              }              // get cached passphrase -            String cachedPassphrase = mPassphraseCache.get(keyId); +            CachedPassphrase cachedPassphrase = mPassphraseCache.get(keyId);              if (cachedPassphrase == null) { -                Log.d(Constants.TAG, "PassphraseCacheService Passphrase not (yet) cached, returning null"); +                Log.d(Constants.TAG, "PassphraseCacheService: Passphrase not (yet) cached, returning null");                  // not really an error, just means the passphrase is not cached but not empty either                  return null;              }              // set it again to reset the cache life cycle -            Log.d(Constants.TAG, "PassphraseCacheService Cache passphrase again when getting it!"); -            addCachedPassphrase(this, keyId, cachedPassphrase); -            return cachedPassphrase; +            Log.d(Constants.TAG, "PassphraseCacheService: Cache passphrase again when getting it!"); +            addCachedPassphrase(this, keyId, cachedPassphrase.getPassphrase(), cachedPassphrase.getPrimaryUserID()); +            return cachedPassphrase.getPassphrase();          } catch (ProviderHelper.NotFoundException e) { -            Log.e(Constants.TAG, "PassphraseCacheService Passphrase for unknown key was requested!"); +            Log.e(Constants.TAG, "PassphraseCacheService: Passphrase for unknown key was requested!");              return null;          }      } @@ -211,7 +229,7 @@ public class PassphraseCacheService extends Service {                  public void onReceive(Context context, Intent intent) {                      String action = intent.getAction(); -                    Log.d(Constants.TAG, "PassphraseCacheService Received broadcast..."); +                    Log.d(Constants.TAG, "PassphraseCacheService: Received broadcast...");                      if (action.equals(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE)) {                          long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1); @@ -236,10 +254,8 @@ public class PassphraseCacheService extends Service {      private static PendingIntent buildIntent(Context context, long keyId) {          Intent intent = new Intent(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE);          intent.putExtra(EXTRA_KEY_ID, keyId); -        PendingIntent sender = PendingIntent.getBroadcast(context, REQUEST_ID, intent, +        return PendingIntent.getBroadcast(context, REQUEST_ID, intent,                  PendingIntent.FLAG_CANCEL_CURRENT); - -        return sender;      }      /** @@ -256,14 +272,17 @@ public class PassphraseCacheService extends Service {              if (ACTION_PASSPHRASE_CACHE_ADD.equals(intent.getAction())) {                  long ttl = intent.getLongExtra(EXTRA_TTL, DEFAULT_TTL);                  long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1); +                  String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE); +                String primaryUserID = intent.getStringExtra(EXTRA_USER_ID);                  Log.d(Constants.TAG, -                        "PassphraseCacheService Received ACTION_PASSPHRASE_CACHE_ADD intent in onStartCommand() with keyId: " -                                + keyId + ", ttl: " + ttl); +                        "PassphraseCacheService: Received ACTION_PASSPHRASE_CACHE_ADD intent in onStartCommand() with keyId: " +                                + keyId + ", ttl: " + ttl + ", usrId: " + primaryUserID +                ); -                // add keyId and passphrase to memory -                mPassphraseCache.put(keyId, passphrase); +                // add keyId, passphrase and primary user id to memory +                mPassphraseCache.put(keyId, new CachedPassphrase(passphrase, primaryUserID));                  if (ttl > 0) {                      // register new alarm with keyId for this passphrase @@ -271,6 +290,8 @@ public class PassphraseCacheService extends Service {                      AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);                      am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, keyId));                  } + +                updateService();              } else if (ACTION_PASSPHRASE_CACHE_GET.equals(intent.getAction())) {                  long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);                  Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER); @@ -284,10 +305,21 @@ public class PassphraseCacheService extends Service {                  try {                      messenger.send(msg);                  } catch (RemoteException e) { -                    Log.e(Constants.TAG, "PassphraseCacheService Sending message failed", e); +                    Log.e(Constants.TAG, "PassphraseCacheService: Sending message failed", e);                  } +            } else if (ACTION_PASSPHRASE_CACHE_CLEAR.equals(intent.getAction())) { +                AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); + +                // Stop all ttl alarms +                for (int i = 0; i < mPassphraseCache.size(); i++) { +                    am.cancel(buildIntent(this, mPassphraseCache.keyAt(i))); +                } + +                mPassphraseCache.clear(); + +                updateService();              } else { -                Log.e(Constants.TAG, "PassphraseCacheService Intent or Intent Action not supported!"); +                Log.e(Constants.TAG, "PassphraseCacheService: Intent or Intent Action not supported!");              }          } @@ -306,11 +338,74 @@ public class PassphraseCacheService extends Service {          Log.d(Constants.TAG, "PassphraseCacheService Timeout of keyId " + keyId + ", removed from memory!"); -        // stop whole service if no cached passphrases remaining -        if (mPassphraseCache.size() == 0) { -            Log.d(Constants.TAG, "PassphraseCacheServic No passphrases remaining in memory, stopping service!"); -            stopSelf(); +        updateService(); +    } + +    private void updateService() { +        if (mPassphraseCache.size() > 0) { +            startForeground(NOTIFICATION_ID, getNotification()); +        } else { +            // stop whole service if no cached passphrases remaining +            Log.d(Constants.TAG, "PassphraseCacheService: No passphrases remaining in memory, stopping service!"); +            stopForeground(true); +        } +    } + +    private Notification getNotification() { +        NotificationCompat.Builder builder = new NotificationCompat.Builder(this); + +        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { +            builder.setSmallIcon(R.drawable.ic_launcher) +                    .setContentTitle(getString(R.string.app_name)) +                    .setContentText(String.format(getString(R.string.passp_cache_notif_n_keys), +                            mPassphraseCache.size())); + +            NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); + +            inboxStyle.setBigContentTitle(getString(R.string.passp_cache_notif_keys)); + +            // Moves events into the big view +            for (int i = 0; i < mPassphraseCache.size(); i++) { +                inboxStyle.addLine(mPassphraseCache.valueAt(i).getPrimaryUserID()); +            } + +            // Moves the big view style object into the notification object. +            builder.setStyle(inboxStyle); + +            // Add purging action +            Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class); +            intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR); +            builder.addAction( +                    R.drawable.abc_ic_clear_normal, +                    getString(R.string.passp_cache_notif_clear), +                    PendingIntent.getService( +                            getApplicationContext(), +                            0, +                            intent, +                            PendingIntent.FLAG_UPDATE_CURRENT +                    ) +            ); +        } else { +            // Fallback, since expandable notifications weren't available back then +            builder.setSmallIcon(R.drawable.ic_launcher) +                    .setContentTitle(String.format(getString(R.string.passp_cache_notif_n_keys, +                            mPassphraseCache.size()))) +                    .setContentText(getString(R.string.passp_cache_notif_click_to_clear)); + +            Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class); +            intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR); + +            builder.setContentIntent( +                    PendingIntent.getService( +                            getApplicationContext(), +                            0, +                            intent, +                            PendingIntent.FLAG_UPDATE_CURRENT +                    ) +            );          } + +        return builder.build();      }      @Override @@ -341,4 +436,29 @@ public class PassphraseCacheService extends Service {      private final IBinder mBinder = new PassphraseCacheBinder(); -} +    public class CachedPassphrase { +        private String primaryUserID; +        private String passphrase; + +        public CachedPassphrase(String passphrase, String primaryUserID) { +            setPassphrase(passphrase); +            setPrimaryUserID(primaryUserID); +        } + +        public String getPrimaryUserID() { +            return primaryUserID; +        } + +        public String getPassphrase() { +            return passphrase; +        } + +        public void setPrimaryUserID(String primaryUserID) { +            this.primaryUserID = primaryUserID; +        } + +        public void setPassphrase(String passphrase) { +            this.passphrase = passphrase; +        } +    } +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index a56095767..5e90b396e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -27,23 +27,19 @@ public class SaveKeyringParcel implements Parcelable {      // the key fingerprint, for safety. MUST be null for a new key.      public byte[] mFingerprint; -    public String newPassphrase; +    public String mNewPassphrase; -    public ArrayList<String> addUserIds; -    public ArrayList<SubkeyAdd> addSubKeys; +    public ArrayList<String> mAddUserIds; +    public ArrayList<SubkeyAdd> mAddSubKeys; -    public ArrayList<SubkeyChange> changeSubKeys; -    public String changePrimaryUserId; +    public ArrayList<SubkeyChange> mChangeSubKeys; +    public String mChangePrimaryUserId; -    public ArrayList<String> revokeUserIds; -    public ArrayList<Long> revokeSubKeys; +    public ArrayList<String> mRevokeUserIds; +    public ArrayList<Long> mRevokeSubKeys;      public SaveKeyringParcel() { -        addUserIds = new ArrayList<String>(); -        addSubKeys = new ArrayList<SubkeyAdd>(); -        changeSubKeys = new ArrayList<SubkeyChange>(); -        revokeUserIds = new ArrayList<String>(); -        revokeSubKeys = new ArrayList<Long>(); +        reset();      }      public SaveKeyringParcel(long masterKeyId, byte[] fingerprint) { @@ -52,6 +48,16 @@ public class SaveKeyringParcel implements Parcelable {          mFingerprint = fingerprint;      } +    public void reset() { +        mNewPassphrase = null; +        mAddUserIds = new ArrayList<String>(); +        mAddSubKeys = new ArrayList<SubkeyAdd>(); +        mChangePrimaryUserId = null; +        mChangeSubKeys = new ArrayList<SubkeyChange>(); +        mRevokeUserIds = new ArrayList<String>(); +        mRevokeSubKeys = new ArrayList<Long>(); +    } +      // performance gain for using Parcelable here would probably be negligible,      // use Serializable instead.      public static class SubkeyAdd implements Serializable { @@ -70,6 +76,7 @@ public class SaveKeyringParcel implements Parcelable {      public static class SubkeyChange implements Serializable {          public long mKeyId;          public Integer mFlags; +        // this is a long unix timestamp, in seconds (NOT MILLISECONDS!)          public Long mExpiry;          public SubkeyChange(long keyId, Integer flags, Long expiry) {              mKeyId = keyId; @@ -82,16 +89,16 @@ public class SaveKeyringParcel implements Parcelable {          mMasterKeyId = source.readInt() != 0 ? source.readLong() : null;          mFingerprint = source.createByteArray(); -        newPassphrase = source.readString(); +        mNewPassphrase = source.readString(); -        addUserIds = source.createStringArrayList(); -        addSubKeys = (ArrayList<SubkeyAdd>) source.readSerializable(); +        mAddUserIds = source.createStringArrayList(); +        mAddSubKeys = (ArrayList<SubkeyAdd>) source.readSerializable(); -        changeSubKeys = (ArrayList<SubkeyChange>) source.readSerializable(); -        changePrimaryUserId = source.readString(); +        mChangeSubKeys = (ArrayList<SubkeyChange>) source.readSerializable(); +        mChangePrimaryUserId = source.readString(); -        revokeUserIds = source.createStringArrayList(); -        revokeSubKeys = (ArrayList<Long>) source.readSerializable(); +        mRevokeUserIds = source.createStringArrayList(); +        mRevokeSubKeys = (ArrayList<Long>) source.readSerializable();      }      @Override @@ -102,16 +109,16 @@ public class SaveKeyringParcel implements Parcelable {          }          destination.writeByteArray(mFingerprint); -        destination.writeString(newPassphrase); +        destination.writeString(mNewPassphrase); -        destination.writeStringList(addUserIds); -        destination.writeSerializable(addSubKeys); +        destination.writeStringList(mAddUserIds); +        destination.writeSerializable(mAddSubKeys); -        destination.writeSerializable(changeSubKeys); -        destination.writeString(changePrimaryUserId); +        destination.writeSerializable(mChangeSubKeys); +        destination.writeString(mChangePrimaryUserId); -        destination.writeStringList(revokeUserIds); -        destination.writeSerializable(revokeSubKeys); +        destination.writeStringList(mRevokeUserIds); +        destination.writeSerializable(mRevokeSubKeys);      }      public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java deleted file mode 100644 index a565f0707..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.sufficientlysecure.keychain.testsupport; - -import android.content.Context; - -import org.sufficientlysecure.keychain.pgp.NullProgressable; -import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.service.OperationResults; - -import java.util.Collection; - -/** - * Helper for tests of the Keyring import in ProviderHelper. - */ -public class KeyringTestingHelper { - -    private final Context context; - -    public KeyringTestingHelper(Context robolectricContext) { -        this.context = robolectricContext; -    } - -    public boolean addKeyring(Collection<String> blobFiles) throws Exception { - -        ProviderHelper providerHelper = new ProviderHelper(context); - -        byte[] data = TestDataUtil.readAllFully(blobFiles); -        UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data); -        long masterKeyId = ring.getMasterKeyId(); - -        // Should throw an exception; key is not yet saved -        retrieveKeyAndExpectNotFound(providerHelper, masterKeyId); - -        OperationResults.SaveKeyringResult saveKeyringResult = providerHelper.savePublicKeyRing(ring, new NullProgressable()); - -        boolean saveSuccess = saveKeyringResult.success(); - -        // Now re-retrieve the saved key. Should not throw an exception. -        providerHelper.getWrappedPublicKeyRing(masterKeyId); - -        // A different ID should still fail -        retrieveKeyAndExpectNotFound(providerHelper, masterKeyId - 1); - -        return saveSuccess; -    } - - -    private void retrieveKeyAndExpectNotFound(ProviderHelper providerHelper, long masterKeyId) { -        try { -            providerHelper.getWrappedPublicKeyRing(masterKeyId); -            throw new AssertionError("Was expecting the previous call to fail!"); -        } catch (ProviderHelper.NotFoundException expectedException) { -            // good -        } -    } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java deleted file mode 100644 index 1ab5878cc..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.sufficientlysecure.keychain.testsupport; - -import android.content.Context; - -import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; -import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.util.InputData; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * For functional tests of PgpDecryptVerify - */ -public class PgpVerifyTestingHelper { - -    private final Context context; - -    public PgpVerifyTestingHelper(Context robolectricContext) { -        this.context = robolectricContext; -    } - -    public int doTestFile(String testFileName) throws Exception { -        ProviderHelper providerHelper = new ProviderHelperStub(context); - -        PgpDecryptVerify.PassphraseCache passphraseCache = new PgpDecryptVerify.PassphraseCache() { -            public String getCachedPassphrase(long masterKeyId) { -                return "I am a passphrase"; -            } -        }; - -        byte[] sampleInputBytes = TestDataUtil.readFully(getClass().getResourceAsStream(testFileName)); - -        InputStream sampleInput = new ByteArrayInputStream(sampleInputBytes); - -        InputData data = new InputData(sampleInput, sampleInputBytes.length); -        OutputStream outStream = new ByteArrayOutputStream(); - -        PgpDecryptVerify verify = new PgpDecryptVerify.Builder(providerHelper, passphraseCache, data, outStream).build(); -        PgpDecryptVerifyResult result = verify.execute(); - -        return result.getSignatureResult().getStatus(); -    } - - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java deleted file mode 100644 index c6d834bf9..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.sufficientlysecure.keychain.testsupport; - -import android.content.Context; -import android.net.Uri; - -import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing; -import org.sufficientlysecure.keychain.provider.ProviderHelper; - -/** - * Created by art on 21/06/14. - */ -class ProviderHelperStub extends ProviderHelper { -    public ProviderHelperStub(Context context) { -        super(context); -    } - -    @Override -    public WrappedPublicKeyRing getWrappedPublicKeyRing(Uri id) throws NotFoundException { -        byte[] data = TestDataUtil.readFully(getClass().getResourceAsStream("/public-key-for-sample.blob")); -        return new WrappedPublicKeyRing(data, false, 0); -    } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java deleted file mode 100644 index 9e6ede761..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.sufficientlysecure.keychain.testsupport; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Collection; - -/** - * Misc support functions. Would just use Guava / Apache Commons but - * avoiding extra dependencies. - */ -public class TestDataUtil { -    public static byte[] readFully(InputStream input) { -        ByteArrayOutputStream output = new ByteArrayOutputStream(); -        appendToOutput(input, output); -        return output.toByteArray(); -    } - -    private static void appendToOutput(InputStream input, ByteArrayOutputStream output) { -        byte[] buffer = new byte[8192]; -        int bytesRead; -        try { -            while ((bytesRead = input.read(buffer)) != -1) { -                output.write(buffer, 0, bytesRead); -            } -        } catch (IOException e) { -            throw new RuntimeException(e); -        } -    } - -    public static byte[] readAllFully(Collection<String> inputResources) { -        ByteArrayOutputStream output = new ByteArrayOutputStream(); - -        for (String inputResource : inputResources) { -            appendToOutput(getResourceAsStream(inputResource), output); -        } -        return output.toByteArray(); -    } - - -    public static InputStream getResourceAsStream(String resourceName) { -        return TestDataUtil.class.getResourceAsStream(resourceName); -    } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java deleted file mode 100644 index 1cc0f9a95..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Test support classes. - * This is only in main code because of gradle-Android Studio-robolectric issues. Having - * classes in main code means IDE autocomplete, class detection, etc., all works. - * TODO Move into test package when possible - */ -package org.sufficientlysecure.keychain.testsupport; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java index 7ac229b91..3c69fb071 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java @@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui;  import android.app.ProgressDialog;  import android.content.Intent;  import android.database.Cursor; +import android.graphics.PorterDuff;  import android.net.Uri;  import android.os.Bundle;  import android.os.Handler; @@ -37,12 +38,11 @@ import android.widget.ArrayAdapter;  import android.widget.CheckBox;  import android.widget.CompoundButton;  import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.ImageView;  import android.widget.ListView;  import android.widget.Spinner;  import android.widget.TextView; -import com.devspark.appmsg.AppMsg; -  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.helper.Preferences; @@ -52,10 +52,12 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;  import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;  import org.sufficientlysecure.keychain.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.OperationResultParcel;  import org.sufficientlysecure.keychain.service.PassphraseCacheService;  import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;  import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;  import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Notify;  import java.util.ArrayList; @@ -64,7 +66,8 @@ import java.util.ArrayList;   */  public class CertifyKeyActivity extends ActionBarActivity implements          SelectSecretKeyLayoutFragment.SelectSecretKeyCallback, LoaderManager.LoaderCallbacks<Cursor> { -    private View mSignButton; +    private View mCertifyButton; +    private ImageView mActionCertifyImage;      private CheckBox mUploadKeyCheckbox;      private Spinner mSelectKeyserverSpinner; @@ -88,10 +91,19 @@ public class CertifyKeyActivity extends ActionBarActivity implements          mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getSupportFragmentManager()                  .findFragmentById(R.id.sign_key_select_key_fragment); +        mSelectKeyserverSpinner = (Spinner) findViewById(R.id.upload_key_keyserver); +        mUploadKeyCheckbox = (CheckBox) findViewById(R.id.sign_key_upload_checkbox); +        mCertifyButton = findViewById(R.id.certify_key_certify_button); +        mActionCertifyImage = (ImageView) findViewById(R.id.certify_key_action_certify_image); +        mUserIds = (ListView) findViewById(R.id.view_key_user_ids); + +        // make certify image gray, like action icons +        mActionCertifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), +                PorterDuff.Mode.SRC_IN); +          mSelectKeyFragment.setCallback(this);          mSelectKeyFragment.setFilterCertify(true); -        mSelectKeyserverSpinner = (Spinner) findViewById(R.id.upload_key_keyserver);          ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,                  android.R.layout.simple_spinner_item, Preferences.getPreferences(this)                  .getKeyServers() @@ -99,7 +111,6 @@ public class CertifyKeyActivity extends ActionBarActivity implements          adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);          mSelectKeyserverSpinner.setAdapter(adapter); -        mUploadKeyCheckbox = (CheckBox) findViewById(R.id.sign_key_upload_checkbox);          if (!mUploadKeyCheckbox.isChecked()) {              mSelectKeyserverSpinner.setEnabled(false);          } else { @@ -118,14 +129,15 @@ public class CertifyKeyActivity extends ActionBarActivity implements              }          }); -        mSignButton = findViewById(R.id.sign_key_sign_button); -        mSignButton.setOnClickListener(new OnClickListener() { +        mCertifyButton.setOnClickListener(new OnClickListener() {              @Override              public void onClick(View v) {                  if (mPubKeyId != 0) {                      if (mMasterKeyId == 0) {                          mSelectKeyFragment.setError(getString(R.string.select_key_to_certify)); +                        Notify.showNotify(CertifyKeyActivity.this, getString(R.string.select_key_to_certify), +                                Notify.Style.ERROR);                      } else {                          initiateSigning();                      } @@ -141,7 +153,6 @@ public class CertifyKeyActivity extends ActionBarActivity implements          }          Log.e(Constants.TAG, "uri: " + mDataUri); -        mUserIds = (ListView) findViewById(R.id.view_key_user_ids);          mUserIdsAdapter = new UserIdsAdapter(this, null, 0, true);          mUserIds.setAdapter(mUserIdsAdapter); @@ -230,7 +241,8 @@ public class CertifyKeyActivity extends ActionBarActivity implements                                  startSigning();                              }                          } -                    }); +                    } +            );              // bail out; need to wait until the user has entered the passphrase before trying again              return;          } else { @@ -246,8 +258,8 @@ public class CertifyKeyActivity extends ActionBarActivity implements          // Bail out if there is not at least one user id selected          ArrayList<String> userIds = mUserIdsAdapter.getSelectedUserIds();          if (userIds.isEmpty()) { -            AppMsg.makeText(CertifyKeyActivity.this, "No User IDs to sign selected!", -                    AppMsg.STYLE_ALERT).show(); +            Notify.showNotify(CertifyKeyActivity.this, "No identities selected!", +                    Notify.Style.ERROR);              return;          } @@ -274,8 +286,8 @@ public class CertifyKeyActivity extends ActionBarActivity implements                  if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { -                    AppMsg.makeText(CertifyKeyActivity.this, R.string.key_certify_success, -                            AppMsg.STYLE_INFO).show(); +                    Notify.showNotify(CertifyKeyActivity.this, R.string.key_certify_success, +                            Notify.Style.INFO);                      // check if we need to send the key to the server or not                      if (mUploadKeyCheckbox.isChecked()) { @@ -327,8 +339,10 @@ public class CertifyKeyActivity extends ActionBarActivity implements                  super.handleMessage(message);                  if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { -                    AppMsg.makeText(CertifyKeyActivity.this, R.string.key_send_success, -                            AppMsg.STYLE_INFO).show(); +                    Intent intent = new Intent(); +                    intent.putExtra(OperationResultParcel.EXTRA_RESULT, message.getData()); +                    Notify.showNotify(CertifyKeyActivity.this, R.string.key_send_success, +                            Notify.Style.INFO);                      setResult(RESULT_OK);                      finish(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java new file mode 100644 index 000000000..534ac5811 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java @@ -0,0 +1,85 @@ +/* + * 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.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.ActionBarActivity; + +import org.sufficientlysecure.keychain.R; + +public class CreateKeyActivity extends ActionBarActivity { + +    public static final String EXTRA_NAME = "name"; +    public static final String EXTRA_EMAIL = "email"; + +    public static final int FRAG_ACTION_START = 0; +    public static final int FRAG_ACTION_TO_RIGHT = 1; +    public static final int FRAG_ACTION_TO_LEFT = 2; + +    @Override +    public void onCreate(Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); + +        setContentView(R.layout.create_key_activity); + +        // pass extras into fragment +        CreateKeyInputFragment frag = +                CreateKeyInputFragment.newInstance( +                        getIntent().getStringExtra(EXTRA_NAME), +                        getIntent().getStringExtra(EXTRA_EMAIL) +                ); +        loadFragment(null, frag, FRAG_ACTION_START); +    } + +    public void loadFragment(Bundle savedInstanceState, Fragment fragment, int 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; +        } + +        // Add the fragment to the 'fragment_container' FrameLayout +        // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! +        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + +        switch (action) { +            case FRAG_ACTION_START: +                transaction.setCustomAnimations(0, 0); +                transaction.replace(R.id.create_key_fragment_container, fragment) +                        .commitAllowingStateLoss(); +                break; +            case FRAG_ACTION_TO_LEFT: +                getSupportFragmentManager().popBackStackImmediate(); +                break; +            case FRAG_ACTION_TO_RIGHT: +                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(); +                break; + +        } +        // do it immediately! +        getSupportFragmentManager().executePendingTransactions(); +    } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java new file mode 100644 index 000000000..b64480087 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.ui; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.TextView; + +import org.spongycastle.bcpg.sig.KeyFlags; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.service.OperationResults; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.util.Notify; + +public class CreateKeyFinalFragment extends Fragment { + +    CreateKeyActivity mCreateKeyActivity; + +    TextView mNameEdit; +    TextView mEmailEdit; +    CheckBox mUploadCheckbox; +    View mBackButton; +    View mCreateButton; + +    public static final String ARG_NAME = "name"; +    public static final String ARG_EMAIL = "email"; +    public static final String ARG_PASSPHRASE = "passphrase"; + +    String mName; +    String mEmail; +    String mPassphrase; + +    /** +     * Creates new instance of this fragment +     */ +    public static CreateKeyFinalFragment newInstance(String name, String email, String passphrase) { +        CreateKeyFinalFragment frag = new CreateKeyFinalFragment(); + +        Bundle args = new Bundle(); +        args.putString(ARG_NAME, name); +        args.putString(ARG_EMAIL, email); +        args.putString(ARG_PASSPHRASE, passphrase); + +        frag.setArguments(args); + +        return frag; +    } + +    @Override +    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { +        View view = inflater.inflate(R.layout.create_key_final_fragment, container, false); + +        mNameEdit = (TextView) view.findViewById(R.id.name); +        mEmailEdit = (TextView) view.findViewById(R.id.email); +        mUploadCheckbox = (CheckBox) view.findViewById(R.id.create_key_upload); +        mBackButton = view.findViewById(R.id.create_key_back_button); +        mCreateButton = view.findViewById(R.id.create_key_create_button); + +        // get args +        mName = getArguments().getString(ARG_NAME); +        mEmail = getArguments().getString(ARG_EMAIL); +        mPassphrase = getArguments().getString(ARG_PASSPHRASE); + +        // set values +        mNameEdit.setText(mName); +        mEmailEdit.setText(mEmail); + +        mCreateButton.setOnClickListener(new View.OnClickListener() { +            @Override +            public void onClick(View v) { +                createKey(); +            } +        }); + +        mBackButton.setOnClickListener(new View.OnClickListener() { +            @Override +            public void onClick(View v) { +                mCreateKeyActivity.loadFragment(null, null, CreateKeyActivity.FRAG_ACTION_TO_LEFT); +            } +        }); + +        return view; +    } + +    @Override +    public void onActivityCreated(Bundle savedInstanceState) { +        super.onActivityCreated(savedInstanceState); + +        mCreateKeyActivity = (CreateKeyActivity) getActivity(); +    } + +    private void createKey() { +        Intent intent = new Intent(getActivity(), KeychainIntentService.class); +        intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING); + +        // Message is received after importing is done in KeychainIntentService +        KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( +                getActivity(), +                getString(R.string.progress_importing), +                ProgressDialog.STYLE_HORIZONTAL) { +            public void handleMessage(Message message) { +                // handle messages by standard KeychainIntentServiceHandler first +                super.handleMessage(message); + +                if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { +                    // get returned data bundle +                    Bundle returnData = message.getData(); +                    if (returnData == null) { +                        return; +                    } +                    final OperationResults.EditKeyResult result = +                            returnData.getParcelable(OperationResultParcel.EXTRA_RESULT); +                    if (result == null) { +                        return; +                    } + +                    if (mUploadCheckbox.isChecked()) { +                        if (result.getResult() == OperationResultParcel.RESULT_OK) { +                            // result will be displayed after upload +                            uploadKey(result); +                        } else { +                            // display result on error without finishing activity +                            result.createNotify(getActivity()); +                        } +                    } else { +                        // TODO: return result +                        result.createNotify(getActivity()); + +                        getActivity().setResult(Activity.RESULT_OK); +                        getActivity().finish(); +                    } +                } +            } +        }; + +        // fill values for this action +        Bundle data = new Bundle(); + +        SaveKeyringParcel parcel = new SaveKeyringParcel(); +        parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.CERTIFY_OTHER, null)); +        parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.SIGN_DATA, null)); +        parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, null)); +        String userId = mName + " <" + mEmail + ">"; +        parcel.mAddUserIds.add(userId); +        parcel.mNewPassphrase = mPassphrase; + +        // get selected key entries +        data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, parcel); + +        intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + +        // Create a new Messenger for the communication back +        Messenger messenger = new Messenger(saveHandler); +        intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + +        saveHandler.showProgressDialog(getActivity()); + +        getActivity().startService(intent); +    } + +    private void uploadKey(final OperationResults.EditKeyResult editKeyResult) { +        // Send all information needed to service to upload key in other thread +        final Intent intent = new Intent(getActivity(), KeychainIntentService.class); + +        intent.setAction(KeychainIntentService.ACTION_UPLOAD_KEYRING); + +        // set data uri as path to keyring +        Uri blobUri = KeychainContract.KeyRingData.buildPublicKeyRingUri( +                Long.toString(editKeyResult.mRingMasterKeyId) +        ); +        intent.setData(blobUri); + +        // fill values for this action +        Bundle data = new Bundle(); + +        // upload to favorite keyserver +        String keyserver = Preferences.getPreferences(getActivity()).getKeyServers()[0]; +        data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, keyserver); + +        intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + +        // Message is received after uploading is done in KeychainIntentService +        KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(), +                getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) { +            public void handleMessage(Message message) { +                // handle messages by standard KeychainIntentServiceHandler first +                super.handleMessage(message); + +                if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { +                    // TODO: return results + +                    editKeyResult.createNotify(getActivity()); + +                    Notify.showNotify(getActivity(), R.string.key_send_success, +                            Notify.Style.INFO); + +                    getActivity().setResult(Activity.RESULT_OK); +                    getActivity().finish(); +                } +            } +        }; + +        // Create a new Messenger for the communication back +        Messenger messenger = new Messenger(saveHandler); +        intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + +        // show progress dialog +        saveHandler.showProgressDialog(getActivity()); + +        // start service with intent +        getActivity().startService(intent); +    } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java new file mode 100644 index 000000000..ef5a3b145 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java @@ -0,0 +1,214 @@ +/* + * 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.Context; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Patterns; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; +import android.widget.EditText; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ContactHelper; + +import java.util.regex.Matcher; + +public class CreateKeyInputFragment extends Fragment { + +    CreateKeyActivity mCreateKeyActivity; + +    AutoCompleteTextView mNameEdit; +    AutoCompleteTextView mEmailEdit; +    EditText mPassphraseEdit; +    EditText mPassphraseEditAgain; +    View mCreateButton; + +    public static final String ARG_NAME = "name"; +    public static final String ARG_EMAIL = "email"; + +    /** +     * Creates new instance of this fragment +     */ +    public static CreateKeyInputFragment newInstance(String name, String email) { +        CreateKeyInputFragment frag = new CreateKeyInputFragment(); + +        Bundle args = new Bundle(); +        args.putString(ARG_NAME, name); +        args.putString(ARG_EMAIL, email); + +        frag.setArguments(args); + +        return frag; +    } + +    @Override +    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { +        View view = inflater.inflate(R.layout.create_key_input_fragment, container, false); + +        mNameEdit = (AutoCompleteTextView) view.findViewById(R.id.name); +        mEmailEdit = (AutoCompleteTextView) view.findViewById(R.id.email); +        mPassphraseEdit = (EditText) view.findViewById(R.id.passphrase); +        mPassphraseEditAgain = (EditText) view.findViewById(R.id.passphrase_again); +        mCreateButton = view.findViewById(R.id.create_key_button); + +        // initial values +        String name = getArguments().getString(ARG_NAME); +        String email = getArguments().getString(ARG_EMAIL); +        mNameEdit.setText(name); +        mEmailEdit.setText(email); + +        // focus non-empty edit fields +        if (name != null && email != null) { +            mPassphraseEdit.requestFocus(); +        } else if (name != null) { +            mEmailEdit.requestFocus(); +        } + +        mEmailEdit.setThreshold(1); // Start working from first character +        mEmailEdit.setAdapter( +                new ArrayAdapter<String> +                        (getActivity(), android.R.layout.simple_spinner_dropdown_item, +                                ContactHelper.getPossibleUserEmails(getActivity()) +                        ) +        ); +        mEmailEdit.addTextChangedListener(new TextWatcher() { +            @Override +            public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { +            } + +            @Override +            public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { +            } + +            @Override +            public void afterTextChanged(Editable editable) { +                String email = editable.toString(); +                if (email.length() > 0) { +                    Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email); +                    if (emailMatcher.matches()) { +                        mEmailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0, +                                R.drawable.uid_mail_ok, 0); +                    } else { +                        mEmailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0, +                                R.drawable.uid_mail_bad, 0); +                    } +                } else { +                    // remove drawable if email is empty +                    mEmailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); +                } +            } +        }); + +        mNameEdit.setThreshold(1); // Start working from first character +        mNameEdit.setAdapter( +                new ArrayAdapter<String> +                        (getActivity(), android.R.layout.simple_spinner_dropdown_item, +                                ContactHelper.getPossibleUserNames(getActivity()) +                        ) +        ); + +        mCreateButton.setOnClickListener(new View.OnClickListener() { +            @Override +            public void onClick(View v) { +                createKeyCheck(); +            } +        }); + +        return view; +    } + +    @Override +    public void onActivityCreated(Bundle savedInstanceState) { +        super.onActivityCreated(savedInstanceState); + +        mCreateKeyActivity = (CreateKeyActivity) getActivity(); +    } + +    private void createKeyCheck() { +        if (isEditTextNotEmpty(getActivity(), mNameEdit) +                && isEditTextNotEmpty(getActivity(), mEmailEdit) +                && isEditTextNotEmpty(getActivity(), mPassphraseEdit) +                && areEditTextsEqual(getActivity(), mPassphraseEdit, mPassphraseEditAgain)) { + +            CreateKeyFinalFragment frag = +                    CreateKeyFinalFragment.newInstance( +                            mNameEdit.getText().toString(), +                            mEmailEdit.getText().toString(), +                            mPassphraseEdit.getText().toString() +                    ); + +            hideKeyboard(); +            mCreateKeyActivity.loadFragment(null, frag, CreateKeyActivity.FRAG_ACTION_TO_RIGHT); +        } +    } + +    private void hideKeyboard() { +        InputMethodManager inputManager = (InputMethodManager) getActivity() +                .getSystemService(Context.INPUT_METHOD_SERVICE); + +        //check if no view has focus: +        View v = getActivity().getCurrentFocus(); +        if (v == null) +            return; + +        inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0); +    } + +    /** +     * Checks if text of given EditText is not empty. If it is empty an error is +     * set and the EditText gets the focus. +     * +     * @param context +     * @param editText +     * @return true if EditText is not empty +     */ +    private static boolean isEditTextNotEmpty(Context context, EditText editText) { +        boolean output = true; +        if (editText.getText().toString().length() == 0) { +            editText.setError(context.getString(R.string.create_key_empty)); +            editText.requestFocus(); +            output = false; +        } else { +            editText.setError(null); +        } + +        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; +    } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java index dd05537ef..5b61c3f52 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java @@ -28,6 +28,9 @@ import android.view.LayoutInflater;  import android.view.View;  import android.view.ViewGroup;  import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.ImageButton; +  import android.widget.TextView;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; @@ -107,7 +110,6 @@ public class DecryptFileFragment extends DecryptFragment {      private void decryptAction() {          if (mInputUri == null) { -            //AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show();              Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR);              return;          } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java index d450bdcca..16a7b911e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java @@ -132,7 +132,7 @@ public abstract class DecryptFragment extends Fragment {                          mResultText.setText(R.string.decrypt_result_decrypted_and_signature_certified);                      } -                    mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_green)); +                    mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_green_light));                      mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);                      mSignatureLayout.setVisibility(View.VISIBLE);                      mLookupKey.setVisibility(View.GONE); @@ -146,7 +146,7 @@ public abstract class DecryptFragment extends Fragment {                          mResultText.setText(R.string.decrypt_result_decrypted_and_signature_uncertified);                      } -                    mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_orange)); +                    mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_orange_light));                      mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);                      mSignatureLayout.setVisibility(View.VISIBLE);                      mLookupKey.setVisibility(View.GONE); @@ -160,7 +160,7 @@ public abstract class DecryptFragment extends Fragment {                          mResultText.setText(R.string.decrypt_result_decrypted_unknown_pub_key);                      } -                    mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_orange)); +                    mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_orange_light));                      mSignatureStatusImage.setImageResource(R.drawable.overlay_error);                      mSignatureLayout.setVisibility(View.VISIBLE);                      mLookupKey.setVisibility(View.VISIBLE); @@ -170,7 +170,7 @@ public abstract class DecryptFragment extends Fragment {                  case OpenPgpSignatureResult.SIGNATURE_ERROR: {                      mResultText.setText(R.string.decrypt_result_invalid_signature); -                    mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_red)); +                    mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_red_light));                      mSignatureStatusImage.setImageResource(R.drawable.overlay_error);                      mSignatureLayout.setVisibility(View.GONE);                      mLookupKey.setVisibility(View.GONE); @@ -180,7 +180,7 @@ public abstract class DecryptFragment extends Fragment {                  default: {                      mResultText.setText(R.string.error); -                    mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_red)); +                    mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_red_light));                      mSignatureStatusImage.setImageResource(R.drawable.overlay_error);                      mSignatureLayout.setVisibility(View.GONE);                      mLookupKey.setVisibility(View.GONE); @@ -192,7 +192,7 @@ public abstract class DecryptFragment extends Fragment {              mLookupKey.setVisibility(View.GONE);              // successful decryption-only -            mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_purple)); +            mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_purple_light));              mResultText.setText(R.string.decrypt_result_decrypted);          }      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java index 2db6c232c..cf7a0b4b8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java @@ -27,7 +27,7 @@ import android.view.View;  import android.view.View.OnClickListener;  import android.view.ViewGroup;  import android.widget.EditText; -import com.devspark.appmsg.AppMsg; +  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; @@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.pgp.PgpHelper;  import org.sufficientlysecure.keychain.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;  import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Notify;  import java.util.regex.Matcher; @@ -105,12 +106,10 @@ public class DecryptMessageFragment extends DecryptFragment {                  mCiphertext = matcher.group(1);                  decryptStart(null);              } else { -                AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_ALERT) -                        .show(); +                Notify.showNotify(getActivity(), R.string.error_invalid_data, Notify.Style.ERROR);              }          } else { -            AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_ALERT) -                    .show(); +            Notify.showNotify(getActivity(), R.string.error_invalid_data, Notify.Style.ERROR);          }      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index d80425c3c..6ddaec17f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -33,7 +33,7 @@ public class EditKeyActivity extends ActionBarActivity {      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState); -        setContentView(R.layout.edit_key_activity_new); +        setContentView(R.layout.edit_key_activity);          Uri dataUri = getIntent().getData();          if (dataUri == null) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityOld.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityOld.java deleted file mode 100644 index 51457cd45..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityOld.java +++ /dev/null @@ -1,744 +0,0 @@ -/* - * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> - * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program.  If not, see <http://www.gnu.org/licenses/>. - */ - -package org.sufficientlysecure.keychain.ui; - -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import android.support.v4.app.ActivityCompat; -import android.support.v7.app.ActionBarActivity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; -import android.widget.LinearLayout; -import android.widget.Toast; - -import com.devspark.appmsg.AppMsg; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.ActionBarHelper; -import org.sufficientlysecure.keychain.helper.ExportHelper; -import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.pgp.UncachedSecretKey; -import org.sufficientlysecure.keychain.pgp.WrappedSecretKey; -import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing; -import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.service.PassphraseCacheService; -import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder; -import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; -import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; -import org.sufficientlysecure.keychain.ui.widget.Editor; -import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener; -import org.sufficientlysecure.keychain.ui.widget.KeyEditor; -import org.sufficientlysecure.keychain.ui.widget.SectionView; -import org.sufficientlysecure.keychain.ui.widget.UserIdEditor; -import org.sufficientlysecure.keychain.util.Log; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; -import java.util.Vector; - -public class EditKeyActivityOld extends ActionBarActivity implements EditorListener { - -    // Actions for internal use only: -    public static final String ACTION_CREATE_KEY = Constants.INTENT_PREFIX + "CREATE_KEY"; -    public static final String ACTION_EDIT_KEY = Constants.INTENT_PREFIX + "EDIT_KEY"; - -    // possible extra keys -    public static final String EXTRA_USER_IDS = "user_ids"; -    public static final String EXTRA_NO_PASSPHRASE = "no_passphrase"; -    public static final String EXTRA_GENERATE_DEFAULT_KEYS = "generate_default_keys"; - -    // EDIT -    private Uri mDataUri; - -    private SectionView mUserIdsView; -    private SectionView mKeysView; - -    private String mCurrentPassphrase = null; -    private String mNewPassphrase = null; -    private String mSavedNewPassphrase = null; -    private boolean mIsPassphraseSet; -    private boolean mNeedsSaving; -    private boolean mIsBrandNewKeyring = false; - -    private Button mChangePassphrase; - -    private CheckBox mNoPassphrase; - -    Vector<String> mUserIds; -    Vector<UncachedSecretKey> mKeys; -    Vector<Integer> mKeysUsages; -    boolean mMasterCanSign = true; - -    ExportHelper mExportHelper; - -    public boolean needsSaving() { -        mNeedsSaving = (mUserIdsView == null) ? false : mUserIdsView.needsSaving(); -        mNeedsSaving |= (mKeysView == null) ? false : mKeysView.needsSaving(); -        mNeedsSaving |= hasPassphraseChanged(); -        mNeedsSaving |= mIsBrandNewKeyring; -        return mNeedsSaving; -    } - - -    public void somethingChanged() { -        ActivityCompat.invalidateOptionsMenu(this); -    } - -    public void onDeleted(Editor e, boolean wasNewItem) { -        somethingChanged(); -    } - -    public void onEdited() { -        somethingChanged(); -    } - -    @Override -    public void onCreate(Bundle savedInstanceState) { -        super.onCreate(savedInstanceState); - -        mExportHelper = new ExportHelper(this); - -        // Inflate a "Done"/"Cancel" custom action bar view -        ActionBarHelper.setTwoButtonView(getSupportActionBar(), -                R.string.btn_save, R.drawable.ic_action_save, -                new View.OnClickListener() { -                    @Override -                    public void onClick(View v) { -                        // Save -                        saveClicked(); -                    } -                }, R.string.menu_key_edit_cancel, R.drawable.ic_action_cancel, -                new View.OnClickListener() { -                    @Override -                    public void onClick(View v) { -                        // Cancel -                        cancelClicked(); -                    } -                } -        ); - -        mUserIds = new Vector<String>(); -        mKeys = new Vector<UncachedSecretKey>(); -        mKeysUsages = new Vector<Integer>(); - -        // Catch Intents opened from other apps -        Intent intent = getIntent(); -        String action = intent.getAction(); -        if (ACTION_CREATE_KEY.equals(action)) { -            handleActionCreateKey(intent); -        } else if (ACTION_EDIT_KEY.equals(action)) { -            handleActionEditKey(intent); -        } -    } - -    /** -     * Handle intent action to create new key -     * -     * @param intent -     */ -    private void handleActionCreateKey(Intent intent) { -        Bundle extras = intent.getExtras(); - -        mCurrentPassphrase = ""; -        mIsBrandNewKeyring = true; - -        if (extras != null) { -            // if userId is given, prefill the fields -            if (extras.containsKey(EXTRA_USER_IDS)) { -                Log.d(Constants.TAG, "UserIds are given!"); -                mUserIds.add(extras.getString(EXTRA_USER_IDS)); -            } - -            // if no passphrase is given -            if (extras.containsKey(EXTRA_NO_PASSPHRASE)) { -                boolean noPassphrase = extras.getBoolean(EXTRA_NO_PASSPHRASE); -                if (noPassphrase) { -                    // check "no passphrase" checkbox and remove button -                    mNoPassphrase.setChecked(true); -                    mChangePassphrase.setVisibility(View.GONE); -                } -            } - -            // generate key -            if (extras.containsKey(EXTRA_GENERATE_DEFAULT_KEYS)) { -                /* -                boolean generateDefaultKeys = extras.getBoolean(EXTRA_GENERATE_DEFAULT_KEYS); -                if (generateDefaultKeys) { - -                    // fill values for this action -                    Bundle data = new Bundle(); -                    data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, -                            mCurrentPassphrase); - -                    serviceIntent.putExtra(KeychainIntentService.EXTRA_DATA, data); - -                    // Message is received after generating is done in KeychainIntentService -                    KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( -                            this, getResources().getQuantityString(R.plurals.progress_generating, 1), -                            ProgressDialog.STYLE_HORIZONTAL, true, - -                            new DialogInterface.OnCancelListener() { -                                @Override -                                public void onCancel(DialogInterface dialog) { -                                    // Stop key generation on cancel -                                    stopService(serviceIntent); -                                    EditKeyActivity.this.setResult(Activity.RESULT_CANCELED); -                                    EditKeyActivity.this.finish(); -                                } -                            }) { - -                        @Override -                        public void handleMessage(Message message) { -                            // handle messages by standard KeychainIntentServiceHandler first -                            super.handleMessage(message); - -                            if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { -                                // get new key from data bundle returned from service -                                Bundle data = message.getDataAsStringList(); - -                                ArrayList<UncachedSecretKey> newKeys = -                                        PgpConversionHelper.BytesToPGPSecretKeyList(data -                                                .getByteArray(KeychainIntentService.RESULT_NEW_KEY)); - -                                ArrayList<Integer> keyUsageFlags = data.getIntegerArrayList( -                                        KeychainIntentService.RESULT_KEY_USAGES); - -                                if (newKeys.size() == keyUsageFlags.size()) { -                                    for (int i = 0; i < newKeys.size(); ++i) { -                                        mKeys.add(newKeys.get(i)); -                                        mKeysUsages.add(keyUsageFlags.get(i)); -                                    } -                                } - -                                buildLayout(true); -                            } -                        } -                    }; - -                    // Create a new Messenger for the communication back -                    Messenger messenger = new Messenger(saveHandler); -                    serviceIntent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - -                    saveHandler.showProgressDialog(this); - -                    // start service with intent -                    startService(serviceIntent); -                } -                */ -            } -        } else { -            buildLayout(false); -        } -    } - -    /** -     * Handle intent action to edit existing key -     * -     * @param intent -     */ -    private void handleActionEditKey(Intent intent) { -        mDataUri = intent.getData(); -        if (mDataUri == null) { -            Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!"); -            finish(); -        } else { -            Log.d(Constants.TAG, "uri: " + mDataUri); - -            try { -                Uri secretUri = KeyRings.buildUnifiedKeyRingUri(mDataUri); -                WrappedSecretKeyRing keyRing = new ProviderHelper(this).getWrappedSecretKeyRing(secretUri); - -                mMasterCanSign = keyRing.getSubKey().canCertify(); -                for (WrappedSecretKey key : keyRing.secretKeyIterator()) { -                    // Turn into uncached instance -                    mKeys.add(key.getUncached()); -                    mKeysUsages.add(key.getKeyUsage()); // get usage when view is created -                } - -                boolean isSet = false; -                for (String userId : keyRing.getSubKey().getUserIds()) { -                    Log.d(Constants.TAG, "Added userId " + userId); -                    if (!isSet) { -                        isSet = true; -                        String[] parts = KeyRing.splitUserId(userId); -                        if (parts[0] != null) { -                            setTitle(parts[0]); -                        } -                    } -                    mUserIds.add(userId); -                } - -                buildLayout(false); - -                mCurrentPassphrase = ""; -                mIsPassphraseSet = keyRing.hasPassphrase(); -                if (!mIsPassphraseSet) { -                    // check "no passphrase" checkbox and remove button -                    mNoPassphrase.setChecked(true); -                    mChangePassphrase.setVisibility(View.GONE); -                } - -            } catch (ProviderHelper.NotFoundException e) { -                Log.e(Constants.TAG, "Keyring not found: " + e.getMessage(), e); -                Toast.makeText(this, R.string.error_no_secret_key_found, Toast.LENGTH_SHORT).show(); -                finish(); -            } - -        } -    } - -    /** -     * Shows the dialog to set a new passphrase -     */ -    private void showSetPassphraseDialog() { -        // Message is received after passphrase is cached -        Handler returnHandler = new Handler() { -            @Override -            public void handleMessage(Message message) { -                if (message.what == SetPassphraseDialogFragment.MESSAGE_OKAY) { -                    Bundle data = message.getData(); - -                    // set new returned passphrase! -                    mNewPassphrase = data -                            .getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE); - -                    updatePassphraseButtonText(); -                    somethingChanged(); -                } -            } -        }; - -        // Create a new Messenger for the communication back -        Messenger messenger = new Messenger(returnHandler); - -        // set title based on isPassphraseSet() -        int title; -        if (isPassphraseSet()) { -            title = R.string.title_change_passphrase; -        } else { -            title = R.string.title_set_passphrase; -        } - -        SetPassphraseDialogFragment setPassphraseDialog = SetPassphraseDialogFragment.newInstance( -                messenger, null, title); - -        setPassphraseDialog.show(getSupportFragmentManager(), "setPassphraseDialog"); -    } - -    /** -     * Build layout based on mUserId, mKeys and mKeysUsages Vectors. It creates Views for every user -     * id and key. -     * -     * @param newKeys -     */ -    private void buildLayout(boolean newKeys) { -        setContentView(R.layout.edit_key_activity); - -        // find views -        mChangePassphrase = (Button) findViewById(R.id.edit_key_btn_change_passphrase); -        mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase); -        // Build layout based on given userIds and keys - -        LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); - -        LinearLayout container = (LinearLayout) findViewById(R.id.edit_key_container); -        if (mIsPassphraseSet) { -            mChangePassphrase.setText(getString(R.string.btn_change_passphrase)); -        } -        mUserIdsView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); -        mUserIdsView.setType(SectionView.TYPE_USER_ID); -        mUserIdsView.setCanBeEdited(mMasterCanSign); -        mUserIdsView.setUserIds(mUserIds); -        mUserIdsView.setEditorListener(this); -        container.addView(mUserIdsView); -        mKeysView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); -        mKeysView.setType(SectionView.TYPE_KEY); -        mKeysView.setCanBeEdited(mMasterCanSign); -        mKeysView.setKeys(mKeys, mKeysUsages, newKeys); -        mKeysView.setEditorListener(this); -        container.addView(mKeysView); - -        updatePassphraseButtonText(); - -        mChangePassphrase.setOnClickListener(new OnClickListener() { -            public void onClick(View v) { -                showSetPassphraseDialog(); -            } -        }); - -        // disable passphrase when no passphrase checkbox is checked! -        mNoPassphrase.setOnCheckedChangeListener(new OnCheckedChangeListener() { - -            @Override -            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { -                if (isChecked) { -                    // remove passphrase -                    mSavedNewPassphrase = mNewPassphrase; -                    mNewPassphrase = ""; -                    mChangePassphrase.setVisibility(View.GONE); -                } else { -                    mNewPassphrase = mSavedNewPassphrase; -                    mChangePassphrase.setVisibility(View.VISIBLE); -                } -                somethingChanged(); -            } -        }); -    } - -    private long getMasterKeyId() { -        if (mKeysView.getEditors().getChildCount() == 0) { -            return 0; -        } -        return ((KeyEditor) mKeysView.getEditors().getChildAt(0)).getValue().getKeyId(); -    } - -    public boolean isPassphraseSet() { -        if (mNoPassphrase.isChecked()) { -            return true; -        } else if ((mIsPassphraseSet) -                || (mNewPassphrase != null && !mNewPassphrase.equals(""))) { -            return true; -        } else { -            return false; -        } -    } - -    public boolean hasPassphraseChanged() { -        if (mNoPassphrase != null) { -            if (mNoPassphrase.isChecked()) { -                return mIsPassphraseSet; -            } else { -                return (mNewPassphrase != null && !mNewPassphrase.equals("")); -            } -        } else { -            return false; -        } -    } - -    private void saveClicked() { -        final long masterKeyId = getMasterKeyId(); -        if (needsSaving()) { //make sure, as some versions don't support invalidateOptionsMenu -            try { -                if (!isPassphraseSet()) { -                    throw new PgpGeneralException(this.getString(R.string.set_a_passphrase)); -                } - -                String passphrase; -                if (mIsPassphraseSet) { -                    passphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId); -                } else { -                    passphrase = ""; -                } -                if (passphrase == null) { -                    PassphraseDialogFragment.show(this, masterKeyId, -                            new Handler() { -                                @Override -                                public void handleMessage(Message message) { -                                    if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { -                                        mCurrentPassphrase = PassphraseCacheService.getCachedPassphrase( -                                                EditKeyActivityOld.this, masterKeyId); -                                        checkEmptyIDsWanted(); -                                    } -                                } -                            }); -                } else { -                    mCurrentPassphrase = passphrase; -                    checkEmptyIDsWanted(); -                } -            } catch (PgpGeneralException e) { -                AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()), -                        AppMsg.STYLE_ALERT).show(); -            } -        } else { -            AppMsg.makeText(this, R.string.error_change_something_first, AppMsg.STYLE_ALERT).show(); -        } -    } - -    private void checkEmptyIDsWanted() { -        try { -            ArrayList<String> userIDs = getUserIds(mUserIdsView); -            List<Boolean> newIDs = mUserIdsView.getNewIDFlags(); -            ArrayList<String> originalIDs = mUserIdsView.getOriginalIDs(); -            int curID = 0; -            for (String userID : userIDs) { -                if (userID.equals("") && (!userID.equals(originalIDs.get(curID)) || newIDs.get(curID))) { -                    CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder( -                            EditKeyActivityOld.this); - -                    alert.setIcon(R.drawable.ic_dialog_alert_holo_light); -                    alert.setTitle(R.string.warning); -                    alert.setMessage(EditKeyActivityOld.this.getString(R.string.ask_empty_id_ok)); - -                    alert.setPositiveButton(EditKeyActivityOld.this.getString(android.R.string.yes), -                            new DialogInterface.OnClickListener() { -                                public void onClick(DialogInterface dialog, int id) { -                                    dialog.dismiss(); -                                    finallySaveClicked(); -                                } -                            } -                    ); -                    alert.setNegativeButton(this.getString(android.R.string.no), -                            new DialogInterface.OnClickListener() { -                                public void onClick(DialogInterface dialog, int id) { -                                    dialog.dismiss(); -                                } -                            } -                    ); -                    alert.setCancelable(false); -                    alert.show(); -                    return; -                } -                curID++; -            } -        } catch (PgpGeneralException e) { -            Log.e(Constants.TAG, getString(R.string.error_message, e.getMessage())); -            AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()), AppMsg.STYLE_ALERT).show(); -        } -        finallySaveClicked(); -    } - -    private boolean[] toPrimitiveArray(final List<Boolean> booleanList) { -        final boolean[] primitives = new boolean[booleanList.size()]; -        int index = 0; -        for (Boolean object : booleanList) { -            primitives[index++] = object; -        } -        return primitives; -    } - -    private void finallySaveClicked() { -            /* -        try { -            // Send all information needed to service to edit key in other thread -            Intent intent = new Intent(this, KeychainIntentService.class); - -            intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING); - -            OldSaveKeyringParcel saveParams = new OldSaveKeyringParcel(); -            saveParams.userIds = getUserIds(mUserIdsView); -            saveParams.originalIDs = mUserIdsView.getOriginalIDs(); -            saveParams.deletedIDs = mUserIdsView.getDeletedIDs(); -            saveParams.newIDs = toPrimitiveArray(mUserIdsView.getNewIDFlags()); -            saveParams.primaryIDChanged = mUserIdsView.primaryChanged(); -            saveParams.moddedKeys = toPrimitiveArray(mKeysView.getNeedsSavingArray()); -            saveParams.deletedKeys = mKeysView.getDeletedKeys(); -            saveParams.keysExpiryDates = getKeysExpiryDates(mKeysView); -            saveParams.keysUsages = getKeysUsages(mKeysView); -            saveParams.newPassphrase = mNewPassphrase; -            saveParams.oldPassphrase = mCurrentPassphrase; -            saveParams.newKeys = toPrimitiveArray(mKeysView.getNewKeysArray()); -            saveParams.keys = getKeys(mKeysView); -            saveParams.originalPrimaryID = mUserIdsView.getOriginalPrimaryID(); - -            // fill values for this action -            Bundle data = new Bundle(); -            data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, mMasterCanSign); -            data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, saveParams); - -            intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - -            // Message is received after saving is done in KeychainIntentService -            KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, -                    getString(R.string.progress_saving), ProgressDialog.STYLE_HORIZONTAL) { -                public void handleMessage(Message message) { -                    // handle messages by standard KeychainIntentServiceHandler first -                    super.handleMessage(message); - -                    if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { -                        Intent data = new Intent(); - -                        // return uri pointing to new created key -                        Uri uri = KeyRings.buildGenericKeyRingUri(getMasterKeyId()); -                        data.setData(uri); - -                        setResult(RESULT_OK, data); -                        finish(); -                    } -                } -            }; - -            // Create a new Messenger for the communication back -            Messenger messenger = new Messenger(saveHandler); -            intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - -            saveHandler.showProgressDialog(this); - -            // start service with intent -            startService(intent); -        } catch (PgpGeneralException e) { -            Log.e(Constants.TAG, getString(R.string.error_message, e.getMessage())); -            AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()), -                    AppMsg.STYLE_ALERT).show(); -        } -            */ -    } - -    private void cancelClicked() { -        if (needsSaving()) { //ask if we want to save -            CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder( -                    EditKeyActivityOld.this); - -            alert.setIcon(R.drawable.ic_dialog_alert_holo_light); -            alert.setTitle(R.string.warning); -            alert.setMessage(EditKeyActivityOld.this.getString(R.string.ask_save_changed_key)); - -            alert.setPositiveButton(EditKeyActivityOld.this.getString(android.R.string.yes), -                    new DialogInterface.OnClickListener() { -                        public void onClick(DialogInterface dialog, int id) { -                            dialog.dismiss(); -                            saveClicked(); -                        } -                    }); -            alert.setNegativeButton(this.getString(android.R.string.no), -                    new DialogInterface.OnClickListener() { -                        public void onClick(DialogInterface dialog, int id) { -                            dialog.dismiss(); -                            setResult(RESULT_CANCELED); -                            finish(); -                        } -                    }); -            alert.setCancelable(false); -            alert.show(); -        } else { -            setResult(RESULT_CANCELED); -            finish(); -        } -    } - -    /** -     * Returns user ids from the SectionView -     * -     * @param userIdsView -     * @return -     */ -    private ArrayList<String> getUserIds(SectionView userIdsView) throws PgpGeneralException { -        ArrayList<String> userIds = new ArrayList<String>(); - -        ViewGroup userIdEditors = userIdsView.getEditors(); - -        boolean gotMainUserId = false; -        for (int i = 0; i < userIdEditors.getChildCount(); ++i) { -            UserIdEditor editor = (UserIdEditor) userIdEditors.getChildAt(i); -            String userId; -            userId = editor.getValue(); - -            if (editor.isMainUserId()) { -                userIds.add(0, userId); -                gotMainUserId = true; -            } else { -                userIds.add(userId); -            } -        } - -        if (userIds.size() == 0) { -            throw new PgpGeneralException(getString(R.string.error_key_needs_a_user_id)); -        } - -        if (!gotMainUserId) { -            throw new PgpGeneralException(getString(R.string.error_main_user_id_must_not_be_empty)); -        } - -        return userIds; -    } - -    /** -     * Returns keys from the SectionView -     * -     * @param keysView -     * @return -     */ -    private ArrayList<UncachedSecretKey> getKeys(SectionView keysView) throws PgpGeneralException { -        ArrayList<UncachedSecretKey> keys = new ArrayList<UncachedSecretKey>(); - -        ViewGroup keyEditors = keysView.getEditors(); - -        if (keyEditors.getChildCount() == 0) { -            throw new PgpGeneralException(getString(R.string.error_key_needs_master_key)); -        } - -        for (int i = 0; i < keyEditors.getChildCount(); ++i) { -            KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i); -            keys.add(editor.getValue()); -        } - -        return keys; -    } - -    /** -     * Returns usage selections of keys from the SectionView -     * -     * @param keysView -     * @return -     */ -    private ArrayList<Integer> getKeysUsages(SectionView keysView) throws PgpGeneralException { -        ArrayList<Integer> keysUsages = new ArrayList<Integer>(); - -        ViewGroup keyEditors = keysView.getEditors(); - -        if (keyEditors.getChildCount() == 0) { -            throw new PgpGeneralException(getString(R.string.error_key_needs_master_key)); -        } - -        for (int i = 0; i < keyEditors.getChildCount(); ++i) { -            KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i); -            keysUsages.add(editor.getUsage()); -        } - -        return keysUsages; -    } - -    private ArrayList<Calendar> getKeysExpiryDates(SectionView keysView) throws PgpGeneralException { -        ArrayList<Calendar> keysExpiryDates = new ArrayList<Calendar>(); - -        ViewGroup keyEditors = keysView.getEditors(); - -        if (keyEditors.getChildCount() == 0) { -            throw new PgpGeneralException(getString(R.string.error_key_needs_master_key)); -        } - -        for (int i = 0; i < keyEditors.getChildCount(); ++i) { -            KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i); -            keysExpiryDates.add(editor.getExpiryDate()); -        } - -        return keysExpiryDates; -    } - -    private void updatePassphraseButtonText() { -        mChangePassphrase.setText(isPassphraseSet() ? getString(R.string.btn_change_passphrase) -                : getString(R.string.btn_set_passphrase)); -    } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java index c76dc0164..9083d1567 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -42,24 +42,28 @@ import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;  import org.sufficientlysecure.keychain.helper.ActionBarHelper; -import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;  import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;  import org.sufficientlysecure.keychain.service.OperationResults; +import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;  import org.sufficientlysecure.keychain.service.PassphraseCacheService;  import org.sufficientlysecure.keychain.service.SaveKeyringParcel;  import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter;  import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter;  import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;  import org.sufficientlysecure.keychain.ui.adapter.UserIdsAddedAdapter; +import org.sufficientlysecure.keychain.ui.dialog.ChangeExpiryDialogFragment; +import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment;  import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment;  import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;  import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;  import org.sufficientlysecure.keychain.util.Log;  import java.util.ArrayList; +import java.util.Date;  public class EditKeyFragment extends LoaderFragment implements          LoaderManager.LoaderCallbacks<Cursor> { @@ -164,8 +168,8 @@ public class EditKeyFragment extends LoaderFragment implements          try {              Uri secretUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri); -            WrappedSecretKeyRing keyRing = -                    new ProviderHelper(getActivity()).getWrappedSecretKeyRing(secretUri); +            CanonicalizedSecretKeyRing keyRing = +                    new ProviderHelper(getActivity()).getCanonicalizedSecretKeyRing(secretUri);              mSaveKeyringParcel = new SaveKeyringParcel(keyRing.getMasterKeyId(),                      keyRing.getUncachedKeyRing().getFingerprint()); @@ -214,10 +218,18 @@ public class EditKeyFragment extends LoaderFragment implements          mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mUserIdsAddedData);          mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter); -        mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0); +        mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0, mSaveKeyringParcel);          mSubkeysList.setAdapter(mSubkeysAdapter); -        mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSaveKeyringParcel.addSubKeys); +        mSubkeysList.setOnItemClickListener(new AdapterView.OnItemClickListener() { +            @Override +            public void onItemClick(AdapterView<?> parent, View view, int position, long id) { +                long keyId = mSubkeysAdapter.getKeyId(position); +                editSubkey(keyId); +            } +        }); + +        mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSaveKeyringParcel.mAddSubKeys);          mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter);          // Prepare the loaders. Either re-connect with an existing ones, @@ -287,7 +299,7 @@ public class EditKeyFragment extends LoaderFragment implements                      Bundle data = message.getData();                      // cache new returned passphrase! -                    mSaveKeyringParcel.newPassphrase = data +                    mSaveKeyringParcel.mNewPassphrase = data                              .getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE);                  }              } @@ -309,19 +321,19 @@ public class EditKeyFragment extends LoaderFragment implements                  switch (message.what) {                      case EditUserIdDialogFragment.MESSAGE_CHANGE_PRIMARY_USER_ID:                          // toggle -                        if (mSaveKeyringParcel.changePrimaryUserId != null -                                && mSaveKeyringParcel.changePrimaryUserId.equals(userId)) { -                            mSaveKeyringParcel.changePrimaryUserId = null; +                        if (mSaveKeyringParcel.mChangePrimaryUserId != null +                                && mSaveKeyringParcel.mChangePrimaryUserId.equals(userId)) { +                            mSaveKeyringParcel.mChangePrimaryUserId = null;                          } else { -                            mSaveKeyringParcel.changePrimaryUserId = userId; +                            mSaveKeyringParcel.mChangePrimaryUserId = userId;                          }                          break;                      case EditUserIdDialogFragment.MESSAGE_REVOKE:                          // toggle -                        if (mSaveKeyringParcel.revokeUserIds.contains(userId)) { -                            mSaveKeyringParcel.revokeUserIds.remove(userId); +                        if (mSaveKeyringParcel.mRevokeUserIds.contains(userId)) { +                            mSaveKeyringParcel.mRevokeUserIds.remove(userId);                          } else { -                            mSaveKeyringParcel.revokeUserIds.add(userId); +                            mSaveKeyringParcel.mRevokeUserIds.add(userId);                          }                          break;                  } @@ -342,6 +354,72 @@ public class EditKeyFragment extends LoaderFragment implements          });      } +    private void editSubkey(final long keyId) { +        Handler returnHandler = new Handler() { +            @Override +            public void handleMessage(Message message) { +                switch (message.what) { +                    case EditSubkeyDialogFragment.MESSAGE_CHANGE_EXPIRY: +                        editSubkeyExpiry(keyId); +                        break; +                    case EditSubkeyDialogFragment.MESSAGE_REVOKE: +                        // toggle +                        if (mSaveKeyringParcel.mRevokeSubKeys.contains(keyId)) { +                            mSaveKeyringParcel.mRevokeSubKeys.remove(keyId); +                        } else { +                            mSaveKeyringParcel.mRevokeSubKeys.add(keyId); +                        } +                        break; +                } +                getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad(); +            } +        }; + +        // Create a new Messenger for the communication back +        final Messenger messenger = new Messenger(returnHandler); + +        DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { +            public void run() { +                EditSubkeyDialogFragment dialogFragment = +                        EditSubkeyDialogFragment.newInstance(messenger); + +                dialogFragment.show(getActivity().getSupportFragmentManager(), "editSubkeyDialog"); +            } +        }); +    } + +    private void editSubkeyExpiry(final long keyId) { +        Handler returnHandler = new Handler() { +            @Override +            public void handleMessage(Message message) { +                switch (message.what) { +                    case ChangeExpiryDialogFragment.MESSAGE_NEW_EXPIRY_DATE: +                        // toggle +//                        if (mSaveKeyringParcel.changePrimaryUserId != null +//                                && mSaveKeyringParcel.changePrimaryUserId.equals(userId)) { +//                            mSaveKeyringParcel.changePrimaryUserId = null; +//                        } else { +//                            mSaveKeyringParcel.changePrimaryUserId = userId; +//                        } +                        break; +                } +                getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad(); +            } +        }; + +        // Create a new Messenger for the communication back +        final Messenger messenger = new Messenger(returnHandler); + +        DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { +            public void run() { +                ChangeExpiryDialogFragment dialogFragment = +                        ChangeExpiryDialogFragment.newInstance(messenger, new Date(), new Date()); + +                dialogFragment.show(getActivity().getSupportFragmentManager(), "editSubkeyExpiryDialog"); +            } +        }); +    } +      private void addUserId() {          mUserIdsAddedAdapter.add(new UserIdsAddedAdapter.UserIdModel());      } @@ -373,10 +451,11 @@ public class EditKeyFragment extends LoaderFragment implements      }      private void save(String passphrase) { -        Log.d(Constants.TAG, "add userids to parcel: " + mUserIdsAddedAdapter.getDataAsStringList()); -        Log.d(Constants.TAG, "mSaveKeyringParcel.newPassphrase: " + mSaveKeyringParcel.newPassphrase); +        mSaveKeyringParcel.mAddUserIds = mUserIdsAddedAdapter.getDataAsStringList(); -        mSaveKeyringParcel.addUserIds = mUserIdsAddedAdapter.getDataAsStringList(); +        Log.d(Constants.TAG, "mSaveKeyringParcel.mAddUserIds: " + mSaveKeyringParcel.mAddUserIds); +        Log.d(Constants.TAG, "mSaveKeyringParcel.mNewPassphrase: " + mSaveKeyringParcel.mNewPassphrase); +        Log.d(Constants.TAG, "mSaveKeyringParcel.mRevokeUserIds: " + mSaveKeyringParcel.mRevokeUserIds);          // Message is received after importing is done in KeychainIntentService          KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( @@ -388,26 +467,30 @@ public class EditKeyFragment extends LoaderFragment implements                  super.handleMessage(message);                  if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { -                    getActivity().finish(); -                    // TODO below                      // get returned data bundle                      Bundle returnData = message.getData();                      if (returnData == null) {                          return;                      } -                    final OperationResults.SaveKeyringResult result = -                            returnData.getParcelable(KeychainIntentService.RESULT); +                    final OperationResults.EditKeyResult result = +                            returnData.getParcelable(EditKeyResult.EXTRA_RESULT);                      if (result == null) {                          return;                      } -                    // if good -> finish, return result to showkey and display there!                      // if bad -> display here! +                    if (!result.success()) { +                        result.createNotify(getActivity()).show(); +                        return; +                    } -//                    result.displayNotify(ImportKeysActivity.this); +                    // if good -> finish, return result to showkey and display there! +                    Intent intent = new Intent(); +                    intent.putExtra(EditKeyResult.EXTRA_RESULT, result); +                    getActivity().setResult(EditKeyActivity.RESULT_OK, intent); +                    getActivity().finish(); -//                    getActivity().finish();                  }              }          }; @@ -432,4 +515,4 @@ public class EditKeyFragment extends LoaderFragment implements          // start service with intent          getActivity().startService(intent);      } -}
\ No newline at end of file +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java new file mode 100644 index 000000000..5f3f170a1 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.ui; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; +import android.view.View; +import android.view.Window; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.util.Log; + +public class FirstTimeActivity extends ActionBarActivity { + +    View mCreateKey; +    View mImportKey; +    View mSkipSetup; + +    public static final int REQUEST_CODE_CREATE_OR_IMPORT_KEY = 0x00007012; + +    @Override +    protected void onCreate(Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); + +        supportRequestWindowFeature(Window.FEATURE_NO_TITLE); + +        setContentView(R.layout.first_time_activity); + +        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(); +            } +        }); + +        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 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(); +            } +        } else { +            Log.e(Constants.TAG, "No valid request code!"); +        } +    } + +    private void finishSetup() { +        Preferences prefs = Preferences.getPreferences(this); +        prefs.setFirstTime(false); +        Intent intent = new Intent(FirstTimeActivity.this, KeyListActivity.class); +        startActivity(intent); +        finish(); +    } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 3ff3b56bf..524fe2ef2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -40,17 +40,19 @@ import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.helper.OtherHelper;  import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.keyimport.FileImportCache;  import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;  import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;  import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;  import org.sufficientlysecure.keychain.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; +import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult;  import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;  import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout;  import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.Notify; +import java.io.IOException;  import java.util.ArrayList;  import java.util.Locale; @@ -354,9 +356,8 @@ public class ImportKeysActivity extends ActionBarActivity {          ImportKeysServerFragment f = (ImportKeysServerFragment)                  getActiveFragment(mViewPager, TAB_KEYSERVER); -        // TODO: Currently it simply uses keyserver nr 0 -        String keyserver = Preferences.getPreferences(ImportKeysActivity.this) -                .getKeyServers()[0]; +        // ask favorite keyserver +        String keyserver = Preferences.getPreferences(ImportKeysActivity.this).getKeyServers()[0];          // set fields of ImportKeysServerFragment          f.setQueryAndKeyserver(query, keyserver); @@ -427,15 +428,15 @@ public class ImportKeysActivity extends ActionBarActivity {                      if (returnData == null) {                          return;                      } -                    final ImportResult result = -                            returnData.getParcelable(KeychainIntentService.RESULT); +                    final ImportKeyResult result = +                            returnData.getParcelable(KeychainIntentService.RESULT_IMPORT);                      if (result == null) {                          return;                      }                      if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(getIntent().getAction())) {                          Intent intent = new Intent(); -                        intent.putExtra(EXTRA_RESULT, result); +                        intent.putExtra(ImportKeyResult.EXTRA_RESULT, result);                          ImportKeysActivity.this.setResult(RESULT_OK, intent);                          ImportKeysActivity.this.finish();                          return; @@ -451,7 +452,7 @@ public class ImportKeysActivity extends ActionBarActivity {                          return;                      } -                    result.displayNotify(ImportKeysActivity.this); +                    result.createNotify(ImportKeysActivity.this).show();                  }              }          }; @@ -470,19 +471,29 @@ public class ImportKeysActivity extends ActionBarActivity {              // get DATA from selected key entries              ArrayList<ParcelableKeyRing> selectedEntries = mListFragment.getSelectedData(); -            data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, selectedEntries); -            intent.putExtra(KeychainIntentService.EXTRA_DATA, data); +            // instead of given the entries by Intent extra, cache them into a file +            // to prevent Java Binder problems on heavy imports +            // read FileImportCache for more info. +            try { +                FileImportCache cache = new FileImportCache(this); +                cache.writeCache(selectedEntries); -            // Create a new Messenger for the communication back -            Messenger messenger = new Messenger(saveHandler); -            intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); +                intent.putExtra(KeychainIntentService.EXTRA_DATA, data); -            // show progress dialog -            saveHandler.showProgressDialog(this); +                // Create a new Messenger for the communication back +                Messenger messenger = new Messenger(saveHandler); +                intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); -            // start service with intent -            startService(intent); +                // show progress dialog +                saveHandler.showProgressDialog(this); + +                // start service with intent +                startService(intent); +            } catch (IOException e) { +                Log.e(Constants.TAG, "Problem writing cache file", e); +                Notify.showNotify(this, "Problem writing cache file!", Notify.Style.ERROR); +            }          } else if (ls instanceof ImportKeysListFragment.KeyserverLoaderState) {              ImportKeysListFragment.KeyserverLoaderState sls = (ImportKeysListFragment.KeyserverLoaderState) ls; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java index 9a39b6cc3..fde0f5f23 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -35,7 +35,6 @@ import org.sufficientlysecure.keychain.helper.Preferences;  import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;  import org.sufficientlysecure.keychain.keyimport.Keyserver;  import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;  import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper;  import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter;  import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListKeybaseLoader; @@ -288,13 +287,13 @@ public class ImportKeysListFragment extends ListFragment implements                  if (error == null) {                      // No error                      mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings(); -                } else if (error instanceof ImportKeysListLoader.FileHasNoContent) { -                    Notify.showNotify(getActivity(), R.string.error_import_file_no_content, Notify.Style.ERROR); -                } else if (error instanceof ImportKeysListLoader.NonPgpPart) { +                } else if (error instanceof ImportKeysListLoader.NoValidKeysException) { +                    Notify.showNotify(getActivity(), R.string.error_import_no_valid_keys, Notify.Style.ERROR); +                } else if (error instanceof ImportKeysListLoader.NonPgpPartException) {                      Notify.showNotify(getActivity(), -                            ((ImportKeysListLoader.NonPgpPart) error).getCount() + " " + getResources(). +                            ((ImportKeysListLoader.NonPgpPartException) error).getCount() + " " + getResources().                                      getQuantityString(R.plurals.error_import_non_pgp_part, -                                            ((ImportKeysListLoader.NonPgpPart) error).getCount()), +                                            ((ImportKeysListLoader.NonPgpPartException) error).getCount()),                              Notify.Style.OK                      );                  } else { @@ -308,9 +307,11 @@ public class ImportKeysListFragment extends ListFragment implements                  if (error == null) {                      // No error                  } else if (error instanceof Keyserver.QueryTooShortException) { -                    Notify.showNotify(getActivity(), R.string.error_keyserver_insufficient_query, Notify.Style.ERROR); +                    Notify.showNotify(getActivity(), R.string.error_query_too_short, Notify.Style.ERROR);                  } else if (error instanceof Keyserver.TooManyResponsesException) { -                    Notify.showNotify(getActivity(), R.string.error_keyserver_too_many_responses, Notify.Style.ERROR); +                    Notify.showNotify(getActivity(), R.string.error_too_many_responses, Notify.Style.ERROR); +                } else if (error instanceof Keyserver.QueryTooShortOrTooManyResponsesException) { +                    Notify.showNotify(getActivity(), R.string.error_too_short_or_too_many_responses, Notify.Style.ERROR);                  } else if (error instanceof Keyserver.QueryFailedException) {                      Log.d(Constants.TAG,                              "Unrecoverable keyserver query error: " + error.getLocalizedMessage()); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java index 849576284..c74f4c96c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java @@ -17,33 +17,21 @@  package org.sufficientlysecure.keychain.ui; -import android.app.ProgressDialog;  import android.content.Intent;  import android.os.Bundle; -import android.os.Message; -import android.os.Messenger;  import android.view.Menu;  import android.view.MenuItem; -import com.devspark.appmsg.AppMsg; - -import org.spongycastle.bcpg.sig.KeyFlags;  import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.Constants.choice.algorithm;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.helper.ExportHelper; -import org.sufficientlysecure.keychain.helper.OtherHelper; -import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; +import org.sufficientlysecure.keychain.helper.Preferences;  import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.provider.KeychainDatabase; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.service.SaveKeyringParcel; -import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;  import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Notify;  import java.io.IOException; -import java.util.ArrayList;  public class KeyListActivity extends DrawerActivity { @@ -53,6 +41,14 @@ public class KeyListActivity extends DrawerActivity {      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState); +        // if this is the first time show first time activity +        Preferences prefs = Preferences.getPreferences(this); +        if (prefs.isFirstTime()) { +            startActivity(new Intent(this, FirstTimeActivity.class)); +            finish(); +            return; +        } +          mExportHelper = new ExportHelper(this);          setContentView(R.layout.key_list_activity); @@ -66,9 +62,10 @@ public class KeyListActivity extends DrawerActivity {          super.onCreateOptionsMenu(menu);          getMenuInflater().inflate(R.menu.key_list, menu); -        if(Constants.DEBUG) { +        if (Constants.DEBUG) {              menu.findItem(R.id.menu_key_list_debug_read).setVisible(true);              menu.findItem(R.id.menu_key_list_debug_write).setVisible(true); +            menu.findItem(R.id.menu_key_list_debug_first_time).setVisible(true);          }          return true; @@ -85,10 +82,6 @@ public class KeyListActivity extends DrawerActivity {                  createKey();                  return true; -            case R.id.menu_key_list_create_expert: -                createKeyExpert(); -                return true; -              case R.id.menu_key_list_export:                  mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true);                  return true; @@ -96,24 +89,32 @@ public class KeyListActivity extends DrawerActivity {              case R.id.menu_key_list_debug_read:                  try {                      KeychainDatabase.debugRead(this); -                    AppMsg.makeText(this, "Restored from backup", AppMsg.STYLE_CONFIRM).show(); +                    Notify.showNotify(this, "Restored Notify.Style backup", Notify.Style.INFO);                      getContentResolver().notifyChange(KeychainContract.KeyRings.CONTENT_URI, null); -                } catch(IOException e) { +                } catch (IOException e) {                      Log.e(Constants.TAG, "IO Error", e); -                    AppMsg.makeText(this, "IO Error: " + e.getMessage(), AppMsg.STYLE_ALERT).show(); +                    Notify.showNotify(this, "IO Notify.Style: " + e.getMessage(), Notify.Style.ERROR);                  }                  return true;              case R.id.menu_key_list_debug_write:                  try {                      KeychainDatabase.debugWrite(this); -                    AppMsg.makeText(this, "Backup successful", AppMsg.STYLE_CONFIRM).show(); +                    Notify.showNotify(this, "Backup Notify.Style", Notify.Style.INFO);                  } catch(IOException e) {                      Log.e(Constants.TAG, "IO Error", e); -                    AppMsg.makeText(this, "IO Error: " + e.getMessage(), AppMsg.STYLE_ALERT).show(); +                    Notify.showNotify(this, "IO Notify.Style: " + e.getMessage(), Notify.Style.ERROR);                  }                  return true; +            case R.id.menu_key_list_debug_first_time: +                Preferences prefs = Preferences.getPreferences(this); +                prefs.setFirstTime(true); +                Intent intent = new Intent(this, FirstTimeActivity.class); +                startActivity(intent); +                finish(); +                return true; +              default:                  return super.onOptionsItemSelected(item);          } @@ -125,50 +126,8 @@ public class KeyListActivity extends DrawerActivity {      }      private void createKey() { -        Intent intent = new Intent(this, WizardActivity.class); -//        intent.setAction(EditKeyActivity.ACTION_CREATE_KEY); -//        intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true); -//        intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, ""); // show user id view -        startActivityForResult(intent, 0); +        Intent intent = new Intent(this, CreateKeyActivity.class); +        startActivity(intent);      } -    private void createKeyExpert() { -        Intent intent = new Intent(this, KeychainIntentService.class); -        intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING); - -        // Message is received after importing is done in KeychainIntentService -        KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( -                this, -                getString(R.string.progress_importing), -                ProgressDialog.STYLE_HORIZONTAL) { -            public void handleMessage(Message message) { -                // handle messages by standard KeychainIntentServiceHandler first -                super.handleMessage(message); -                Bundle data = message.getData(); -                // OtherHelper.logDebugBundle(data, "message reply"); -            } -        }; - -        // fill values for this action -        Bundle data = new Bundle(); - -        SaveKeyringParcel parcel = new SaveKeyringParcel(); -        parcel.addSubKeys.add(new SubkeyAdd(algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null)); -        parcel.addSubKeys.add(new SubkeyAdd(algorithm.rsa, 1024, KeyFlags.SIGN_DATA, null)); -        parcel.addUserIds.add("swagerinho"); -        parcel.newPassphrase = "swag"; - -        // get selected key entries -        data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, parcel); - -        intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - -        // Create a new Messenger for the communication back -        Messenger messenger = new Messenger(saveHandler); -        intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - -        saveHandler.showProgressDialog(this); - -        startService(intent); -    } -}
\ No newline at end of file +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index e2ca50a4c..b8efa6d86 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -22,6 +22,7 @@ import android.content.Context;  import android.content.Intent;  import android.database.Cursor;  import android.graphics.Color; +import android.graphics.PorterDuff;  import android.net.Uri;  import android.os.Build;  import android.os.Bundle; @@ -46,14 +47,11 @@ import android.view.View.OnClickListener;  import android.view.ViewGroup;  import android.widget.AbsListView.MultiChoiceModeListener;  import android.widget.AdapterView; -import android.widget.FrameLayout;  import android.widget.ImageView;  import android.widget.ListView;  import android.widget.TextView;  import android.widget.Button; -import com.devspark.appmsg.AppMsg; -  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.helper.ExportHelper; @@ -62,6 +60,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;  import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;  import org.sufficientlysecure.keychain.util.Highlighter;  import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Notify;  import java.util.Date;  import java.util.HashMap; @@ -86,6 +85,7 @@ public class KeyListFragment extends LoaderFragment      private Button mButtonEmptyCreate;      private Button mButtonEmptyImport; +    public static final int REQUEST_CODE_CREATE_OR_IMPORT_KEY = 0x00007012;      /**       * Load custom layout with StickyListView from library @@ -104,11 +104,8 @@ public class KeyListFragment extends LoaderFragment              @Override              public void onClick(View v) { -                Intent intent = new Intent(getActivity(), EditKeyActivityOld.class); -                intent.setAction(EditKeyActivityOld.ACTION_CREATE_KEY); -                intent.putExtra(EditKeyActivityOld.EXTRA_GENERATE_DEFAULT_KEYS, true); -                intent.putExtra(EditKeyActivityOld.EXTRA_USER_IDS, ""); // show user id view -                startActivityForResult(intent, 0); +                Intent intent = new Intent(getActivity(), CreateKeyActivity.class); +                startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY);              }          });          mButtonEmptyImport = (Button) view.findViewById(R.id.key_list_empty_button_import); @@ -117,8 +114,8 @@ public class KeyListFragment extends LoaderFragment              @Override              public void onClick(View v) {                  Intent intent = new Intent(getActivity(), ImportKeysActivity.class); -                intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE); -                startActivityForResult(intent, 0); +                intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN); +                startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY);              }          }); @@ -339,8 +336,8 @@ public class KeyListFragment extends LoaderFragment      public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) {          // Can only work on singular secret keys          if(hasSecret && masterKeyIds.length > 1) { -            AppMsg.makeText(getActivity(), R.string.secret_cannot_multiple, -                    AppMsg.STYLE_ALERT).show(); +            Notify.showNotify(getActivity(), R.string.secret_cannot_multiple, +                    Notify.Style.ERROR);              return;          } @@ -437,9 +434,7 @@ public class KeyListFragment extends LoaderFragment          private class ItemViewHolder {              TextView mMainUserId;              TextView mMainUserIdRest; -            FrameLayout mStatusLayout; -            TextView mRevoked; -            ImageView mVerified; +            ImageView mStatus;          }          @Override @@ -448,9 +443,7 @@ public class KeyListFragment extends LoaderFragment              ItemViewHolder holder = new ItemViewHolder();              holder.mMainUserId = (TextView) view.findViewById(R.id.mainUserId);              holder.mMainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); -            holder.mStatusLayout = (FrameLayout) view.findViewById(R.id.status_layout); -            holder.mRevoked = (TextView) view.findViewById(R.id.revoked); -            holder.mVerified = (ImageView) view.findViewById(R.id.verified); +            holder.mStatus = (ImageView) view.findViewById(R.id.status_image);              view.setTag(holder);              return view;          } @@ -486,25 +479,36 @@ public class KeyListFragment extends LoaderFragment                  if (cursor.getInt(KeyListFragment.INDEX_HAS_ANY_SECRET) != 0) {                      // this is a secret key -                    h.mStatusLayout.setVisibility(View.VISIBLE); -                    h.mRevoked.setVisibility(View.GONE); -                    h.mVerified.setVisibility(View.GONE); +                    h.mStatus.setVisibility(View.GONE);                  } else { -                    // this is a public key - show if it's revoked +                    // this is a public key - show if it's revoked, expired, or verified                      boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;                      boolean isExpired = !cursor.isNull(INDEX_EXPIRY)                              && new Date(cursor.getLong(INDEX_EXPIRY)*1000).before(new Date()); -                    if(isRevoked || isExpired) { -                        h.mStatusLayout.setVisibility(View.VISIBLE); -                        h.mRevoked.setVisibility(View.VISIBLE); -                        h.mVerified.setVisibility(View.GONE); -                        h.mRevoked.setText(isRevoked ? R.string.revoked : R.string.expired); +                    boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0; + +                    // Note: order is important! +                    if (isRevoked) { +                        h.mStatus.setImageDrawable( +                                getResources().getDrawable(R.drawable.status_signature_revoked_cutout)); +                        h.mStatus.setColorFilter(getResources().getColor(R.color.android_red_light), +                                PorterDuff.Mode.SRC_ATOP); +                        h.mStatus.setVisibility(View.VISIBLE); +                    } else if (isExpired) { +                        h.mStatus.setImageDrawable( +                                getResources().getDrawable(R.drawable.status_signature_expired_cutout)); +                        h.mStatus.setColorFilter(getResources().getColor(R.color.android_orange_light), +                                PorterDuff.Mode.SRC_ATOP); +                        h.mStatus.setVisibility(View.VISIBLE); +                    } else if (isVerified) { +                        h.mStatus.setImageDrawable( +                                getResources().getDrawable(R.drawable.status_signature_verified_cutout)); +                        h.mStatus.setColorFilter(getResources().getColor(R.color.android_green_light), +                                PorterDuff.Mode.SRC_ATOP); +                        h.mStatus.setVisibility(View.VISIBLE);                      } else { -                        boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0; -                        h.mStatusLayout.setVisibility(isVerified ? View.VISIBLE : View.GONE); -                        h.mRevoked.setVisibility(View.GONE); -                        h.mVerified.setVisibility(isVerified ? View.VISIBLE : View.GONE); +                        h.mStatus.setVisibility(View.GONE);                      }                  }              } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index 67317de6e..43de6774b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.util.Log;  import java.util.ArrayList;  import java.util.HashMap; +import java.util.List;  public class LogDisplayFragment extends ListFragment implements OnTouchListener { @@ -135,7 +136,7 @@ public class LogDisplayFragment extends ListFragment implements OnTouchListener          private LayoutInflater mInflater;          private int dipFactor; -        public LogAdapter(Context context, ArrayList<LogEntryParcel> log, LogLevel level) { +        public LogAdapter(Context context, OperationResultParcel.OperationLog log, LogLevel level) {              super(context, R.layout.log_display_item);              mInflater = LayoutInflater.from(getContext());              dipFactor = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, @@ -177,9 +178,11 @@ public class LogDisplayFragment extends ListFragment implements OnTouchListener              if (entry.mParameters != null && entry.mParameters.length > 0                      && entry.mParameters[0] instanceof Integer) {                  ih.mText.setText(getResources().getQuantityString(entry.mType.getMsgId(), -                        (Integer) entry.mParameters[0], entry.mParameters)); +                        (Integer) entry.mParameters[0], +                        entry.mParameters));              } else { -                ih.mText.setText(getResources().getString(entry.mType.getMsgId(), entry.mParameters)); +                ih.mText.setText(getResources().getString(entry.mType.getMsgId(), +                        entry.mParameters));              }              ih.mText.setTextColor(entry.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK);              convertView.setPadding((entry.mIndent) * dipFactor, 0, 0, 0); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java index 448d29156..dcacdbc9d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java @@ -121,6 +121,9 @@ public class PreferencesActivity extends PreferenceActivity {              initializeForceV3Signatures(                      (CheckBoxPreference) findPreference(Constants.Pref.FORCE_V3_SIGNATURES)); +            initializeConcealPgpApplication( +                    (CheckBoxPreference) findPreference(Constants.Pref.CONCEAL_PGP_APPLICATION)); +          } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {              // Load the legacy preferences headers              addPreferencesFromResource(R.xml.preference_headers_legacy); @@ -264,6 +267,9 @@ public class PreferencesActivity extends PreferenceActivity {              initializeForceV3Signatures(                      (CheckBoxPreference) findPreference(Constants.Pref.FORCE_V3_SIGNATURES)); + +            initializeConcealPgpApplication( +                    (CheckBoxPreference) findPreference(Constants.Pref.CONCEAL_PGP_APPLICATION));          }      } @@ -396,4 +402,15 @@ public class PreferencesActivity extends PreferenceActivity {                      }                  });      } + +    private static void initializeConcealPgpApplication(final CheckBoxPreference mConcealPgpApplication) { +        mConcealPgpApplication.setChecked(sPreferences.getConcealPgpApplication()); +        mConcealPgpApplication.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { +            public boolean onPreferenceChange(Preference preference, Object newValue) { +                mConcealPgpApplication.setChecked((Boolean) newValue); +                sPreferences.setConcealPgpApplication((Boolean) newValue); +                return false; +            } +        }); +    }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeActivity.java new file mode 100644 index 000000000..a906dfa97 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeActivity.java @@ -0,0 +1,114 @@ +/* + * 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.net.Uri; +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; +import android.view.View; +import android.widget.ImageView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ActionBarHelper; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Notify; +import org.sufficientlysecure.keychain.util.Notify.Style; +import org.sufficientlysecure.keychain.util.QrCodeUtils; + +public class QrCodeActivity extends ActionBarActivity { + +    private ImageView mFingerprintQrCode; + +    private static final int QR_CODE_SIZE = 1000; + +    @Override +    public void onCreate(Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); + +        // Inflate a "Done" custom action bar +        ActionBarHelper.setOneButtonView(getSupportActionBar(), +                R.string.btn_okay, R.drawable.ic_action_done, +                new View.OnClickListener() { +                    @Override +                    public void onClick(View v) { +                        // "Done" +                        finish(); +                    } +                } +        ); + +        setContentView(R.layout.qr_code_activity); + +        Uri dataUri = getIntent().getData(); +        if (dataUri == null) { +            Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); +            finish(); +            return; +        } + +        mFingerprintQrCode = (ImageView) findViewById(R.id.qr_code_image); + +        mFingerprintQrCode.setOnClickListener(new View.OnClickListener() { +            @Override +            public void onClick(View v) { +                finish(); +            } +        }); + +        ProviderHelper providerHelper = new ProviderHelper(this); +        try { +            byte[] blob = (byte[]) providerHelper.getGenericData( +                    KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri), +                    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); +                finish(); +            } + +            String fingerprint = PgpKeyHelper.convertFingerprintToHex(blob); +            String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint; +            mFingerprintQrCode.setImageBitmap(QrCodeUtils.getQRCodeBitmap(qrCodeContent, QR_CODE_SIZE)); +        } catch (ProviderHelper.NotFoundException e) { +            Log.e(Constants.TAG, "key not found!", e); +            Notify.showNotify(this, R.string.error_key_not_found, Style.ERROR); +            finish(); +        } +    } + +    @Override +    protected void onResume() { +        super.onResume(); + +        // custom activity transition to get zoom in effect +        this.overridePendingTransition(R.anim.qr_code_zoom_enter, android.R.anim.fade_out); +    } + +    @Override +    protected void onPause() { +        super.onPause(); + +        // custom activity transition to get zoom out effect +        this.overridePendingTransition(0, R.anim.qr_code_zoom_exit); +    } + +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java index c7fffe263..5201b5df8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java @@ -35,7 +35,7 @@ import android.widget.TextView;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing; +import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;  import org.sufficientlysecure.keychain.pgp.WrappedSignature;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; @@ -143,16 +143,16 @@ public class ViewCertActivity extends ActionBarActivity              try {                  ProviderHelper providerHelper = new ProviderHelper(this); -                WrappedPublicKeyRing signeeRing = -                        providerHelper.getWrappedPublicKeyRing(data.getLong(INDEX_MASTER_KEY_ID)); -                WrappedPublicKeyRing signerRing = -                        providerHelper.getWrappedPublicKeyRing(sig.getKeyId()); +                CanonicalizedPublicKeyRing signeeRing = +                        providerHelper.getCanonicalizedPublicKeyRing(data.getLong(INDEX_MASTER_KEY_ID)); +                CanonicalizedPublicKeyRing signerRing = +                        providerHelper.getCanonicalizedPublicKeyRing(sig.getKeyId());                  try { -                    sig.init(signerRing.getSubkey()); -                    if (sig.verifySignature(signeeRing.getSubkey(), signeeUid)) { +                    sig.init(signerRing.getPublicKey()); +                    if (sig.verifySignature(signeeRing.getPublicKey(), signeeUid)) {                          mStatus.setText(R.string.cert_verify_ok); -                        mStatus.setTextColor(getResources().getColor(R.color.result_green)); +                        mStatus.setTextColor(getResources().getColor(R.color.android_green_light));                      } else {                          mStatus.setText(R.string.cert_verify_failed);                          mStatus.setTextColor(getResources().getColor(R.color.alert)); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 22a23e6a5..69efb4cbd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -19,9 +19,9 @@  package org.sufficientlysecure.keychain.ui;  import android.annotation.TargetApi; -import android.app.Activity;  import android.content.Intent;  import android.database.Cursor; +import android.graphics.PorterDuff;  import android.net.Uri;  import android.nfc.NdefMessage;  import android.nfc.NdefRecord; @@ -43,8 +43,9 @@ import android.view.Menu;  import android.view.MenuItem;  import android.view.View;  import android.view.Window; - -import com.devspark.appmsg.AppMsg; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; @@ -54,11 +55,12 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;  import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;  import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; +import org.sufficientlysecure.keychain.service.OperationResultParcel;  import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;  import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout.TabColorizer;  import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout; +import org.sufficientlysecure.keychain.util.Notify;  import java.util.Date;  import java.util.HashMap; @@ -81,9 +83,11 @@ public class ViewKeyActivity extends ActionBarActivity implements      private ViewPager mViewPager;      private SlidingTabLayout mSlidingTabLayout;      private PagerTabStripAdapter mTabsAdapter; + +    private LinearLayout mStatusLayout; +    private TextView mStatusText; +    private ImageView mStatusImage;      private View mStatusDivider; -    private View mStatusRevoked; -    private View mStatusExpired;      public static final int REQUEST_CODE_LOOKUP_KEY = 0x00007006; @@ -115,9 +119,10 @@ public class ViewKeyActivity extends ActionBarActivity implements          setContentView(R.layout.view_key_activity); -        mStatusDivider = findViewById(R.id.status_divider); -        mStatusRevoked = findViewById(R.id.view_key_revoked); -        mStatusExpired = findViewById(R.id.view_key_expired); +        mStatusLayout = (LinearLayout) findViewById(R.id.view_key_status_layout); +        mStatusText = (TextView) findViewById(R.id.view_key_status_text); +        mStatusImage = (ImageView) findViewById(R.id.view_key_status_image); +        mStatusDivider = findViewById(R.id.view_key_status_divider);          mViewPager = (ViewPager) findViewById(R.id.view_key_pager);          mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.view_key_sliding_tab_layout); @@ -295,7 +300,7 @@ public class ViewKeyActivity extends ActionBarActivity implements                  }              }          } catch (ProviderHelper.NotFoundException e) { -            AppMsg.makeText(this, R.string.error_key_not_found, AppMsg.STYLE_ALERT).show(); +            Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR);              Log.e(Constants.TAG, "Key not found", e);          }          return super.onOptionsItemSelected(item); @@ -351,22 +356,11 @@ public class ViewKeyActivity extends ActionBarActivity implements      @Override      protected void onActivityResult(int requestCode, int resultCode, Intent data) { -        switch (requestCode) { -            case REQUEST_CODE_LOOKUP_KEY: { -                if (resultCode == Activity.RESULT_OK) { -                    ImportResult result = data.getParcelableExtra(ImportKeysActivity.EXTRA_RESULT); -                    if (result != null) { -                        result.displayNotify(this); -                    } -                } -                break; -            } - -            default: { -                super.onActivityResult(requestCode, resultCode, data); - -                break; -            } +        if (data != null && data.hasExtra(OperationResultParcel.EXTRA_RESULT)) { +            OperationResultParcel result = data.getParcelableExtra(OperationResultParcel.EXTRA_RESULT); +            result.createNotify(this).show(); +        } else { +            super.onActivityResult(requestCode, resultCode, data);          }      } @@ -454,8 +448,8 @@ public class ViewKeyActivity extends ActionBarActivity implements          public void handleMessage(Message msg) {              switch (msg.what) {                  case NFC_SENT: -                    AppMsg.makeText(ViewKeyActivity.this, R.string.nfc_successful, -                            AppMsg.STYLE_INFO).show(); +                    Notify.showNotify( +                            ViewKeyActivity.this, R.string.nfc_successful, Notify.Style.INFO);                      break;              }          } @@ -514,22 +508,32 @@ public class ViewKeyActivity extends ActionBarActivity implements                      String keyIdStr = PgpKeyHelper.convertKeyIdToHex(masterKeyId);                      getSupportActionBar().setSubtitle(keyIdStr); -                    // If this key is revoked, it cannot be used for anything! -                    if (data.getInt(INDEX_UNIFIED_IS_REVOKED) != 0) { +                    boolean isRevoked = data.getInt(INDEX_UNIFIED_IS_REVOKED) > 0; +                    boolean isExpired = !data.isNull(INDEX_UNIFIED_EXPIRY) +                            && new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000).before(new Date()); + +                    // Note: order is important +                    if (isRevoked) { +                        mStatusText.setText(R.string.view_key_revoked); +                        mStatusText.setTextColor(getResources().getColor(R.color.android_red_light)); +                        mStatusImage.setImageDrawable( +                                getResources().getDrawable(R.drawable.status_signature_revoked_cutout)); +                        mStatusImage.setColorFilter(getResources().getColor(R.color.android_red_light), +                                PorterDuff.Mode.SRC_ATOP); +                        mStatusDivider.setVisibility(View.VISIBLE); +                        mStatusLayout.setVisibility(View.VISIBLE); +                    } else if (isExpired) { +                        mStatusText.setText(R.string.view_key_expired); +                        mStatusText.setTextColor(getResources().getColor(R.color.android_orange_light)); +                        mStatusImage.setImageDrawable( +                                getResources().getDrawable(R.drawable.status_signature_expired_cutout)); +                        mStatusImage.setColorFilter(getResources().getColor(R.color.android_orange_light), +                                PorterDuff.Mode.SRC_ATOP);                          mStatusDivider.setVisibility(View.VISIBLE); -                        mStatusRevoked.setVisibility(View.VISIBLE); -                        mStatusExpired.setVisibility(View.GONE); +                        mStatusLayout.setVisibility(View.VISIBLE);                      } else { -                        mStatusRevoked.setVisibility(View.GONE); - -                        Date expiryDate = new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000); -                        if (!data.isNull(INDEX_UNIFIED_EXPIRY) && expiryDate.before(new Date())) { -                            mStatusDivider.setVisibility(View.VISIBLE); -                            mStatusExpired.setVisibility(View.VISIBLE); -                        } else { -                            mStatusDivider.setVisibility(View.GONE); -                            mStatusExpired.setVisibility(View.GONE); -                        } +                        mStatusDivider.setVisibility(View.GONE); +                        mStatusLayout.setVisibility(View.GONE);                      }                      break; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index f0636cf2c..a156ff5f1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui;  import android.content.Intent;  import android.database.Cursor; +import android.graphics.PorterDuff;  import android.net.Uri;  import android.os.Bundle;  import android.support.v4.app.LoaderManager; @@ -27,10 +28,9 @@ import android.support.v4.content.Loader;  import android.view.LayoutInflater;  import android.view.View;  import android.view.ViewGroup; +import android.widget.ImageView;  import android.widget.ListView; -import com.devspark.appmsg.AppMsg; -  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; @@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;  import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Notify;  import java.util.Date; @@ -52,6 +53,8 @@ public class ViewKeyMainFragment extends LoaderFragment implements      private View mActionEditDivider;      private View mActionEncrypt;      private View mActionCertify; +    private View mActionCertifyText; +    private ImageView mActionCertifyImage;      private View mActionCertifyDivider;      private ListView mUserIds; @@ -76,6 +79,11 @@ public class ViewKeyMainFragment extends LoaderFragment implements          mActionEditDivider = view.findViewById(R.id.view_key_action_edit_divider);          mActionEncrypt = view.findViewById(R.id.view_key_action_encrypt);          mActionCertify = view.findViewById(R.id.view_key_action_certify); +        mActionCertifyText = view.findViewById(R.id.view_key_action_certify_text); +        mActionCertifyImage = (ImageView) view.findViewById(R.id.view_key_action_certify_image); +        // make certify image gray, like action icons +        mActionCertifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), +                PorterDuff.Mode.SRC_IN);          mActionCertifyDivider = view.findViewById(R.id.view_key_action_certify_divider);          return root; @@ -182,6 +190,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements                      if (data.getInt(INDEX_UNIFIED_IS_REVOKED) != 0) {                          mActionEdit.setEnabled(false);                          mActionCertify.setEnabled(false); +                        mActionCertifyText.setEnabled(false);                          mActionEncrypt.setEnabled(false);                      } else {                          mActionEdit.setEnabled(true); @@ -189,9 +198,11 @@ public class ViewKeyMainFragment extends LoaderFragment implements                          Date expiryDate = new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000);                          if (!data.isNull(INDEX_UNIFIED_EXPIRY) && expiryDate.before(new Date())) {                              mActionCertify.setEnabled(false); +                            mActionCertifyText.setEnabled(false);                              mActionEncrypt.setEnabled(false);                          } else {                              mActionCertify.setEnabled(true); +                            mActionCertifyText.setEnabled(true);                              mActionEncrypt.setEnabled(true);                          }                      } @@ -225,7 +236,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements      private void encrypt(Uri dataUri) {          // If there is no encryption key, don't bother.          if (!mHasEncrypt) { -            AppMsg.makeText(getActivity(), R.string.error_no_encrypt_subkey, AppMsg.STYLE_ALERT).show(); +            Notify.showNotify(getActivity(), R.string.error_no_encrypt_subkey, Notify.Style.ERROR);              return;          }          try { @@ -246,15 +257,13 @@ public class ViewKeyMainFragment extends LoaderFragment implements      private void certify(Uri dataUri) {          Intent signIntent = new Intent(getActivity(), CertifyKeyActivity.class);          signIntent.setData(dataUri); -        startActivity(signIntent); +        startActivityForResult(signIntent, 0);      }      private void editKey(Uri dataUri) {          Intent editIntent = new Intent(getActivity(), EditKeyActivity.class);          editIntent.setData(KeychainContract.KeyRingData.buildSecretKeyRingUri(dataUri)); -//        editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY); -//        startActivityForResult(editIntent, 0); -        startActivity(editIntent); +        startActivityForResult(editIntent, 0);      }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java index 52b573f47..e81d5dbf7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java @@ -20,7 +20,14 @@ package org.sufficientlysecure.keychain.ui;  import android.annotation.TargetApi;  import android.content.Intent;  import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.TransitionDrawable;  import android.net.Uri; +import android.os.AsyncTask;  import android.os.Build;  import android.os.Bundle;  import android.provider.Settings; @@ -33,8 +40,6 @@ import android.view.ViewGroup;  import android.widget.ImageView;  import android.widget.TextView; -import com.devspark.appmsg.AppMsg; -  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; @@ -45,8 +50,8 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;  import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment; -import org.sufficientlysecure.keychain.ui.dialog.ShareQrCodeDialogFragment;  import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Notify;  import org.sufficientlysecure.keychain.util.QrCodeUtils;  import java.io.IOException; @@ -152,7 +157,7 @@ public class ViewKeyShareFragment extends LoaderFragment implements                          KeyRings.buildUnifiedKeyRingUri(dataUri),                          Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);                  String fingerprint = PgpKeyHelper.convertFingerprintToHex(data); -                if(!toClipboard){ +                if (!toClipboard) {                      content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;                  } else {                      content = fingerprint; @@ -171,13 +176,13 @@ public class ViewKeyShareFragment extends LoaderFragment implements                  } else {                      message = getResources().getString(R.string.key_copied_to_clipboard);                  } -                AppMsg.makeText(getActivity(), message, AppMsg.STYLE_INFO).show(); +                Notify.showNotify(getActivity(), message, Notify.Style.OK);              } else {                  // Android will fail with android.os.TransactionTooLargeException if key is too big                  // see http://www.lonestarprod.com/?p=34                  if (content.length() >= 86389) { -                    AppMsg.makeText(getActivity(), R.string.key_too_big_for_sharing, -                            AppMsg.STYLE_ALERT).show(); +                    Notify.showNotify(getActivity(), R.string.key_too_big_for_sharing, +                            Notify.Style.ERROR);                      return;                  } @@ -195,19 +200,20 @@ public class ViewKeyShareFragment extends LoaderFragment implements              }          } catch (PgpGeneralException e) {              Log.e(Constants.TAG, "error processing key!", e); -            AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show(); +            Notify.showNotify(getActivity(), R.string.error_key_processing, Notify.Style.ERROR);          } catch (IOException e) {              Log.e(Constants.TAG, "error processing key!", e); -            AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show(); +            Notify.showNotify(getActivity(), R.string.error_key_processing, Notify.Style.ERROR);          } catch (ProviderHelper.NotFoundException e) {              Log.e(Constants.TAG, "key not found!", e); -            AppMsg.makeText(getActivity(), R.string.error_key_not_found, AppMsg.STYLE_ALERT).show(); +            Notify.showNotify(getActivity(), R.string.error_key_not_found, Notify.Style.ERROR);          }      }      private void showQrCodeDialog() { -        ShareQrCodeDialogFragment dialog = ShareQrCodeDialogFragment.newInstance(mDataUri); -        dialog.show(ViewKeyShareFragment.this.getActivity().getSupportFragmentManager(), "shareQrCodeDialog"); +        Intent qrCodeIntent = new Intent(getActivity(), QrCodeActivity.class); +        qrCodeIntent.setData(mDataUri); +        startActivity(qrCodeIntent);      }      private void showNfcHelpDialog() { @@ -292,10 +298,7 @@ public class ViewKeyShareFragment extends LoaderFragment implements                      String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);                      mFingerprint.setText(PgpKeyHelper.colorizeFingerprint(fingerprint)); -                    String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint; -                    mFingerprintQrCode.setImageBitmap( -                            QrCodeUtils.getQRCodeBitmap(qrCodeContent, QR_CODE_SIZE) -                    ); +                    loadQrCode(fingerprint);                      break;                  } @@ -311,4 +314,35 @@ public class ViewKeyShareFragment extends LoaderFragment implements       */      public void onLoaderReset(Loader<Cursor> loader) {      } + +    /** +     * Load QR Code asynchronously and with a fade in animation +     * +     * @param fingerprint +     */ +    private void loadQrCode(final String fingerprint) { +        AsyncTask<Void, Void, Bitmap> loadTask = +                new AsyncTask<Void, Void, Bitmap>() { +                    protected Bitmap doInBackground(Void... unused) { +                        String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint; +                        return QrCodeUtils.getQRCodeBitmap(qrCodeContent, QR_CODE_SIZE); +                    } + +                    protected void onPostExecute(Bitmap qrCode) { +                        mFingerprintQrCode.setImageBitmap(qrCode); + +                        // Transition drawable with a transparent drawable and the final bitmap +                        final TransitionDrawable td = +                                new TransitionDrawable(new Drawable[]{ +                                        new ColorDrawable(Color.TRANSPARENT), +                                        new BitmapDrawable(getResources(), qrCode) +                                }); + +                        mFingerprintQrCode.setImageDrawable(td); +                        td.startTransition(200); +                    } +                }; + +        loadTask.execute(); +    }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/WizardActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/WizardActivity.java deleted file mode 100644 index 601fc09f9..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/WizardActivity.java +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program.  If not, see <http://www.gnu.org/licenses/>. - */ - -package org.sufficientlysecure.keychain.ui; - -import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.support.v7.app.ActionBarActivity; -import android.text.Editable; -import android.text.TextWatcher; -import android.util.Patterns; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.InputMethodManager; -import android.widget.ArrayAdapter; -import android.widget.AutoCompleteTextView; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.RadioGroup; -import android.widget.TextView; - -import org.sufficientlysecure.htmltextview.HtmlTextView; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.ContactHelper; -import org.sufficientlysecure.keychain.util.Log; - -import java.util.regex.Matcher; - - -public class WizardActivity extends ActionBarActivity { - -    private State mCurrentState; - -    // values for mCurrentScreen -    private enum State { -        START, CREATE_KEY, IMPORT_KEY, K9 -    } - -    public static final int REQUEST_CODE_IMPORT = 0x00007703; - -    Button mBackButton; -    Button mNextButton; -    StartFragment mStartFragment; -    CreateKeyFragment mCreateKeyFragment; -    K9Fragment mK9Fragment; - -    private static final String K9_PACKAGE = "com.fsck.k9"; -    //    private static final String K9_MARKET_INTENT_URI_BASE = "market://details?id=%s"; -//    private static final Intent K9_MARKET_INTENT = new Intent(Intent.ACTION_VIEW, Uri.parse( -//            String.format(K9_MARKET_INTENT_URI_BASE, K9_PACKAGE))); -    private static final Intent K9_MARKET_INTENT = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/k9mail/k-9/releases/tag/4.904")); - -    LinearLayout mProgressLayout; -    View mProgressLine; -    ProgressBar mProgressBar; -    ImageView mProgressImage; -    TextView mProgressText; - -    /** -     * Checks if text of given EditText is not empty. If it is empty an error is -     * set and the EditText gets the focus. -     * -     * @param context -     * @param editText -     * @return true if EditText is not empty -     */ -    private static boolean isEditTextNotEmpty(Context context, EditText editText) { -        boolean output = true; -        if (editText.getText().toString().length() == 0) { -            editText.setError("empty!"); -            editText.requestFocus(); -            output = false; -        } else { -            editText.setError(null); -        } - -        return output; -    } - -    public static class StartFragment extends Fragment { -        public static StartFragment newInstance() { -            StartFragment myFragment = new StartFragment(); - -            Bundle args = new Bundle(); -            myFragment.setArguments(args); - -            return myFragment; -        } - -        @Override -        public View onCreateView(LayoutInflater inflater, ViewGroup container, -                                 Bundle savedInstanceState) { -            return inflater.inflate(R.layout.wizard_start_fragment, -                    container, false); -        } -    } - -    public static class CreateKeyFragment extends Fragment { -        public static CreateKeyFragment newInstance() { -            CreateKeyFragment myFragment = new CreateKeyFragment(); - -            Bundle args = new Bundle(); -            myFragment.setArguments(args); - -            return myFragment; -        } - -        @Override -        public View onCreateView(LayoutInflater inflater, ViewGroup container, -                                 Bundle savedInstanceState) { -            View view = inflater.inflate(R.layout.wizard_create_key_fragment, -                    container, false); - -            final AutoCompleteTextView emailView = (AutoCompleteTextView) view.findViewById(R.id.email); -            emailView.setThreshold(1); // Start working from first character -            emailView.setAdapter( -                    new ArrayAdapter<String> -                            (getActivity(), android.R.layout.simple_spinner_dropdown_item, -                                    ContactHelper.getPossibleUserEmails(getActivity()) -                            ) -            ); -            emailView.addTextChangedListener(new TextWatcher() { -                @Override -                public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { -                } - -                @Override -                public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { -                } - -                @Override -                public void afterTextChanged(Editable editable) { -                    String email = editable.toString(); -                    if (email.length() > 0) { -                        Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email); -                        if (emailMatcher.matches()) { -                            emailView.setCompoundDrawablesWithIntrinsicBounds(0, 0, -                                    R.drawable.uid_mail_ok, 0); -                        } else { -                            emailView.setCompoundDrawablesWithIntrinsicBounds(0, 0, -                                    R.drawable.uid_mail_bad, 0); -                        } -                    } else { -                        // remove drawable if email is empty -                        emailView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); -                    } -                } -            }); -            final AutoCompleteTextView nameView = (AutoCompleteTextView) view.findViewById(R.id.name); -            nameView.setThreshold(1); // Start working from first character -            nameView.setAdapter( -                    new ArrayAdapter<String> -                            (getActivity(), android.R.layout.simple_spinner_dropdown_item, -                                    ContactHelper.getPossibleUserNames(getActivity()) -                            ) -            ); -            return view; -        } -    } - -    public static class K9Fragment extends Fragment { -        public static K9Fragment newInstance() { -            K9Fragment myFragment = new K9Fragment(); - -            Bundle args = new Bundle(); -            myFragment.setArguments(args); - -            return myFragment; -        } - -        @Override -        public View onCreateView(LayoutInflater inflater, ViewGroup container, -                                 Bundle savedInstanceState) { -            View v = inflater.inflate(R.layout.wizard_k9_fragment, -                    container, false); - -            HtmlTextView text = (HtmlTextView) v -                    .findViewById(R.id.wizard_k9_text); -            text.setHtmlFromString("Install K9. It's good for you! Here is a screenhot how to enable OK in K9: (TODO)", true); - -            return v; -        } - -    } - -    /** -     * Loads new fragment -     * -     * @param fragment -     */ -    private void loadFragment(Fragment fragment) { -        FragmentManager fragmentManager = getSupportFragmentManager(); -        FragmentTransaction fragmentTransaction = fragmentManager -                .beginTransaction(); -        fragmentTransaction.replace(R.id.wizard_container, -                fragment); -        fragmentTransaction.commit(); -    } - -    /** -     * Instantiate View and initialize fragments for this Activity -     */ -    @Override -    protected void onCreate(Bundle savedInstanceState) { -        super.onCreate(savedInstanceState); - -        setContentView(R.layout.wizard_activity); -        mBackButton = (Button) findViewById(R.id.wizard_back); -        mNextButton = (Button) findViewById(R.id.wizard_next); - -        // progress layout -        mProgressLayout = (LinearLayout) findViewById(R.id.wizard_progress); -        mProgressLine = findViewById(R.id.wizard_progress_line); -        mProgressBar = (ProgressBar) findViewById(R.id.wizard_progress_progressbar); -        mProgressImage = (ImageView) findViewById(R.id.wizard_progress_image); -        mProgressText = (TextView) findViewById(R.id.wizard_progress_text); - -        changeToState(State.START); -    } - -    private enum ProgressState { -        WORKING, ENABLED, DISABLED, ERROR -    } - -    private void showProgress(ProgressState state, String text) { -        switch (state) { -            case WORKING: -                mProgressBar.setVisibility(View.VISIBLE); -                mProgressImage.setVisibility(View.GONE); -                break; -            case ENABLED: -                mProgressBar.setVisibility(View.GONE); -                mProgressImage.setVisibility(View.VISIBLE); -//			mProgressImage.setImageDrawable(getResources().getDrawable( -//					R.drawable.status_enabled)); -                break; -            case DISABLED: -                mProgressBar.setVisibility(View.GONE); -                mProgressImage.setVisibility(View.VISIBLE); -//			mProgressImage.setImageDrawable(getResources().getDrawable( -//					R.drawable.status_disabled)); -                break; -            case ERROR: -                mProgressBar.setVisibility(View.GONE); -                mProgressImage.setVisibility(View.VISIBLE); -//			mProgressImage.setImageDrawable(getResources().getDrawable( -//					R.drawable.status_fail)); -                break; - -            default: -                break; -        } -        mProgressText.setText(text); - -        mProgressLine.setVisibility(View.VISIBLE); -        mProgressLayout.setVisibility(View.VISIBLE); -    } - -    private void hideProgress() { -        mProgressLine.setVisibility(View.GONE); -        mProgressLayout.setVisibility(View.GONE); -    } - -    public void nextOnClick(View view) { -        // close keyboard -        if (getCurrentFocus() != null) { -            InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); -            inputManager.hideSoftInputFromWindow(getCurrentFocus() -                    .getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); -        } - -        switch (mCurrentState) { -            case START: { -                RadioGroup radioGroup = (RadioGroup) findViewById(R.id.wizard_start_radio_group); -                int selectedId = radioGroup.getCheckedRadioButtonId(); -                switch (selectedId) { -                    case R.id.wizard_start_new_key: { -                        changeToState(State.CREATE_KEY); -                        break; -                    } -                    case R.id.wizard_start_import: { -                        changeToState(State.IMPORT_KEY); -                        break; -                    } -                    case R.id.wizard_start_skip: { -                        finish(); -                        break; -                    } -                } - -                mBackButton.setText(R.string.btn_back); -                break; -            } -            case CREATE_KEY: -                EditText nameEdit = (EditText) findViewById(R.id.name); -                EditText emailEdit = (EditText) findViewById(R.id.email); -                EditText passphraseEdit = (EditText) findViewById(R.id.passphrase); - -                if (isEditTextNotEmpty(this, nameEdit) -                        && isEditTextNotEmpty(this, emailEdit) -                        && isEditTextNotEmpty(this, passphraseEdit)) { - -//                    SaveKeyringParcel newKey = new SaveKeyringParcel(); -//                    newKey.addUserIds.add(nameEdit.getText().toString() + " <" -//                            + emailEdit.getText().toString() + ">"); - - -                    AsyncTask<String, Boolean, Boolean> generateTask = new AsyncTask<String, Boolean, Boolean>() { - -                        @Override -                        protected void onPreExecute() { -                            super.onPreExecute(); - -                            showProgress(ProgressState.WORKING, "generating key..."); -                        } - -                        @Override -                        protected Boolean doInBackground(String... params) { -                            return true; -                        } - -                        @Override -                        protected void onPostExecute(Boolean result) { -                            super.onPostExecute(result); - -                            if (result) { -                                showProgress(ProgressState.ENABLED, "key generated successfully!"); - -                                changeToState(State.K9); -                            } else { -                                showProgress(ProgressState.ERROR, "error in key gen"); -                            } -                        } - -                    }; - -                    generateTask.execute(""); -                } -                break; -            case K9: { -                RadioGroup radioGroup = (RadioGroup) findViewById(R.id.wizard_k9_radio_group); -                int selectedId = radioGroup.getCheckedRadioButtonId(); -                switch (selectedId) { -                    case R.id.wizard_k9_install: { -                        try { -                            startActivity(K9_MARKET_INTENT); -                        } catch (ActivityNotFoundException e) { -                            Log.e(Constants.TAG, "Activity not found for: " + K9_MARKET_INTENT); -                        } -                        break; -                    } -                    case R.id.wizard_k9_skip: { -                        finish(); -                        break; -                    } -                } - -                finish(); -                break; -            } -            default: -                break; -        } -    } - -    @Override -    protected void onActivityResult(int requestCode, int resultCode, Intent data) { -        super.onActivityResult(requestCode, resultCode, data); -        switch (requestCode) { -            case REQUEST_CODE_IMPORT: { -                if (resultCode == Activity.RESULT_OK) { -                    // imported now... -                    changeToState(State.K9); -                } else { -                    // back to start -                    changeToState(State.START); -                } -                break; -            } - -            default: { -                super.onActivityResult(requestCode, resultCode, data); - -                break; -            } -        } -    } - -    public void backOnClick(View view) { -        switch (mCurrentState) { -            case START: -                finish(); -                break; -            case CREATE_KEY: -                changeToState(State.START); -                break; -            case IMPORT_KEY: -                changeToState(State.START); -                break; -            default: -                changeToState(State.START); -                break; -        } -    } - -    private void changeToState(State state) { -        switch (state) { -            case START: { -                mCurrentState = State.START; -                mStartFragment = StartFragment.newInstance(); -                loadFragment(mStartFragment); -                mBackButton.setText(android.R.string.cancel); -                mNextButton.setText(R.string.btn_next); -                break; -            } -            case CREATE_KEY: { -                mCurrentState = State.CREATE_KEY; -                mCreateKeyFragment = CreateKeyFragment.newInstance(); -                loadFragment(mCreateKeyFragment); -                break; -            } -            case IMPORT_KEY: { -                mCurrentState = State.IMPORT_KEY; -                Intent intent = new Intent(this, ImportKeysActivity.class); -                intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN); -                startActivityForResult(intent, REQUEST_CODE_IMPORT); -                break; -            } -            case K9: { -                mCurrentState = State.K9; -                mBackButton.setEnabled(false); // don't go back to import/create key -                mK9Fragment = K9Fragment.newInstance(); -                loadFragment(mK9Fragment); -                break; -            } -        } -    } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java index 99f959035..4971c535c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java @@ -30,20 +30,21 @@ import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.PositionAwareInputStream;  import java.io.BufferedInputStream; +import java.io.IOException;  import java.util.ArrayList; +import java.util.Iterator;  import java.util.List;  public class ImportKeysListLoader          extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { -    public static class FileHasNoContent extends Exception { - +    public static class NoValidKeysException extends Exception {      } -    public static class NonPgpPart extends Exception { +    public static class NonPgpPartException extends Exception {          private int mCount; -        public NonPgpPart(int count) { +        public NonPgpPartException(int count) {              this.mCount = count;          } @@ -67,7 +68,6 @@ public class ImportKeysListLoader      @Override      public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() { -          // This has already been loaded! nvm any further, just return          if (mEntryListWrapper != null) {              return mEntryListWrapper; @@ -119,9 +119,6 @@ public class ImportKeysListLoader       * @return       */      private void generateListOfKeyrings(InputData inputData) { - -        boolean isEmpty = true; -          PositionAwareInputStream progressIn = new PositionAwareInputStream(                  inputData.getInputStream()); @@ -130,27 +127,23 @@ public class ImportKeysListLoader          // armor blocks          BufferedInputStream bufferedInput = new BufferedInputStream(progressIn);          try { - -            // read all available blocks... (asc files can contain many blocks with BEGIN END) -            while (bufferedInput.available() > 0) { -                // TODO: deal with non-keyring objects? -                List<UncachedKeyRing> rings = UncachedKeyRing.fromStream(bufferedInput); -                for(UncachedKeyRing key : rings) { -                    ImportKeysListEntry item = new ImportKeysListEntry(getContext(), key); -                    mData.add(item); -                    mParcelableRings.put(item.hashCode(), new ParcelableKeyRing(key.getEncoded())); -                    isEmpty = false; -                } +            // parse all keyrings +            Iterator<UncachedKeyRing> it = UncachedKeyRing.fromStream(bufferedInput); +            while (it.hasNext()) { +                UncachedKeyRing ring = it.next(); +                ImportKeysListEntry item = new ImportKeysListEntry(getContext(), ring); +                mData.add(item); +                mParcelableRings.put(item.hashCode(), new ParcelableKeyRing(ring.getEncoded()));              } -        } catch (Exception e) { -            Log.e(Constants.TAG, "Exception on parsing key file!", e); -            mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, e); -        } +        } catch (IOException e) { +            Log.e(Constants.TAG, "IOException on parsing key file! Return NoValidKeysException!", e); -        if (isEmpty) { -            Log.e(Constants.TAG, "File has no content!", new FileHasNoContent()); +            NoValidKeysException e1 = new NoValidKeysException();              mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> -                    (mData, new FileHasNoContent()); +                    (mData, e1); +        } catch (Exception e) { +            Log.e(Constants.TAG, "Other Exception on parsing key file!", e); +            mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, e);          }      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java index 02b1f31e2..e5dbebe01 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java @@ -32,14 +32,15 @@ import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.helper.OtherHelper;  import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;  import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel;  import java.util.Date;  public class SubkeysAdapter extends CursorAdapter {      private LayoutInflater mInflater; +    private SaveKeyringParcel mSaveKeyringParcel;      private boolean hasAnySecret; -      private ColorStateList mDefaultTextColor;      public static final String[] SUBKEYS_PROJECTION = new String[]{ @@ -71,10 +72,21 @@ public class SubkeysAdapter extends CursorAdapter {      private static final int INDEX_EXPIRY = 11;      private static final int INDEX_FINGERPRINT = 12; -    public SubkeysAdapter(Context context, Cursor c, int flags) { +    public SubkeysAdapter(Context context, Cursor c, int flags, +                          SaveKeyringParcel saveKeyringParcel) {          super(context, c, flags);          mInflater = LayoutInflater.from(context); +        mSaveKeyringParcel = saveKeyringParcel; +    } + +    public SubkeysAdapter(Context context, Cursor c, int flags) { +        this(context, c, flags, null); +    } + +    public long getKeyId(int position) { +        mCursor.moveToPosition(position); +        return mCursor.getLong(INDEX_KEY_ID);      }      @Override @@ -94,79 +106,94 @@ public class SubkeysAdapter extends CursorAdapter {      @Override      public void bindView(View view, Context context, Cursor cursor) { -        TextView keyId = (TextView) view.findViewById(R.id.keyId); -        TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails); -        TextView keyExpiry = (TextView) view.findViewById(R.id.keyExpiry); -        ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey); -        ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey); -        ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey); -        ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey); -        ImageView revokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey); - -        String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(INDEX_KEY_ID)); +        TextView vKeyId = (TextView) view.findViewById(R.id.keyId); +        TextView vKeyDetails = (TextView) view.findViewById(R.id.keyDetails); +        TextView vKeyExpiry = (TextView) view.findViewById(R.id.keyExpiry); +        ImageView vMasterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey); +        ImageView vCertifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey); +        ImageView vEncryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey); +        ImageView vSignIcon = (ImageView) view.findViewById(R.id.ic_signKey); +        ImageView vRevokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey); +        ImageView vEditImage = (ImageView) view.findViewById(R.id.edit_image); + +        long keyId = cursor.getLong(INDEX_KEY_ID); +        String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId);          String algorithmStr = PgpKeyHelper.getAlgorithmInfo(                  context,                  cursor.getInt(INDEX_ALGORITHM),                  cursor.getInt(INDEX_KEY_SIZE)          ); -        keyId.setText(keyIdStr); +        vKeyId.setText(keyIdStr);          // may be set with additional "stripped" later on          if (hasAnySecret && cursor.getInt(INDEX_HAS_SECRET) == 0) { -            keyDetails.setText(algorithmStr + ", " + +            vKeyDetails.setText(algorithmStr + ", " +                      context.getString(R.string.key_stripped));          } else { -            keyDetails.setText(algorithmStr); +            vKeyDetails.setText(algorithmStr);          }          // Set icons according to properties -        masterKeyIcon.setVisibility(cursor.getInt(INDEX_RANK) == 0 ? View.VISIBLE : View.INVISIBLE); -        certifyIcon.setVisibility(cursor.getInt(INDEX_CAN_CERTIFY) != 0 ? View.VISIBLE : View.GONE); -        encryptIcon.setVisibility(cursor.getInt(INDEX_CAN_ENCRYPT) != 0 ? View.VISIBLE : View.GONE); -        signIcon.setVisibility(cursor.getInt(INDEX_CAN_SIGN) != 0 ? View.VISIBLE : View.GONE); +        vMasterKeyIcon.setVisibility(cursor.getInt(INDEX_RANK) == 0 ? View.VISIBLE : View.INVISIBLE); +        vCertifyIcon.setVisibility(cursor.getInt(INDEX_CAN_CERTIFY) != 0 ? View.VISIBLE : View.GONE); +        vEncryptIcon.setVisibility(cursor.getInt(INDEX_CAN_ENCRYPT) != 0 ? View.VISIBLE : View.GONE); +        vSignIcon.setVisibility(cursor.getInt(INDEX_CAN_SIGN) != 0 ? View.VISIBLE : View.GONE); + +        boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; + +        // for edit key +        if (mSaveKeyringParcel != null) { +            boolean revokeThisSubkey = (mSaveKeyringParcel.mRevokeSubKeys.contains(keyId)); -        boolean valid = true; -        if (cursor.getInt(INDEX_IS_REVOKED) > 0) { -            revokedKeyIcon.setVisibility(View.VISIBLE); +            if (revokeThisSubkey) { +                if (!isRevoked) { +                    isRevoked = true; +                } +            } -            valid = false; +            vEditImage.setVisibility(View.VISIBLE);          } else { -            keyId.setTextColor(mDefaultTextColor); -            keyDetails.setTextColor(mDefaultTextColor); -            keyExpiry.setTextColor(mDefaultTextColor); +            vEditImage.setVisibility(View.GONE); +        } -            revokedKeyIcon.setVisibility(View.GONE); +        if (isRevoked) { +            vRevokedKeyIcon.setVisibility(View.VISIBLE); +        } else { +            vKeyId.setTextColor(mDefaultTextColor); +            vKeyDetails.setTextColor(mDefaultTextColor); +            vKeyExpiry.setTextColor(mDefaultTextColor); + +            vRevokedKeyIcon.setVisibility(View.GONE);          } +        boolean isExpired;          if (!cursor.isNull(INDEX_EXPIRY)) {              Date expiryDate = new Date(cursor.getLong(INDEX_EXPIRY) * 1000); +            isExpired = expiryDate.before(new Date()); -            valid = valid && expiryDate.after(new Date()); -            keyExpiry.setText( -                    context.getString(R.string.label_expiry) + ": " + -                            DateFormat.getDateFormat(context).format(expiryDate) -            ); +            vKeyExpiry.setText(context.getString(R.string.label_expiry) + ": " +                    + DateFormat.getDateFormat(context).format(expiryDate));          } else { -            keyExpiry.setText( -                    context.getString(R.string.label_expiry) + ": " + -                            context.getString(R.string.none) -            ); +            isExpired = false; + +            vKeyExpiry.setText(context.getString(R.string.label_expiry) + ": " + context.getString(R.string.none));          }          // if key is expired or revoked, strike through text -        if (!valid) { -            keyId.setText(OtherHelper.strikeOutText(keyId.getText())); -            keyDetails.setText(OtherHelper.strikeOutText(keyDetails.getText())); -            keyExpiry.setText(OtherHelper.strikeOutText(keyExpiry.getText())); +        boolean isInvalid = isRevoked || isExpired; +        if (isInvalid) { +            vKeyId.setText(OtherHelper.strikeOutText(vKeyId.getText())); +            vKeyDetails.setText(OtherHelper.strikeOutText(vKeyDetails.getText())); +            vKeyExpiry.setText(OtherHelper.strikeOutText(vKeyExpiry.getText()));          } -        keyId.setEnabled(valid); -        keyDetails.setEnabled(valid); -        keyExpiry.setEnabled(valid); +        vKeyId.setEnabled(!isInvalid); +        vKeyDetails.setEnabled(!isInvalid); +        vKeyExpiry.setEnabled(!isInvalid);      }      @Override      public View newView(Context context, Cursor cursor, ViewGroup parent) { -        View view = mInflater.inflate(R.layout.view_key_keys_item, null); +        View view = mInflater.inflate(R.layout.view_key_subkey_item, null);          if (mDefaultTextColor == null) {              TextView keyId = (TextView) view.findViewById(R.id.keyId);              mDefaultTextColor = keyId.getTextColors(); @@ -177,13 +204,21 @@ public class SubkeysAdapter extends CursorAdapter {      // Disable selection of items, http://stackoverflow.com/a/4075045      @Override      public boolean areAllItemsEnabled() { -        return false; +        if (mSaveKeyringParcel == null) { +            return false; +        } else { +            return super.areAllItemsEnabled(); +        }      }      // Disable selection of items, http://stackoverflow.com/a/4075045      @Override      public boolean isEnabled(int position) { -        return false; +        if (mSaveKeyringParcel == null) { +            return false; +        } else { +            return super.isEnabled(position); +        }      }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java index d729648e5..c8b74da2e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java @@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.ui.adapter;  import android.content.Context;  import android.database.Cursor; +import android.graphics.PorterDuff; +import android.graphics.Typeface;  import android.support.v4.widget.CursorAdapter;  import android.view.LayoutInflater;  import android.view.View; @@ -40,9 +42,7 @@ import java.util.ArrayList;  public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemClickListener {      private LayoutInflater mInflater; -      private final ArrayList<Boolean> mCheckStates; -      private SaveKeyringParcel mSaveKeyringParcel;      public static final String[] USER_IDS_PROJECTION = new String[]{ @@ -60,7 +60,6 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC      private static final int INDEX_IS_PRIMARY = 4;      private static final int INDEX_IS_REVOKED = 5; -      public UserIdsAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes,                            SaveKeyringParcel saveKeyringParcel) {          super(context, c, flags); @@ -134,15 +133,18 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC          // for edit key          if (mSaveKeyringParcel != null) { -            boolean changeAnyPrimaryUserId = (mSaveKeyringParcel.changePrimaryUserId != null); -            boolean changeThisPrimaryUserId = (mSaveKeyringParcel.changePrimaryUserId != null -                    && mSaveKeyringParcel.changePrimaryUserId.equals(userId)); -            boolean revokeThisUserId = (mSaveKeyringParcel.revokeUserIds.contains(userId)); +            boolean changeAnyPrimaryUserId = (mSaveKeyringParcel.mChangePrimaryUserId != null); +            boolean changeThisPrimaryUserId = (mSaveKeyringParcel.mChangePrimaryUserId != null +                    && mSaveKeyringParcel.mChangePrimaryUserId.equals(userId)); +            boolean revokeThisUserId = (mSaveKeyringParcel.mRevokeUserIds.contains(userId)); +            // only if primary user id will be changed +            // (this is not triggered if the user id is currently the primary one)              if (changeAnyPrimaryUserId) { -                // change all user ids, only this one should be primary +                // change _all_ primary user ids and set new one to true                  isPrimary = changeThisPrimaryUserId;              } +              if (revokeThisUserId) {                  if (!isRevoked) {                      isRevoked = true; @@ -156,7 +158,10 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC          if (isRevoked) {              // set revocation icon (can this even be primary?) -            vVerified.setImageResource(R.drawable.key_certify_revoke); +            vVerified.setImageResource(R.drawable.status_signature_revoked_cutout); +            vVerified.setColorFilter( +                    mContext.getResources().getColor(R.color.bg_gray), +                    PorterDuff.Mode.SRC_IN);              // disable and strike through text for revoked user ids              vName.setEnabled(false); @@ -170,22 +175,33 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC              vAddress.setEnabled(true);              vComment.setEnabled(true); -            // verified: has been verified -            // isPrimary: show small star icon for primary user ids -            int verified = cursor.getInt(INDEX_VERIFIED); -            switch (verified) { +            if (isPrimary) { +                vName.setTypeface(null, Typeface.BOLD); +                vAddress.setTypeface(null, Typeface.BOLD); +            } else { +                vName.setTypeface(null, Typeface.NORMAL); +                vAddress.setTypeface(null, Typeface.NORMAL); +            } + +            int isVerified = cursor.getInt(INDEX_VERIFIED); +            switch (isVerified) {                  case Certs.VERIFIED_SECRET: -                    vVerified.setImageResource(isPrimary -                            ? R.drawable.key_certify_primary_ok_depth0 -                            : R.drawable.key_certify_ok_depth0); +                    vVerified.setImageResource(R.drawable.status_signature_verified_cutout); +                    vVerified.setColorFilter( +                            mContext.getResources().getColor(R.color.android_green_light), +                            PorterDuff.Mode.SRC_IN);                      break;                  case Certs.VERIFIED_SELF: -                    vVerified.setImageResource(isPrimary -                            ? R.drawable.key_certify_primary_ok_self -                            : R.drawable.key_certify_ok_self); +                    vVerified.setImageResource(R.drawable.status_signature_unverified_cutout); +                    vVerified.setColorFilter( +                            mContext.getResources().getColor(R.color.bg_gray), +                            PorterDuff.Mode.SRC_IN);                      break;                  default: -                    vVerified.setImageResource(R.drawable.key_certify_error); +                    vVerified.setImageResource(R.drawable.status_signature_invalid_cutout); +                    vVerified.setColorFilter( +                            mContext.getResources().getColor(R.color.android_red_light), +                            PorterDuff.Mode.SRC_IN);                      break;              }          } @@ -233,7 +249,7 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC      @Override      public View newView(Context context, Cursor cursor, ViewGroup parent) { -        View view = mInflater.inflate(R.layout.view_key_userids_item, null); +        View view = mInflater.inflate(R.layout.view_key_user_id_item, null);          // only need to do this once ever, since mShowCheckBoxes is final          view.findViewById(R.id.checkBox).setVisibility(mCheckStates != null ? View.VISIBLE : View.GONE);          return view; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ChangeExpiryDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ChangeExpiryDialogFragment.java new file mode 100644 index 000000000..d5354a9f6 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ChangeExpiryDialogFragment.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.ui.dialog; + +import android.app.DatePickerDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.support.v4.app.DialogFragment; +import android.text.format.DateUtils; +import android.widget.DatePicker; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +public class ChangeExpiryDialogFragment extends DialogFragment { +    private static final String ARG_MESSENGER = "messenger"; +    private static final String ARG_CREATION_DATE = "creation_date"; +    private static final String ARG_EXPIRY_DATE = "expiry_date"; + +    public static final int MESSAGE_NEW_EXPIRY_DATE = 1; +    public static final String MESSAGE_DATA_EXPIRY_DATE = "expiry_date"; + +    private Messenger mMessenger; +    private Calendar mCreationCal; +    private Calendar mExpiryCal; + +    private int mDatePickerResultCount = 0; +    private DatePickerDialog.OnDateSetListener mExpiryDateSetListener = +            new DatePickerDialog.OnDateSetListener() { +                public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { +                    // Note: Ignore results after the first one - android sends multiples. +                    if (mDatePickerResultCount++ == 0) { +                        Calendar selectedCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); +                        selectedCal.set(year, monthOfYear, dayOfMonth); +                        if (mExpiryCal != null) { +                            long numDays = (selectedCal.getTimeInMillis() / 86400000) +                                    - (mExpiryCal.getTimeInMillis() / 86400000); +                            if (numDays > 0) { +                                Bundle data = new Bundle(); +                                data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, selectedCal.getTime()); +                                sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data); +                            } +                        } else { +                            Bundle data = new Bundle(); +                            data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, selectedCal.getTime()); +                            sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data); +                        } +                    } +                } +            }; + +    public class ExpiryDatePickerDialog extends DatePickerDialog { + +        public ExpiryDatePickerDialog(Context context, OnDateSetListener callBack, +                                      int year, int monthOfYear, int dayOfMonth) { +            super(context, callBack, year, monthOfYear, dayOfMonth); +        } + +        // set permanent title +        public void setTitle(CharSequence title) { +            super.setTitle(getContext().getString(R.string.expiry_date_dialog_title)); +        } +    } + +    /** +     * Creates new instance of this dialog fragment +     */ +    public static ChangeExpiryDialogFragment newInstance(Messenger messenger, +                                                         Date creationDate, Date expiryDate) { +        ChangeExpiryDialogFragment frag = new ChangeExpiryDialogFragment(); +        Bundle args = new Bundle(); +        args.putParcelable(ARG_MESSENGER, messenger); +        args.putSerializable(ARG_CREATION_DATE, creationDate); +        args.putSerializable(ARG_EXPIRY_DATE, expiryDate); + +        frag.setArguments(args); + +        return frag; +    } + +    /** +     * Creates dialog +     */ +    @Override +    public Dialog onCreateDialog(Bundle savedInstanceState) { +        mMessenger = getArguments().getParcelable(ARG_MESSENGER); +        Date creationDate = (Date) getArguments().getSerializable(ARG_CREATION_DATE); +        Date expiryDate = (Date) getArguments().getSerializable(ARG_EXPIRY_DATE); + +        mCreationCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); +        mCreationCal.setTime(creationDate); +        mExpiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); +        mExpiryCal.setTime(expiryDate); + +        /* +         * Using custom DatePickerDialog which overrides the setTitle because +         * the DatePickerDialog title is buggy (unix warparound bug). +         * See: https://code.google.com/p/android/issues/detail?id=49066 +         */ +        DatePickerDialog dialog = new ExpiryDatePickerDialog(getActivity(), +                mExpiryDateSetListener, mExpiryCal.get(Calendar.YEAR), mExpiryCal.get(Calendar.MONTH), +                mExpiryCal.get(Calendar.DAY_OF_MONTH)); +        mDatePickerResultCount = 0; +        dialog.setCancelable(true); +        dialog.setButton(Dialog.BUTTON_NEGATIVE, +                getActivity().getString(R.string.btn_no_date), +                new DialogInterface.OnClickListener() { +                    public void onClick(DialogInterface dialog, int which) { +                        // Note: Ignore results after the first one - android sends multiples. +                        if (mDatePickerResultCount++ == 0) { +                            // none expiry dates corresponds to a null message +                            Bundle data = new Bundle(); +                            data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, null); +                            sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data); +                        } +                    } +                } +        ); + +        // setCalendarViewShown() is supported from API 11 onwards. +        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) { +            // Hide calendarView in tablets because of the unix warparound bug. +            dialog.getDatePicker().setCalendarViewShown(false); +        } +        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) { +            // will crash with IllegalArgumentException if we set a min date +            // that is not before expiry +            if (mCreationCal != null && mCreationCal.before(mExpiryCal)) { +                dialog.getDatePicker().setMinDate(mCreationCal.getTime().getTime() +                        + DateUtils.DAY_IN_MILLIS); +            } else { +                // When created date isn't available +                dialog.getDatePicker().setMinDate(mExpiryCal.getTime().getTime() +                        + DateUtils.DAY_IN_MILLIS); +            } +        } + +        return dialog; +    } + +    /** +     * Send message back to handler which is initialized in a activity +     * +     * @param what Message integer you want to send +     */ +    private void sendMessageToHandler(Integer what, Bundle data) { +        Message msg = Message.obtain(); +        msg.what = what; +        if (data != null) { +            msg.setData(data); +        } + +        try { +            mMessenger.send(msg); +        } catch (RemoteException e) { +            Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); +        } catch (NullPointerException e) { +            Log.w(Constants.TAG, "Messenger is null!", e); +        } +    } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java new file mode 100644 index 000000000..9fef88a78 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.ui.dialog; + +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.support.v4.app.DialogFragment; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.Log; + +public class EditSubkeyDialogFragment extends DialogFragment { +    private static final String ARG_MESSENGER = "messenger"; + +    public static final int MESSAGE_CHANGE_EXPIRY = 1; +    public static final int MESSAGE_REVOKE = 2; + +    private Messenger mMessenger; + +    /** +     * Creates new instance of this dialog fragment +     */ +    public static EditSubkeyDialogFragment newInstance(Messenger messenger) { +        EditSubkeyDialogFragment frag = new EditSubkeyDialogFragment(); +        Bundle args = new Bundle(); +        args.putParcelable(ARG_MESSENGER, messenger); + +        frag.setArguments(args); + +        return frag; +    } + +    /** +     * Creates dialog +     */ +    @Override +    public Dialog onCreateDialog(Bundle savedInstanceState) { +        mMessenger = getArguments().getParcelable(ARG_MESSENGER); + +        CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(getActivity()); +        CharSequence[] array = getResources().getStringArray(R.array.edit_key_edit_subkey); + +        builder.setTitle(R.string.edit_key_edit_subkey_title); +        builder.setItems(array, new DialogInterface.OnClickListener() { + +            @Override +            public void onClick(DialogInterface dialog, int which) { +                switch (which) { +                    case 0: +                        sendMessageToHandler(MESSAGE_CHANGE_EXPIRY, null); +                        break; +                    case 1: +                        sendMessageToHandler(MESSAGE_REVOKE, null); +                        break; +                    default: +                        break; +                } +            } +        }); +        builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { +            @Override +            public void onClick(DialogInterface dialog, int id) { +                dismiss(); +            } +        }); + +        return builder.show(); +    } + +    /** +     * Send message back to handler which is initialized in a activity +     * +     * @param what Message integer you want to send +     */ +    private void sendMessageToHandler(Integer what, Bundle data) { +        Message msg = Message.obtain(); +        msg.what = what; +        if (data != null) { +            msg.setData(data); +        } + +        try { +            mMessenger.send(msg); +        } catch (RemoteException e) { +            Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); +        } catch (NullPointerException e) { +            Log.w(Constants.TAG, "Messenger is null!", e); +        } +    } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java index 59e4d8dee..ef2659cf8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java @@ -33,8 +33,8 @@ import android.support.v4.app.FragmentActivity;  import android.view.KeyEvent;  import android.view.LayoutInflater;  import android.view.View; -import android.view.WindowManager.LayoutParams;  import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager;  import android.widget.Button;  import android.widget.EditText;  import android.widget.TextView; @@ -44,8 +44,8 @@ import android.widget.Toast;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; -import org.sufficientlysecure.keychain.pgp.WrappedSecretKey; -import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.service.PassphraseCacheService; @@ -62,7 +62,6 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor      private Messenger mMessenger;      private EditText mPassphraseEditText; -    private boolean mCanKB;      /**       * Shows passphrase dialog to cache a new passphrase the user enters for using it later for @@ -102,10 +101,10 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor          // check if secret key has a passphrase          if (!(secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none)) {              try { -                if (!new ProviderHelper(context).getWrappedSecretKeyRing(secretKeyId).hasPassphrase()) { +                if (!new ProviderHelper(context).getCanonicalizedSecretKeyRing(secretKeyId).hasPassphrase()) {                      throw new PgpGeneralException("No passphrase! No passphrase dialog needed!");                  } -            } catch(ProviderHelper.NotFoundException e) { +            } catch (ProviderHelper.NotFoundException e) {                  throw new PgpGeneralException("Error: Key not found!", e);              }          } @@ -120,11 +119,6 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor          return frag;      } -    @Override -    public void onCreate(Bundle savedInstanceState) { -        super.onCreate(savedInstanceState); -    } -      /**       * Creates dialog       */ @@ -138,7 +132,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor          alert.setTitle(R.string.title_authentication); -        final WrappedSecretKeyRing secretRing; +        final CanonicalizedSecretKeyRing secretRing;          String userId;          if (secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none) { @@ -147,12 +141,12 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor          } else {              try {                  ProviderHelper helper = new ProviderHelper(activity); -                secretRing = helper.getWrappedSecretKeyRing(secretKeyId); +                secretRing = helper.getCanonicalizedSecretKeyRing(secretKeyId);                  // yes the inner try/catch block is necessary, otherwise the final variable                  // above can't be statically verified to have been set in all cases because                  // the catch clause doesn't return.                  try { -                    userId = secretRing.getPrimaryUserId(); +                    userId = secretRing.getPrimaryUserIdWithFallback();                  } catch (PgpGeneralException e) {                      userId = null;                  } @@ -165,7 +159,6 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor                      }                  });                  alert.setCancelable(false); -                mCanKB = false;                  return alert.create();              } @@ -189,7 +182,8 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor                  // Early breakout if we are dealing with a symmetric key                  if (secretRing == null) { -                    PassphraseCacheService.addCachedPassphrase(activity, Constants.key.symmetric, passphrase); +                    PassphraseCacheService.addCachedPassphrase(activity, Constants.key.symmetric, +                            passphrase, getString(R.string.passp_cache_notif_pwd));                      // also return passphrase back to activity                      Bundle data = new Bundle();                      data.putString(MESSAGE_DATA_PASSPHRASE, passphrase); @@ -197,9 +191,9 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor                      return;                  } -                WrappedSecretKey unlockedSecretKey = null; +                CanonicalizedSecretKey unlockedSecretKey = null; -                for(WrappedSecretKey clickSecretKey : secretRing.secretKeyIterator()) { +                for (CanonicalizedSecretKey clickSecretKey : secretRing.secretKeyIterator()) {                      try {                          boolean unlocked = clickSecretKey.unlock(passphrase);                          if (unlocked) { @@ -228,10 +222,18 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor                  // cache the new passphrase                  Log.d(Constants.TAG, "Everything okay! Caching entered passphrase"); -                PassphraseCacheService.addCachedPassphrase(activity, masterKeyId, passphrase); + +                try { +                    PassphraseCacheService.addCachedPassphrase(activity, masterKeyId, passphrase, +                            secretRing.getPrimaryUserIdWithFallback()); +                } catch (PgpGeneralException e) { +                    Log.e(Constants.TAG, "adding of a passphrase failed", e); +                } +                  if (unlockedSecretKey.getKeyId() != masterKeyId) {                      PassphraseCacheService.addCachedPassphrase( -                            activity, unlockedSecretKey.getKeyId(), passphrase); +                            activity, unlockedSecretKey.getKeyId(), passphrase, +                            unlockedSecretKey.getPrimaryUserIdWithFallback());                  }                  // also return passphrase back to activity @@ -249,20 +251,30 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor              }          }); -        mCanKB = true; -        return alert.show(); -    } +        // Hack to open keyboard. +        // This is the only method that I found to work across all Android versions +        // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/ +        // Notes: * onCreateView can't be used because we want to add buttons to the dialog +        //        * opening in onActivityCreated does not work on Android 4.4 +        mPassphraseEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() { +            @Override +            public void onFocusChange(View v, boolean hasFocus) { +                mPassphraseEditText.post(new Runnable() { +                    @Override +                    public void run() { +                        InputMethodManager imm = (InputMethodManager) getActivity() +                                .getSystemService(Context.INPUT_METHOD_SERVICE); +                        imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT); +                    } +                }); +            } +        }); +        mPassphraseEditText.requestFocus(); -    @Override -    public void onActivityCreated(Bundle arg0) { -        super.onActivityCreated(arg0); -        if (mCanKB) { -            // request focus and open soft keyboard -            mPassphraseEditText.requestFocus(); -            getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); - -            mPassphraseEditText.setOnEditorActionListener(this); -        } +        mPassphraseEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE); +        mPassphraseEditText.setOnEditorActionListener(this); + +        return alert.show();      }      @Override @@ -273,6 +285,27 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor          sendMessageToHandler(MESSAGE_CANCEL);      } +    @Override +    public void onDismiss(DialogInterface dialog) { +        super.onDismiss(dialog); +        Log.d(Constants.TAG, "onDismiss"); + +        // hide keyboard on dismiss +        hideKeyboard(); +    } + +    private void hideKeyboard() { +        InputMethodManager inputManager = (InputMethodManager) getActivity() +                .getSystemService(Context.INPUT_METHOD_SERVICE); + +        //check if no view has focus: +        View v = getActivity().getCurrentFocus(); +        if (v == null) +            return; + +        inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0); +    } +      /**       * Associate the "done" button on the soft keyboard with the okay button in the view       */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java index 93da48b75..1386ed098 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java @@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui.dialog;  import android.app.Activity;  import android.app.AlertDialog;  import android.app.Dialog; +import android.content.Context;  import android.content.DialogInterface;  import android.os.Bundle;  import android.os.Message; @@ -32,6 +33,7 @@ import android.view.LayoutInflater;  import android.view.View;  import android.view.WindowManager.LayoutParams;  import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager;  import android.widget.Button;  import android.widget.CheckBox;  import android.widget.CompoundButton; @@ -164,18 +166,50 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi              }          }); +        // Hack to open keyboard. +        // This is the only method that I found to work across all Android versions +        // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/ +        // Notes: * onCreateView can't be used because we want to add buttons to the dialog +        //        * opening in onActivityCreated does not work on Android 4.4 +        mPassphraseEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() { +            @Override +            public void onFocusChange(View v, boolean hasFocus) { +                mPassphraseEditText.post(new Runnable() { +                    @Override +                    public void run() { +                        InputMethodManager imm = (InputMethodManager) getActivity() +                                .getSystemService(Context.INPUT_METHOD_SERVICE); +                        imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT); +                    } +                }); +            } +        }); +        mPassphraseEditText.requestFocus(); + +        mPassphraseAgainEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE); +        mPassphraseAgainEditText.setOnEditorActionListener(this); +          return alert.show();      }      @Override -    public void onActivityCreated(Bundle arg0) { -        super.onActivityCreated(arg0); +    public void onDismiss(DialogInterface dialog) { +        super.onDismiss(dialog); -        // request focus and open soft keyboard -        mPassphraseEditText.requestFocus(); -        getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); +        // hide keyboard on dismiss +        hideKeyboard(); +    } -        mPassphraseAgainEditText.setOnEditorActionListener(this); +    private void hideKeyboard() { +        InputMethodManager inputManager = (InputMethodManager) getActivity() +                .getSystemService(Context.INPUT_METHOD_SERVICE); + +        //check if no view has focus: +        View v = getActivity().getCurrentFocus(); +        if (v == null) +            return; + +        inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0);      }      /** diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java deleted file mode 100644 index 24608784b..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program.  If not, see <http://www.gnu.org/licenses/>. - */ - -package org.sufficientlysecure.keychain.ui.dialog; - -import android.app.Activity; -import android.app.Dialog; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.app.DialogFragment; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import com.devspark.appmsg.AppMsg; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.QrCodeUtils; - -public class ShareQrCodeDialogFragment extends DialogFragment { -    private static final String ARG_KEY_URI = "uri"; - -    private ImageView mImage; -    private TextView mText; - -    private static final int QR_CODE_SIZE = 1000; - -    /** -     * Creates new instance of this dialog fragment -     */ -    public static ShareQrCodeDialogFragment newInstance(Uri dataUri) { -        ShareQrCodeDialogFragment frag = new ShareQrCodeDialogFragment(); -        Bundle args = new Bundle(); -        args.putParcelable(ARG_KEY_URI, dataUri); - -        frag.setArguments(args); - -        return frag; -    } - -    /** -     * Creates dialog -     */ -    @Override -    public Dialog onCreateDialog(Bundle savedInstanceState) { -        final Activity activity = getActivity(); - -        Uri dataUri = getArguments().getParcelable(ARG_KEY_URI); - -        CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(getActivity()); -        alert.setTitle(R.string.share_qr_code_dialog_title); - -        LayoutInflater inflater = activity.getLayoutInflater(); -        View view = inflater.inflate(R.layout.share_qr_code_dialog, null); -        alert.setView(view); - -        mImage = (ImageView) view.findViewById(R.id.share_qr_code_dialog_image); -        mText = (TextView) view.findViewById(R.id.share_qr_code_dialog_text); - -        ProviderHelper providerHelper = new ProviderHelper(getActivity()); -        String content; -        try { -            alert.setPositiveButton(R.string.btn_okay, null); - -            byte[] blob = (byte[]) providerHelper.getGenericData( -                    KeyRings.buildUnifiedKeyRingUri(dataUri), -                    KeyRings.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB); -            if (blob == null) { -                Log.e(Constants.TAG, "key not found!"); -                AppMsg.makeText(getActivity(), R.string.error_key_not_found, AppMsg.STYLE_ALERT).show(); -                return null; -            } - -            String fingerprint = PgpKeyHelper.convertFingerprintToHex(blob); -            mText.setText(getString(R.string.share_qr_code_dialog_fingerprint_text) + " " + fingerprint); -            content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint; -            setQrCode(content); -        } catch (ProviderHelper.NotFoundException e) { -            Log.e(Constants.TAG, "key not found!", e); -            AppMsg.makeText(getActivity(), R.string.error_key_not_found, AppMsg.STYLE_ALERT).show(); -            return null; -        } - -        return alert.show(); -    } - -    private void setQrCode(String data) { -        mImage.setImageBitmap(QrCodeUtils.getQRCodeBitmap(data, QR_CODE_SIZE)); -    } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java index 67f81fb24..22e3f5d66 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java @@ -29,7 +29,7 @@ import com.github.johnpersano.supertoasts.SuperToast;   */  public class Notify { -    public static enum Style {OK, WARN, ERROR} +    public static enum Style {OK, WARN, INFO, ERROR}      /**       * Shows a simple in-layout notification with the CharSequence given as parameter diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java index 5553ea5d2..95a259336 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java @@ -39,15 +39,21 @@ public class ProgressScaler implements Progressable {       * Set progress of ProgressDialog by sending message to handler on UI thread       */      public void setProgress(String message, int progress, int max) { -        mWrapped.setProgress(message, mFrom + progress * (mTo - mFrom) / max, mMax); +        if (mWrapped != null) { +            mWrapped.setProgress(message, mFrom + progress * (mTo - mFrom) / max, mMax); +        }      }      public void setProgress(int resourceId, int progress, int max) { -        mWrapped.setProgress(resourceId, progress, mMax); +        if (mWrapped != null) { +            mWrapped.setProgress(resourceId, mFrom + progress * (mTo - mFrom) / max, mMax); +        }      }      public void setProgress(int progress, int max) { -        mWrapped.setProgress(progress, max); +        if (mWrapped != null) { +            mWrapped.setProgress(mFrom + progress * (mTo - mFrom) / max, mMax); +        }      }  } diff --git a/OpenKeychain/src/main/res/anim/frag_slide_in_from_left.xml b/OpenKeychain/src/main/res/anim/frag_slide_in_from_left.xml new file mode 100644 index 000000000..4a021c676 --- /dev/null +++ b/OpenKeychain/src/main/res/anim/frag_slide_in_from_left.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<set> +    <translate xmlns:android="http://schemas.android.com/apk/res/android" +        android:fromXDelta="-100%" +        android:toXDelta="0" +        android:interpolator="@android:anim/decelerate_interpolator" +        android:duration="500" /> +</set>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/anim/frag_slide_in_from_right.xml b/OpenKeychain/src/main/res/anim/frag_slide_in_from_right.xml new file mode 100644 index 000000000..1329f8bef --- /dev/null +++ b/OpenKeychain/src/main/res/anim/frag_slide_in_from_right.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<set> +    <translate xmlns:android="http://schemas.android.com/apk/res/android" +        android:fromXDelta="100%" +        android:toXDelta="0" +        android:interpolator="@android:anim/decelerate_interpolator" +        android:duration="500" /> +</set>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/anim/frag_slide_out_to_left.xml b/OpenKeychain/src/main/res/anim/frag_slide_out_to_left.xml new file mode 100644 index 000000000..4e02af2a4 --- /dev/null +++ b/OpenKeychain/src/main/res/anim/frag_slide_out_to_left.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<set> +    <translate xmlns:android="http://schemas.android.com/apk/res/android" +        android:fromXDelta="0" +        android:toXDelta="-100%" +        android:interpolator="@android:anim/decelerate_interpolator" +        android:duration="500" /> +</set>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/anim/frag_slide_out_to_right.xml b/OpenKeychain/src/main/res/anim/frag_slide_out_to_right.xml new file mode 100644 index 000000000..7b5f63e0d --- /dev/null +++ b/OpenKeychain/src/main/res/anim/frag_slide_out_to_right.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<set> +    <translate xmlns:android="http://schemas.android.com/apk/res/android" +        android:fromXDelta="0" +        android:toXDelta="100%" +        android:interpolator="@android:anim/decelerate_interpolator" +        android:duration="500" /> +</set>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/anim/qr_code_zoom_enter.xml b/OpenKeychain/src/main/res/anim/qr_code_zoom_enter.xml new file mode 100644 index 000000000..2b95cfba6 --- /dev/null +++ b/OpenKeychain/src/main/res/anim/qr_code_zoom_enter.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android" +    android:interpolator="@android:anim/decelerate_interpolator"> +    <scale +        android:fromXScale="0.5" +        android:toXScale="1.0" +        android:fromYScale="0.5" +        android:toYScale="1.0" +        android:pivotX="50%p" +        android:pivotY="50%p" +        android:duration="@android:integer/config_mediumAnimTime" /> +    <alpha +        android:fromAlpha="0" +        android:toAlpha="1.0" +        android:duration="@android:integer/config_mediumAnimTime" /> +</set>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/anim/qr_code_zoom_exit.xml b/OpenKeychain/src/main/res/anim/qr_code_zoom_exit.xml new file mode 100644 index 000000000..772375739 --- /dev/null +++ b/OpenKeychain/src/main/res/anim/qr_code_zoom_exit.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android" +    android:interpolator="@android:anim/decelerate_interpolator" +    android:zAdjustment="top"> +    <scale +        android:fromXScale="1.0" +        android:toXScale="0.5" +        android:fromYScale="1.0" +        android:toYScale="0.5" +        android:pivotX="50%p" +        android:pivotY="50%p" +        android:duration="@android:integer/config_mediumAnimTime" /> +    <alpha +        android:fromAlpha="1.0" +        android:toAlpha="0" +        android:duration="@android:integer/config_mediumAnimTime" /> +</set>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/drawable-hdpi/create_key_robot.png b/OpenKeychain/src/main/res/drawable-hdpi/create_key_robot.png Binary files differnew file mode 100644 index 000000000..acc198fc3 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/create_key_robot.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_error.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_error.png Binary files differdeleted file mode 100644 index 391d1c988..000000000 --- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_error.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_depth0.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_depth0.png Binary files differdeleted file mode 100644 index 76944469c..000000000 --- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_depth0.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_self.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_self.png Binary files differdeleted file mode 100644 index 815701015..000000000 --- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_self.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_depth0.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_depth0.png Binary files differdeleted file mode 100644 index 026869c14..000000000 --- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_depth0.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_self.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_self.png Binary files differdeleted file mode 100644 index 12d2e026e..000000000 --- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_self.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_revoke.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_revoke.png Binary files differdeleted file mode 100644 index c39d3a87c..000000000 --- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_revoke.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_lock_closed.png b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_closed.png Binary files differnew file mode 100644 index 000000000..a1b090630 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_closed.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_lock_error.png b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_error.png Binary files differnew file mode 100644 index 000000000..e567055aa --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_error.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_lock_open.png b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_open.png Binary files differnew file mode 100644 index 000000000..98e32eadc --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_open.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired.png Binary files differnew file mode 100644 index 000000000..21e8b536a --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired_cutout.png Binary files differnew file mode 100644 index 000000000..84ac9bec2 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid.png Binary files differnew file mode 100644 index 000000000..9ae2a09ab --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid_cutout.png Binary files differnew file mode 100644 index 000000000..967e00e80 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked.png Binary files differnew file mode 100644 index 000000000..16e1d7181 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked_cutout.png Binary files differnew file mode 100644 index 000000000..244dd0708 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown.png Binary files differnew file mode 100644 index 000000000..5c3ba866d --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown_cutout.png Binary files differnew file mode 100644 index 000000000..82cc25a4b --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified.png Binary files differnew file mode 100644 index 000000000..b8b472a5a --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified_cutout.png Binary files differnew file mode 100644 index 000000000..e752eaeab --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified.png Binary files differnew file mode 100644 index 000000000..d8141b47b --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified_cutout.png Binary files differnew file mode 100644 index 000000000..08a9f464c --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_error.png b/OpenKeychain/src/main/res/drawable-ldpi/key_certify_error.png Binary files differdeleted file mode 100644 index 79fddf78a..000000000 --- a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_error.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_depth0.png b/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_depth0.png Binary files differdeleted file mode 100644 index c400a1820..000000000 --- a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_depth0.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_self.png b/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_self.png Binary files differdeleted file mode 100644 index fb1654b53..000000000 --- a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_self.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_revoke.png b/OpenKeychain/src/main/res/drawable-ldpi/key_certify_revoke.png Binary files differdeleted file mode 100644 index 5228a4b6b..000000000 --- a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_revoke.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-mdpi/create_key_robot.png b/OpenKeychain/src/main/res/drawable-mdpi/create_key_robot.png Binary files differnew file mode 100644 index 000000000..58476bfc9 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/create_key_robot.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_error.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_error.png Binary files differdeleted file mode 100644 index 6def8769f..000000000 --- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_error.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_depth0.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_depth0.png Binary files differdeleted file mode 100644 index e16ec810a..000000000 --- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_depth0.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_self.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_self.png Binary files differdeleted file mode 100644 index 715a16487..000000000 --- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_self.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_depth0.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_depth0.png Binary files differdeleted file mode 100644 index c376a2897..000000000 --- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_depth0.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_self.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_self.png Binary files differdeleted file mode 100644 index 45a261b24..000000000 --- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_self.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_revoke.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_revoke.png Binary files differdeleted file mode 100644 index 62ee0ca42..000000000 --- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_revoke.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_lock_closed.png b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_closed.png Binary files differnew file mode 100644 index 000000000..cfc39f0e7 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_closed.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_lock_error.png b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_error.png Binary files differnew file mode 100644 index 000000000..824dc2672 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_error.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_lock_open.png b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_open.png Binary files differnew file mode 100644 index 000000000..9bca59ae3 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_open.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired.png Binary files differnew file mode 100644 index 000000000..81a900147 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired_cutout.png Binary files differnew file mode 100644 index 000000000..bc91094b5 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid.png Binary files differnew file mode 100644 index 000000000..baa78f795 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid_cutout.png Binary files differnew file mode 100644 index 000000000..bc2f56e2a --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked.png Binary files differnew file mode 100644 index 000000000..7cf985274 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked_cutout.png Binary files differnew file mode 100644 index 000000000..2d2593194 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown.png Binary files differnew file mode 100644 index 000000000..3d4665320 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown_cutout.png Binary files differnew file mode 100644 index 000000000..0fc74d07e --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified.png Binary files differnew file mode 100644 index 000000000..8348b32b3 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified_cutout.png Binary files differnew file mode 100644 index 000000000..96a2d1413 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified.png Binary files differnew file mode 100644 index 000000000..02e53ac8a --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified_cutout.png Binary files differnew file mode 100644 index 000000000..9f7cf837c --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/create_key_robot.png b/OpenKeychain/src/main/res/drawable-xhdpi/create_key_robot.png Binary files differnew file mode 100644 index 000000000..022f2dd2e --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/create_key_robot.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_error.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_error.png Binary files differdeleted file mode 100644 index 8278ce2b6..000000000 --- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_error.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_depth0.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_depth0.png Binary files differdeleted file mode 100644 index e2aef1177..000000000 --- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_depth0.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_self.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_self.png Binary files differdeleted file mode 100644 index 9bb6ceffa..000000000 --- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_self.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_depth0.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_depth0.png Binary files differdeleted file mode 100644 index de6614246..000000000 --- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_depth0.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_self.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_self.png Binary files differdeleted file mode 100644 index ce10da099..000000000 --- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_self.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_revoke.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_revoke.png Binary files differdeleted file mode 100644 index 1478e726b..000000000 --- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_revoke.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_closed.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_closed.png Binary files differnew file mode 100644 index 000000000..7c6bb2d18 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_closed.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_error.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_error.png Binary files differnew file mode 100644 index 000000000..da4a5d89a --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_error.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_open.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_open.png Binary files differnew file mode 100644 index 000000000..cd02fc1e4 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_open.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired.png Binary files differnew file mode 100644 index 000000000..f5105c1ae --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired_cutout.png Binary files differnew file mode 100644 index 000000000..83f6fde35 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid.png Binary files differnew file mode 100644 index 000000000..67880d6db --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid_cutout.png Binary files differnew file mode 100644 index 000000000..29830f5ba --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked.png Binary files differnew file mode 100644 index 000000000..2ed67419b --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked_cutout.png Binary files differnew file mode 100644 index 000000000..2f7695043 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown.png Binary files differnew file mode 100644 index 000000000..a6f1f2792 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown_cutout.png Binary files differnew file mode 100644 index 000000000..2ce28c7ca --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified.png Binary files differnew file mode 100644 index 000000000..c25a84b4d --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified_cutout.png Binary files differnew file mode 100644 index 000000000..442c55eee --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified.png Binary files differnew file mode 100644 index 000000000..6f435a85e --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified_cutout.png Binary files differnew file mode 100644 index 000000000..160ec7cbe --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/create_key_robot.png b/OpenKeychain/src/main/res/drawable-xxhdpi/create_key_robot.png Binary files differnew file mode 100644 index 000000000..5392deafd --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/create_key_robot.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_error.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_error.png Binary files differdeleted file mode 100644 index 9416720eb..000000000 --- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_error.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_depth0.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_depth0.png Binary files differdeleted file mode 100644 index 501a75d63..000000000 --- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_depth0.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_self.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_self.png Binary files differdeleted file mode 100644 index 72ada9c1f..000000000 --- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_self.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_depth0.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_depth0.png Binary files differdeleted file mode 100644 index 1b52ef04d..000000000 --- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_depth0.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_self.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_self.png Binary files differdeleted file mode 100644 index baa1c00d2..000000000 --- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_self.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_revoke.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_revoke.png Binary files differdeleted file mode 100644 index 217f4e914..000000000 --- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_revoke.png +++ /dev/null diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_closed.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_closed.png Binary files differnew file mode 100644 index 000000000..5a9664d59 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_closed.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_error.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_error.png Binary files differnew file mode 100644 index 000000000..608f065af --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_error.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_open.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_open.png Binary files differnew file mode 100644 index 000000000..ee34dd396 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_open.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired.png Binary files differnew file mode 100644 index 000000000..f475c9d84 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired_cutout.png Binary files differnew file mode 100644 index 000000000..33a3efed1 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid.png Binary files differnew file mode 100644 index 000000000..f21c2cf52 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid_cutout.png Binary files differnew file mode 100644 index 000000000..bc39d3496 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked.png Binary files differnew file mode 100644 index 000000000..be1a1d9dc --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked_cutout.png Binary files differnew file mode 100644 index 000000000..58929661f --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown.png Binary files differnew file mode 100644 index 000000000..841cfa958 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown_cutout.png Binary files differnew file mode 100644 index 000000000..3020357a4 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified.png Binary files differnew file mode 100644 index 000000000..525d1cf6b --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified_cutout.png Binary files differnew file mode 100644 index 000000000..3829bb3a0 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified_cutout.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified.png Binary files differnew file mode 100644 index 000000000..54eee5ba0 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified_cutout.png Binary files differnew file mode 100644 index 000000000..3548ee2b6 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified_cutout.png diff --git a/OpenKeychain/src/main/res/drawable/first_time_1.png b/OpenKeychain/src/main/res/drawable/first_time_1.png Binary files differnew file mode 100644 index 000000000..1f340df5c --- /dev/null +++ b/OpenKeychain/src/main/res/drawable/first_time_1.png diff --git a/OpenKeychain/src/main/res/layout/api_account_settings_activity.xml b/OpenKeychain/src/main/res/layout/api_account_settings_activity.xml index 3557c1f00..b2a9c11f5 100644 --- a/OpenKeychain/src/main/res/layout/api_account_settings_activity.xml +++ b/OpenKeychain/src/main/res/layout/api_account_settings_activity.xml @@ -1,20 +1,28 @@  <?xml version="1.0" encoding="utf-8"?> -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      xmlns:tools="http://schemas.android.com/tools" +    android:orientation="vertical"      android:layout_width="match_parent"      android:layout_height="match_parent"> -    <LinearLayout +    <include layout="@layout/notify_area" /> + +    <ScrollView          android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:padding="16dp" -        android:orientation="vertical"> +        android:layout_height="match_parent"> -        <fragment -            android:id="@+id/api_account_settings_fragment" -            android:name="org.sufficientlysecure.keychain.remote.ui.AccountSettingsFragment" +        <LinearLayout              android:layout_width="match_parent" -            android:layout_height="wrap_content" /> +            android:layout_height="wrap_content" +            android:padding="16dp" +            android:orientation="vertical"> + +            <fragment +                android:id="@+id/api_account_settings_fragment" +                android:name="org.sufficientlysecure.keychain.remote.ui.AccountSettingsFragment" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" /> -    </LinearLayout> -</ScrollView> +        </LinearLayout> +    </ScrollView> +</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml b/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml index 49c4ccbfe..1c09820a9 100644 --- a/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml +++ b/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml @@ -1,33 +1,41 @@  <?xml version="1.0" encoding="utf-8"?> -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      xmlns:tools="http://schemas.android.com/tools" +    android:orientation="vertical"      android:layout_width="match_parent"      android:layout_height="match_parent"> -    <LinearLayout +    <include layout="@layout/notify_area" /> + +    <ScrollView          android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:padding="16dp" -        android:orientation="vertical"> +        android:layout_height="match_parent"> -        <fragment -            android:id="@+id/api_app_settings_fragment" -            android:name="org.sufficientlysecure.keychain.remote.ui.AppSettingsFragment" +        <LinearLayout              android:layout_width="match_parent"              android:layout_height="wrap_content" -            tools:layout="@layout/api_app_settings_fragment" /> +            android:padding="16dp" +            android:orientation="vertical"> -        <TextView -            android:layout_width="match_parent" -            android:layout_height="wrap_content" -            android:text="@string/api_settings_accounts" -            android:textAppearance="?android:attr/textAppearanceMedium" /> +            <fragment +                android:id="@+id/api_app_settings_fragment" +                android:name="org.sufficientlysecure.keychain.remote.ui.AppSettingsFragment" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                tools:layout="@layout/api_app_settings_fragment" /> -        <FrameLayout -            android:id="@+id/api_accounts_list_fragment" -            android:layout_width="match_parent" -            android:layout_height="match_parent" -            android:orientation="vertical" /> +            <TextView +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:text="@string/api_settings_accounts" +                android:textAppearance="?android:attr/textAppearanceMedium" /> + +            <FrameLayout +                android:id="@+id/api_accounts_list_fragment" +                android:layout_width="match_parent" +                android:layout_height="match_parent" +                android:orientation="vertical" /> -    </LinearLayout> -</ScrollView> +        </LinearLayout> +    </ScrollView> +</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/certify_key_activity.xml b/OpenKeychain/src/main/res/layout/certify_key_activity.xml index bb43fa805..34d4dbd57 100644 --- a/OpenKeychain/src/main/res/layout/certify_key_activity.xml +++ b/OpenKeychain/src/main/res/layout/certify_key_activity.xml @@ -1,171 +1,192 @@  <?xml version="1.0" encoding="UTF-8"?> -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" -    xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      xmlns:tools="http://schemas.android.com/tools" -    android:layout_width="wrap_content" +    android:orientation="vertical" +    android:layout_width="match_parent"      android:layout_height="match_parent"> -    <LinearLayout -        android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:paddingLeft="16dp" -        android:paddingRight="16dp" -        android:orientation="vertical"> +    <include layout="@layout/notify_area" /> -        <TextView -            style="@style/SectionHeader" -            android:layout_width="wrap_content" -            android:layout_height="wrap_content" -            android:layout_marginBottom="4dp" -            android:layout_marginTop="14dp" -            android:text="@string/section_certification_key" /> +    <ScrollView +        android:layout_width="wrap_content" +        android:layout_height="match_parent"> -        <fragment -            android:id="@+id/sign_key_select_key_fragment" -            android:name="org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment" +        <LinearLayout              android:layout_width="match_parent"              android:layout_height="wrap_content" -            android:layout_marginBottom="4dp" -            android:layout_marginTop="4dp" -            tools:layout="@layout/select_secret_key_layout_fragment" /> - -        <TextView -            style="@style/SectionHeader" -            android:layout_width="wrap_content" -            android:layout_height="wrap_content" -            android:layout_marginBottom="4dp" -            android:layout_marginTop="14dp" -            android:text="@string/section_key_to_certify" /> - -        <TableLayout -            android:layout_width="wrap_content" -            android:layout_height="0dp" -            android:layout_weight="1" -            android:layout_marginLeft="8dp" -            android:shrinkColumns="1"> - -            <TableRow -                android:layout_width="fill_parent" -                android:layout_height="fill_parent"> - -                <TextView -                    android:layout_width="wrap_content" -                    android:layout_height="wrap_content" -                    android:layout_gravity="center_vertical" -                    android:paddingRight="10dip" -                    android:text="@string/label_key_id" /> - -                <TextView -                    android:id="@+id/key_id" -                    android:layout_width="wrap_content" -                    android:layout_height="wrap_content" -                    android:paddingRight="5dip" -                    android:text="" -                    android:typeface="monospace" /> -            </TableRow> - -            <TableRow -                android:layout_width="fill_parent" -                android:layout_height="fill_parent"> - -                <TextView -                    android:layout_width="wrap_content" -                    android:layout_height="wrap_content" -                    android:layout_gravity="center_vertical" -                    android:paddingRight="10dip" -                    android:text="@string/label_main_user_id" /> - -                <TextView -                    android:id="@+id/main_user_id" -                    android:layout_width="match_parent" -                    android:layout_height="wrap_content" /> - -            </TableRow> - -            <TableRow -                android:layout_width="fill_parent" -                android:layout_height="fill_parent"> +            android:paddingLeft="16dp" +            android:paddingRight="16dp" +            android:orientation="vertical"> + +            <TextView +                style="@style/SectionHeader" +                android:layout_width="wrap_content" +                android:layout_height="wrap_content" +                android:layout_marginBottom="4dp" +                android:layout_marginTop="14dp" +                android:text="@string/section_certification_key" /> + +            <fragment +                android:id="@+id/sign_key_select_key_fragment" +                android:name="org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:layout_marginBottom="4dp" +                android:layout_marginTop="4dp" +                tools:layout="@layout/select_secret_key_layout_fragment" /> + +            <TextView +                style="@style/SectionHeader" +                android:layout_width="wrap_content" +                android:layout_height="wrap_content" +                android:layout_marginBottom="4dp" +                android:layout_marginTop="14dp" +                android:text="@string/section_key_to_certify" /> + +            <TableLayout +                android:layout_width="wrap_content" +                android:layout_height="0dp" +                android:layout_weight="1" +                android:layout_marginLeft="8dp" +                android:shrinkColumns="1"> + +                <TableRow +                    android:layout_width="fill_parent" +                    android:layout_height="fill_parent"> + +                    <TextView +                        android:layout_width="wrap_content" +                        android:layout_height="wrap_content" +                        android:layout_gravity="center_vertical" +                        android:paddingRight="10dip" +                        android:text="@string/label_key_id" /> + +                    <TextView +                        android:id="@+id/key_id" +                        android:layout_width="wrap_content" +                        android:layout_height="wrap_content" +                        android:paddingRight="5dip" +                        android:text="" +                        android:typeface="monospace" /> +                </TableRow> + +                <TableRow +                    android:layout_width="fill_parent" +                    android:layout_height="fill_parent"> + +                    <TextView +                        android:layout_width="wrap_content" +                        android:layout_height="wrap_content" +                        android:layout_gravity="center_vertical" +                        android:paddingRight="10dip" +                        android:text="@string/label_main_user_id" /> + +                    <TextView +                        android:id="@+id/main_user_id" +                        android:layout_width="match_parent" +                        android:layout_height="wrap_content" /> + +                </TableRow> + +                <TableRow +                    android:layout_width="fill_parent" +                    android:layout_height="fill_parent"> + +                    <TextView +                        android:layout_width="wrap_content" +                        android:layout_height="wrap_content" +                        android:layout_gravity="center_vertical" +                        android:paddingRight="10dip" +                        android:text="@string/label_fingerprint" /> + +                    <TextView +                        android:id="@+id/view_key_fingerprint" +                        android:layout_width="match_parent" +                        android:layout_height="wrap_content" +                        android:typeface="monospace" /> + +                </TableRow> + +            </TableLayout> + +            <TextView +                style="@style/SectionHeader" +                android:layout_width="wrap_content" +                android:layout_height="wrap_content" +                android:layout_marginTop="14dp" +                android:text="@string/section_uids_to_certify" /> + +            <org.sufficientlysecure.keychain.ui.widget.FixedListView +                android:id="@+id/view_key_user_ids" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:descendantFocusability="blocksDescendants" /> + +            <TextView +                style="@style/SectionHeader" +                android:layout_width="wrap_content" +                android:layout_height="wrap_content" +                android:layout_marginBottom="4dp" +                android:layout_marginTop="14dp" +                android:text="@string/section_upload_key" /> + +            <CheckBox +                android:id="@+id/sign_key_upload_checkbox" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:layout_marginBottom="4dp" +                android:layout_marginTop="4dp" +                android:checked="false" +                android:text="@string/label_send_key" /> + +            <Spinner +                android:id="@+id/upload_key_keyserver" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:layout_marginBottom="4dp" +                android:layout_marginTop="4dp" +                android:enabled="false" /> + +            <TextView +                style="@style/SectionHeader" +                android:layout_width="wrap_content" +                android:layout_height="0dp" +                android:layout_marginTop="14dp" +                android:text="@string/section_actions" +                android:layout_weight="1" /> + +            <LinearLayout +                android:id="@+id/certify_key_certify_button" +                android:layout_width="match_parent" +                android:layout_height="?android:attr/listPreferredItemHeight" +                android:clickable="true" +                android:paddingRight="4dp" +                android:layout_marginBottom="8dp" +                style="@style/SelectableItem" +                android:orientation="horizontal">                  <TextView +                    android:paddingLeft="8dp" +                    android:textAppearance="?android:attr/textAppearanceMedium" +                    android:layout_width="0dip" +                    android:layout_height="match_parent" +                    android:text="@string/key_view_action_certify" +                    android:layout_weight="1" +                    android:gravity="center_vertical" /> + +                <ImageView +                    android:id="@+id/certify_key_action_certify_image"                      android:layout_width="wrap_content" -                    android:layout_height="wrap_content" -                    android:layout_gravity="center_vertical" -                    android:paddingRight="10dip" -                    android:text="@string/label_fingerprint" /> - -                <TextView -                    android:id="@+id/view_key_fingerprint" -                    android:layout_width="match_parent" -                    android:layout_height="wrap_content" -                    android:typeface="monospace" /> - -            </TableRow> +                    android:layout_height="match_parent" +                    android:padding="8dp" +                    android:src="@drawable/status_signature_verified_cutout" +                    android:layout_gravity="center_vertical" /> -        </TableLayout> - -        <TextView -            style="@style/SectionHeader" -            android:layout_width="wrap_content" -            android:layout_height="wrap_content" -            android:layout_marginBottom="4dp" -            android:layout_marginTop="14dp" -            android:text="@string/section_uids_to_certify" /> +            </LinearLayout> -        <org.sufficientlysecure.keychain.ui.widget.FixedListView -            android:id="@+id/view_key_user_ids" -            android:layout_width="match_parent" -            android:layout_height="wrap_content" -            android:descendantFocusability="blocksDescendants" /> -        <TextView -            style="@style/SectionHeader" -            android:layout_width="wrap_content" -            android:layout_height="wrap_content" -            android:layout_marginBottom="4dp" -            android:layout_marginTop="14dp" -            android:text="@string/section_upload_key" /> +        </LinearLayout> -        <CheckBox -            android:id="@+id/sign_key_upload_checkbox" -            android:layout_width="match_parent" -            android:layout_height="wrap_content" -            android:layout_marginBottom="4dp" -            android:layout_marginTop="4dp" -            android:checked="false" -            android:text="@string/label_send_key" /> +    </ScrollView> -        <Spinner -            android:id="@+id/upload_key_keyserver" -            android:layout_width="match_parent" -            android:layout_height="wrap_content" -            android:layout_marginBottom="4dp" -            android:layout_marginTop="4dp" -            android:enabled="false" /> - -        <TextView -            style="@style/SectionHeader" -            android:layout_width="wrap_content" -            android:layout_height="0dp" -            android:layout_marginTop="14dp" -            android:text="@string/section_actions" -            android:layout_weight="1" /> - -        <TextView -            android:id="@+id/sign_key_sign_button" -            android:paddingLeft="8dp" -            android:paddingRight="8dp" -            android:layout_marginBottom="8dp" -            android:textAppearance="?android:attr/textAppearanceMedium" -            android:layout_width="match_parent" -            android:layout_height="match_parent" -            android:text="@string/key_view_action_certify" -            android:minHeight="?android:attr/listPreferredItemHeight" -            android:drawableRight="@drawable/ic_action_good" -            android:drawablePadding="8dp" -            android:gravity="center_vertical" -            android:clickable="true" -            style="@style/SelectableItem" /> - -    </LinearLayout> - -</ScrollView>
\ No newline at end of file +</LinearLayout> diff --git a/OpenKeychain/src/main/res/layout/edit_key_activity_new.xml b/OpenKeychain/src/main/res/layout/create_key_activity.xml index f96b993c5..0bd053c49 100644 --- a/OpenKeychain/src/main/res/layout/edit_key_activity_new.xml +++ b/OpenKeychain/src/main/res/layout/create_key_activity.xml @@ -4,8 +4,10 @@      android:layout_width="match_parent"      android:layout_height="match_parent"> +    <include layout="@layout/notify_area" /> +      <FrameLayout -        android:id="@+id/edit_key_fragment_container" +        android:id="@+id/create_key_fragment_container"          android:layout_width="match_parent"          android:layout_height="match_parent"          android:orientation="vertical" /> diff --git a/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml new file mode 100644 index 000000000..64b3621a4 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml @@ -0,0 +1,154 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:layout_width="wrap_content" +    android:layout_height="match_parent"> + +    <ScrollView +        android:layout_width="match_parent" +        android:layout_height="match_parent" +        android:layout_above="@+id/create_key_buttons_divider"> + +        <LinearLayout +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:paddingLeft="16dp" +            android:paddingRight="16dp" +            android:orientation="vertical"> + +            <TextView +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:paddingTop="16dp" +                android:paddingBottom="8dp" +                android:text="@string/create_key_final_text" +                android:textAppearance="?android:attr/textAppearanceMedium" /> + +            <TextView +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:layout_marginBottom="2dp" +                android:text="@string/label_name" +                android:textColor="@color/tertiary_text_light" +                android:textAppearance="?android:attr/textAppearanceSmall" /> + +            <TextView +                android:id="@+id/name" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:layout_marginBottom="8dp" +                android:layout_marginLeft="8dp" +                android:text="Max Mustermann" +                android:textAppearance="?android:attr/textAppearanceMedium" /> + +            <TextView +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:layout_marginBottom="2dp" +                android:text="@string/label_email" +                android:textColor="@color/tertiary_text_light" +                android:textAppearance="?android:attr/textAppearanceSmall" /> + +            <TextView +                android:id="@+id/email" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:text="max@musterman.com" +                android:textAppearance="?android:attr/textAppearanceMedium" +                android:layout_marginBottom="8dp" +                android:layout_marginLeft="8dp" /> + +            <View +                android:layout_width="match_parent" +                android:layout_height="1dip" +                android:background="?android:attr/listDivider" /> + +            <CheckBox +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:layout_marginTop="8dp" +                android:layout_marginBottom="8dp" +                android:textAppearance="?android:attr/textAppearanceMedium" +                android:text="@string/create_key_upload" +                android:id="@+id/create_key_upload" /> + +            <View +                android:layout_width="match_parent" +                android:layout_height="1dip" +                android:background="?android:attr/listDivider" /> + +            <TextView +                android:layout_width="wrap_content" +                android:layout_height="wrap_content" +                android:layout_marginTop="16dp" +                android:layout_marginBottom="8dp" +                android:text="@string/create_key_final_robot_text" +                android:textColor="@color/android_green_dark" +                android:textAppearance="?android:attr/textAppearanceMedium" +                android:drawableLeft="@drawable/create_key_robot" +                android:drawablePadding="8dp" /> + +        </LinearLayout> +    </ScrollView> + +    <LinearLayout +        android:layout_width="match_parent" +        android:layout_height="wrap_content" +        android:orientation="horizontal" +        android:layout_alignParentBottom="true" +        android:layout_alignParentLeft="true" +        android:layout_alignParentStart="true" +        android:layout_marginLeft="16dp" +        android:layout_marginRight="16dp" +        android:id="@+id/create_key_buttons"> + +        <TextView +            android:id="@+id/create_key_back_button" +            android:paddingLeft="8dp" +            android:paddingRight="8dp" +            android:textAppearance="?android:attr/textAppearanceMedium" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:layout_weight="1" +            android:text="@string/btn_back" +            android:minHeight="?android:attr/listPreferredItemHeight" +            android:gravity="center_vertical" +            android:clickable="true" +            style="@style/SelectableItem" +            android:layout_gravity="center_vertical" /> + +        <View +            android:layout_width="1dp" +            android:layout_height="match_parent" +            android:layout_marginTop="8dp" +            android:layout_marginBottom="8dp" +            android:background="?android:attr/listDivider" /> + +        <TextView +            android:id="@+id/create_key_create_button" +            android:paddingLeft="8dp" +            android:paddingRight="8dp" +            android:textAppearance="?android:attr/textAppearanceMedium" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:layout_weight="1" +            android:text="@string/title_create_key" +            android:minHeight="?android:attr/listPreferredItemHeight" +            android:drawableRight="@drawable/ic_action_new_account" +            android:drawablePadding="8dp" +            android:gravity="center_vertical" +            android:clickable="true" +            style="@style/SelectableItem" +            android:layout_gravity="center_vertical" /> +    </LinearLayout> + +    <View +        android:id="@+id/create_key_buttons_divider" +        android:layout_width="match_parent" +        android:layout_height="1dip" +        android:background="?android:attr/listDivider" +        android:layout_alignTop="@+id/create_key_buttons" +        android:layout_marginLeft="16dp" +        android:layout_marginRight="16dp" +        android:layout_alignParentLeft="true" +        android:layout_alignParentStart="true" /> +</RelativeLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/create_key_input_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_input_fragment.xml new file mode 100644 index 000000000..30ccc05ab --- /dev/null +++ b/OpenKeychain/src/main/res/layout/create_key_input_fragment.xml @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:layout_width="wrap_content" +    android:layout_height="match_parent"> + +    <ScrollView +        android:layout_width="match_parent" +        android:layout_height="match_parent" +        android:fillViewport="false" +        android:layout_above="@+id/create_key_button_divider"> + +        <LinearLayout +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:paddingLeft="16dp" +            android:paddingRight="16dp" +            android:orientation="vertical"> + +            <TextView +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:paddingTop="16dp" +                android:paddingBottom="8dp" +                android:text="@string/create_key_text" +                android:textAppearance="?android:attr/textAppearanceMedium" /> + +            <AutoCompleteTextView +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:layout_marginBottom="8dp" +                android:imeOptions="actionNext" +                android:inputType="textPersonName" +                android:hint="@string/label_name" +                android:ems="10" +                android:id="@+id/name" /> + +            <AutoCompleteTextView +                android:id="@+id/email" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:layout_marginBottom="8dp" +                android:imeOptions="actionNext" +                android:hint="@string/label_email" +                android:ems="10" +                android:inputType="textEmailAddress" /> + +            <EditText +                android:id="@+id/passphrase" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:layout_marginBottom="8dp" +                android:inputType="textPassword" +                android:hint="@string/label_passphrase" +                android:ems="10" +                android:layout_gravity="center_horizontal" /> + +            <EditText +                android:id="@+id/passphrase_again" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:layout_marginBottom="8dp" +                android:inputType="textPassword" +                android:hint="@string/label_passphrase_again" +                android:ems="10" +                android:layout_gravity="center_horizontal" /> + +        </LinearLayout> +    </ScrollView> + +    <View +        android:id="@+id/create_key_button_divider" +        android:layout_width="match_parent" +        android:layout_height="1dip" +        android:layout_marginLeft="16dp" +        android:layout_marginRight="16dp" +        android:background="?android:attr/listDivider" +        android:layout_alignTop="@+id/create_key_buttons" +        android:layout_alignParentLeft="true" +        android:layout_alignParentStart="true" /> + +    <LinearLayout +        android:layout_width="match_parent" +        android:layout_height="wrap_content" +        android:orientation="horizontal" +        android:layout_alignParentBottom="true" +        android:layout_alignParentLeft="true" +        android:layout_alignParentStart="true" +        android:layout_marginLeft="16dp" +        android:layout_marginRight="16dp" +        android:id="@+id/create_key_buttons"> + +        <TextView +            android:id="@+id/create_key_back_button" +            android:paddingLeft="8dp" +            android:paddingRight="8dp" +            android:textAppearance="?android:attr/textAppearanceMedium" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:layout_weight="1" +            android:text="" +            android:minHeight="?android:attr/listPreferredItemHeight" +            android:gravity="center_vertical" +            android:layout_gravity="center_vertical" /> + +        <View +            android:layout_width="1dp" +            android:layout_height="match_parent" +            android:layout_marginTop="8dp" +            android:layout_marginBottom="8dp" +            android:background="?android:attr/listDivider" /> + +        <TextView +            android:id="@+id/create_key_button" +            android:paddingLeft="8dp" +            android:paddingRight="8dp" +            android:textAppearance="?android:attr/textAppearanceMedium" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:layout_weight="1" +            android:text="@string/title_create_key" +            android:minHeight="?android:attr/listPreferredItemHeight" +            android:drawableRight="@drawable/ic_action_new_account" +            android:drawablePadding="8dp" +            android:gravity="center_vertical" +            android:clickable="true" +            style="@style/SelectableItem" +            android:layout_gravity="center_vertical" /> +    </LinearLayout> +</RelativeLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/decrypt_result_include.xml b/OpenKeychain/src/main/res/layout/decrypt_result_include.xml index 05877656b..fcad91df3 100644 --- a/OpenKeychain/src/main/res/layout/decrypt_result_include.xml +++ b/OpenKeychain/src/main/res/layout/decrypt_result_include.xml @@ -8,7 +8,7 @@      android:paddingRight="16dp"      android:paddingTop="4dp"      android:paddingBottom="4dp" -    android:background="@color/result_purple"> +    android:background="@color/android_purple_light">      <View          android:layout_width="match_parent" diff --git a/OpenKeychain/src/main/res/layout/edit_key_activity.xml b/OpenKeychain/src/main/res/layout/edit_key_activity.xml index b6c5a1c9a..7e71ccf53 100644 --- a/OpenKeychain/src/main/res/layout/edit_key_activity.xml +++ b/OpenKeychain/src/main/res/layout/edit_key_activity.xml @@ -1,46 +1,15 @@  <?xml version="1.0" encoding="utf-8"?> -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" -    xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" -    android:layout_width="fill_parent" -    android:layout_height="fill_parent" -    android:fillViewport="true" -    android:orientation="vertical" > +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:orientation="vertical" +    android:layout_width="match_parent" +    android:layout_height="match_parent"> -    <LinearLayout -        android:layout_width="fill_parent" -        android:layout_height="wrap_content" -        android:orientation="vertical" -        android:paddingLeft="16dp" -        android:paddingRight="16dp" > +    <include layout="@layout/notify_area"/> -        <TextView -            style="@style/SectionHeader" -            android:layout_width="wrap_content" -            android:layout_height="wrap_content" -            android:padding="4dp" -            android:text="@string/label_passphrase" /> +    <FrameLayout +        android:id="@+id/edit_key_fragment_container" +        android:layout_width="match_parent" +        android:layout_height="match_parent" +        android:orientation="vertical" /> -        <CheckBox -            android:id="@+id/edit_key_no_passphrase" -            android:layout_width="wrap_content" -            android:layout_height="wrap_content" -            android:text="@string/label_no_passphrase" /> - -        <Button -            android:id="@+id/edit_key_btn_change_passphrase" -            android:layout_width="match_parent" -            android:layout_height="60dp" -            android:padding="4dp" -            android:text="@string/btn_set_passphrase" -            android:drawableLeft="@drawable/ic_action_edit" -            android:background="@drawable/button_edgy" /> - -        <LinearLayout -            android:id="@+id/edit_key_container" -            android:layout_width="fill_parent" -            android:layout_height="wrap_content" -            android:orientation="vertical" > -        </LinearLayout> -    </LinearLayout> - -</ScrollView>
\ No newline at end of file +</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/edit_key_subkey_added_item.xml b/OpenKeychain/src/main/res/layout/edit_key_subkey_added_item.xml index 856bef36a..a4258b998 100644 --- a/OpenKeychain/src/main/res/layout/edit_key_subkey_added_item.xml +++ b/OpenKeychain/src/main/res/layout/edit_key_subkey_added_item.xml @@ -9,7 +9,7 @@          android:layout_width="wrap_content"          android:layout_height="match_parent"          android:minWidth="10dp" -        android:background="@color/result_green" /> +        android:background="@color/android_green_light" />      <TableLayout          android:layout_width="0dp" diff --git a/OpenKeychain/src/main/res/layout/edit_key_user_id_added_item.xml b/OpenKeychain/src/main/res/layout/edit_key_user_id_added_item.xml index e69452db1..ef0e2626e 100644 --- a/OpenKeychain/src/main/res/layout/edit_key_user_id_added_item.xml +++ b/OpenKeychain/src/main/res/layout/edit_key_user_id_added_item.xml @@ -10,7 +10,7 @@          android:layout_width="wrap_content"          android:layout_height="match_parent"          android:minWidth="10dp" -        android:background="@color/result_green" /> +        android:background="@color/android_green_light" />      <LinearLayout          android:orientation="vertical" diff --git a/OpenKeychain/src/main/res/layout/first_time_activity.xml b/OpenKeychain/src/main/res/layout/first_time_activity.xml new file mode 100644 index 000000000..fad1a3b26 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/first_time_activity.xml @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:layout_width="wrap_content" +    android:layout_height="wrap_content" +    android:paddingTop="16dp"> + +    <LinearLayout +        android:id="@+id/first_time_buttons" +        android:layout_width="match_parent" +        android:layout_height="wrap_content" +        android:paddingLeft="16dp" +        android:paddingRight="16dp" +        android:layout_alignParentBottom="true" +        android:orientation="vertical"> + +        <View +            android:layout_width="match_parent" +            android:layout_height="1dip" +            android:background="?android:attr/listDivider" /> + +        <LinearLayout +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:orientation="horizontal"> + +            <TextView +                android:id="@+id/first_time_import_key" +                android:paddingLeft="8dp" +                android:paddingRight="8dp" +                android:textAppearance="?android:attr/textAppearanceMedium" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:layout_weight="1" +                android:text="@string/first_time_import_key" +                android:minHeight="?android:attr/listPreferredItemHeight" +                android:drawableRight="@drawable/ic_action_download" +                android:drawablePadding="8dp" +                android:gravity="center_vertical" +                android:clickable="true" +                style="@style/SelectableItem" /> + +            <View +                android:layout_width="1dp" +                android:layout_height="match_parent" +                android:layout_marginTop="8dp" +                android:layout_marginBottom="8dp" +                android:background="?android:attr/listDivider" /> + +            <TextView +                android:id="@+id/first_time_create_key" +                android:paddingLeft="8dp" +                android:paddingRight="8dp" +                android:textAppearance="?android:attr/textAppearanceMedium" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:layout_weight="1" +                android:text="@string/first_time_create_key" +                android:minHeight="?android:attr/listPreferredItemHeight" +                android:drawableRight="@drawable/ic_action_new_account" +                android:drawablePadding="8dp" +                android:gravity="center_vertical" +                android:clickable="true" +                style="@style/SelectableItem" /> + +        </LinearLayout> + +        <View +            android:layout_width="match_parent" +            android:layout_height="1dip" +            android:background="?android:attr/listDivider" /> + +        <TextView +            android:id="@+id/first_time_cancel" +            android:paddingLeft="8dp" +            android:paddingRight="8dp" +            android:textAppearance="?android:attr/textAppearanceMedium" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:text="@string/first_time_skip" +            android:minHeight="?android:attr/listPreferredItemHeight" +            android:gravity="center" +            android:clickable="true" +            style="@style/SelectableItem" +            android:layout_gravity="center_horizontal" /> + +    </LinearLayout> + +    <LinearLayout +        android:layout_width="match_parent" +        android:layout_height="match_parent" +        android:orientation="vertical" +        android:layout_above="@+id/first_time_buttons"> + +        <TextView +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:textAppearance="?android:attr/textAppearanceLarge" +            android:text="@string/app_name" +            android:drawableLeft="@drawable/ic_launcher" +            android:drawablePadding="16dp" +            android:gravity="center" +            android:layout_gravity="center_horizontal" /> + +        <ImageView +            android:layout_width="wrap_content" +            android:layout_height="0dp" +            android:layout_marginLeft="16dp" +            android:layout_marginRight="16dp" +            android:layout_marginTop="16dp" +            android:layout_marginBottom="16dp" +            android:adjustViewBounds="true" +            android:src="@drawable/first_time_1" +            android:layout_gravity="center_horizontal" +            android:layout_weight="1" /> + +        <TextView +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:layout_marginLeft="64dp" +            android:layout_marginRight="64dp" +            android:textAppearance="?android:attr/textAppearanceMedium" +            android:text="@string/first_time_text1" +            android:layout_gravity="center_horizontal" +            android:gravity="center_horizontal" +            android:layout_marginBottom="16dp" /> + +    </LinearLayout> + + +</RelativeLayout> diff --git a/OpenKeychain/src/main/res/layout/key_list_activity.xml b/OpenKeychain/src/main/res/layout/key_list_activity.xml index c4c2c35cb..297fc526e 100644 --- a/OpenKeychain/src/main/res/layout/key_list_activity.xml +++ b/OpenKeychain/src/main/res/layout/key_list_activity.xml @@ -2,9 +2,18 @@  <android.support.v4.widget.FixedDrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"      android:id="@+id/drawer_layout"      android:layout_width="match_parent" -    android:layout_height="match_parent" > +    android:layout_height="match_parent"> -    <include layout="@layout/key_list_content"/> +    <LinearLayout +        android:orientation="vertical" +        android:layout_width="match_parent" +        android:layout_height="match_parent"> + +        <include layout="@layout/notify_area" /> + +        <include layout="@layout/key_list_content" /> + +    </LinearLayout>      <include layout="@layout/drawer_list" /> diff --git a/OpenKeychain/src/main/res/layout/key_list_item.xml b/OpenKeychain/src/main/res/layout/key_list_item.xml index 99e4c0268..8d600464c 100644 --- a/OpenKeychain/src/main/res/layout/key_list_item.xml +++ b/OpenKeychain/src/main/res/layout/key_list_item.xml @@ -38,30 +38,12 @@              android:textAppearance="?android:attr/textAppearanceSmall" />      </LinearLayout> -    <FrameLayout -        android:id="@+id/status_layout" +    <ImageView          android:layout_width="wrap_content" -        android:layout_height="match_parent"> - -        <TextView -            android:id="@+id/revoked" -            android:layout_width="wrap_content" -            android:layout_height="wrap_content" -            android:singleLine="true" -            android:ellipsize="end" -            android:textAppearance="?android:attr/textAppearanceSmall" -            android:text="@string/revoked" -            android:textColor="#e00" -            android:layout_gravity="center" -            android:padding="12dp" /> - -        <ImageView -            android:layout_width="wrap_content" -            android:layout_height="wrap_content" -            android:id="@+id/verified" -            android:layout_gravity="center" -            android:src="@drawable/key_certify_ok_depth0" -            android:padding="16dp" /> -    </FrameLayout> +        android:layout_height="wrap_content" +        android:id="@+id/status_image" +        android:layout_gravity="center" +        android:src="@drawable/status_signature_revoked_cutout" +        android:padding="16dp" />  </LinearLayout> diff --git a/OpenKeychain/src/main/res/layout/notify_area.xml b/OpenKeychain/src/main/res/layout/notify_area.xml index d1ba265a5..43df1cde8 100644 --- a/OpenKeychain/src/main/res/layout/notify_area.xml +++ b/OpenKeychain/src/main/res/layout/notify_area.xml @@ -1,12 +1,10 @@  <?xml version="1.0" encoding="utf-8"?> -  <merge xmlns:android="http://schemas.android.com/apk/res/android">      <LinearLayout          android:id="@+id/card_container"          android:layout_width="match_parent"          android:layout_height="wrap_content" -        android:background="@color/emphasis"          android:orientation="vertical" />  </merge>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/qr_code_activity.xml b/OpenKeychain/src/main/res/layout/qr_code_activity.xml new file mode 100644 index 000000000..57c869db6 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/qr_code_activity.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:layout_width="match_parent" +    android:layout_height="match_parent" +    android:orientation="vertical"> + +    <ImageView +        android:id="@+id/qr_code_image" +        android:padding="32dp" +        android:layout_width="match_parent" +        android:layout_height="match_parent" +        style="@style/SelectableItem" /> + +</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/share_qr_code_dialog.xml b/OpenKeychain/src/main/res/layout/share_qr_code_dialog.xml deleted file mode 100644 index 0b58ae72f..000000000 --- a/OpenKeychain/src/main/res/layout/share_qr_code_dialog.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" -    android:layout_width="match_parent" -    android:layout_height="wrap_content" -    android:orientation="vertical" > - -    <TextView -        android:id="@+id/share_qr_code_dialog_text" -        android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:padding="8dp" -        android:textAppearance="@android:style/TextAppearance.Medium" /> - -    <ImageView -        android:id="@+id/share_qr_code_dialog_image" -        android:layout_width="match_parent" -        android:layout_height="wrap_content" /> - -</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/upload_key_activity.xml b/OpenKeychain/src/main/res/layout/upload_key_activity.xml index 5a6f732d5..736617ba5 100644 --- a/OpenKeychain/src/main/res/layout/upload_key_activity.xml +++ b/OpenKeychain/src/main/res/layout/upload_key_activity.xml @@ -1,55 +1,62 @@  <?xml version="1.0" encoding="UTF-8"?> -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" -    xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:orientation="vertical"      android:layout_width="match_parent"      android:layout_height="match_parent"> -    <LinearLayout +    <include layout="@layout/notify_area" /> + +    <ScrollView          android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:paddingLeft="16dp" -        android:paddingRight="16dp" -        android:orientation="vertical"> - -        <TextView -            style="@style/SectionHeader" -            android:layout_width="wrap_content" -            android:layout_height="wrap_content" -            android:layout_marginBottom="4dp" -            android:layout_marginTop="14dp" -            android:text="@string/section_key_server" /> +        android:layout_height="match_parent"> -        <Spinner -            android:id="@+id/upload_key_keyserver" +        <LinearLayout              android:layout_width="match_parent"              android:layout_height="wrap_content" -            android:layout_marginBottom="4dp" -            android:layout_marginTop="4dp" /> - -        <TextView -            style="@style/SectionHeader" -            android:layout_width="wrap_content" -            android:layout_height="0dp" -            android:layout_marginTop="14dp" -            android:text="@string/section_actions" -            android:layout_weight="1" /> - -        <TextView -            android:id="@+id/upload_key_action_upload" -            android:paddingLeft="8dp" -            android:paddingRight="8dp" -            android:layout_marginBottom="8dp" -            android:textAppearance="?android:attr/textAppearanceMedium" -            android:layout_width="match_parent" -            android:layout_height="match_parent" -            android:text="@string/btn_export_to_server" -            android:minHeight="?android:attr/listPreferredItemHeight" -            android:drawableRight="@drawable/ic_action_upload" -            android:drawablePadding="8dp" -            android:gravity="center_vertical" -            android:clickable="true" -            style="@style/SelectableItem" /> - -    </LinearLayout> - -</ScrollView>
\ No newline at end of file +            android:paddingLeft="16dp" +            android:paddingRight="16dp" +            android:orientation="vertical"> + +            <TextView +                style="@style/SectionHeader" +                android:layout_width="wrap_content" +                android:layout_height="wrap_content" +                android:layout_marginBottom="4dp" +                android:layout_marginTop="14dp" +                android:text="@string/section_key_server" /> + +            <Spinner +                android:id="@+id/upload_key_keyserver" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:layout_marginBottom="4dp" +                android:layout_marginTop="4dp" /> + +            <TextView +                style="@style/SectionHeader" +                android:layout_width="wrap_content" +                android:layout_height="0dp" +                android:layout_marginTop="14dp" +                android:text="@string/section_actions" +                android:layout_weight="1" /> + +            <TextView +                android:id="@+id/upload_key_action_upload" +                android:paddingLeft="8dp" +                android:paddingRight="8dp" +                android:layout_marginBottom="8dp" +                android:textAppearance="?android:attr/textAppearanceMedium" +                android:layout_width="match_parent" +                android:layout_height="match_parent" +                android:text="@string/btn_export_to_server" +                android:minHeight="?android:attr/listPreferredItemHeight" +                android:drawableRight="@drawable/ic_action_upload" +                android:drawablePadding="8dp" +                android:gravity="center_vertical" +                android:clickable="true" +                style="@style/SelectableItem" /> + +        </LinearLayout> + +    </ScrollView> +</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/view_key_activity.xml b/OpenKeychain/src/main/res/layout/view_key_activity.xml index 481b1ddf5..b15a73c0e 100644 --- a/OpenKeychain/src/main/res/layout/view_key_activity.xml +++ b/OpenKeychain/src/main/res/layout/view_key_activity.xml @@ -4,42 +4,38 @@      android:layout_height="match_parent"      android:orientation="vertical"> -    <LinearLayout -        android:id="@+id/card_container" -        android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:orientation="vertical" /> +    <include layout="@layout/notify_area"/> -    <TextView -        android:layout_width="match_parent" +    <LinearLayout +        android:id="@+id/view_key_status_layout" +        android:layout_width="wrap_content"          android:layout_height="wrap_content" -        android:text="This key is expired!" -        android:id="@+id/view_key_expired" -        android:textColor="@color/alert" -        android:textAppearance="?android:attr/textAppearanceSmall" -        android:gravity="center_vertical|center_horizontal" +        android:layout_gravity="center"          android:layout_marginTop="8dp"          android:layout_marginBottom="8dp" -        android:visibility="gone" /> +        android:orientation="horizontal"> -    <TextView -        android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:text="This key has been revoked!" -        android:id="@+id/view_key_revoked" -        android:textColor="@color/alert" -        android:textAppearance="?android:attr/textAppearanceSmall" -        android:gravity="center_vertical|center_horizontal" -        android:visibility="gone" -        android:layout_marginTop="8dp" -        android:layout_marginBottom="8dp" /> +        <ImageView +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:id="@+id/view_key_status_image" /> + +        <TextView +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:id="@+id/view_key_status_text" +            android:textAppearance="?android:attr/textAppearanceMedium" +            android:layout_gravity="center_vertical" +            android:layout_marginLeft="8dp"/> + +    </LinearLayout>      <View          android:layout_width="match_parent"          android:layout_height="1dip"          android:background="?android:attr/listDivider"          android:visibility="gone" -        android:id="@+id/status_divider" /> +        android:id="@+id/view_key_status_divider" />      <org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout          android:id="@+id/view_key_sliding_tab_layout" diff --git a/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml b/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml index d93420a99..b11538ba9 100644 --- a/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml +++ b/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml @@ -35,20 +35,34 @@              android:layout_height="1dip"              android:background="?android:attr/listDivider" /> -        <TextView +        <LinearLayout              android:id="@+id/view_key_action_certify" -            android:paddingLeft="8dp" -            android:paddingRight="8dp" -            android:textAppearance="?android:attr/textAppearanceMedium"              android:layout_width="match_parent" -            android:layout_height="match_parent" -            android:text="@string/key_view_action_certify" -            android:minHeight="?android:attr/listPreferredItemHeight" -            android:drawableRight="@drawable/ic_action_good" -            android:drawablePadding="8dp" -            android:gravity="center_vertical" +            android:layout_height="?android:attr/listPreferredItemHeight"              android:clickable="true" -            style="@style/SelectableItem" /> +            android:paddingRight="4dp" +            style="@style/SelectableItem" +            android:orientation="horizontal"> + +            <TextView +                android:id="@+id/view_key_action_certify_text" +                android:paddingLeft="8dp" +                android:textAppearance="?android:attr/textAppearanceMedium" +                android:layout_width="0dip" +                android:layout_height="match_parent" +                android:text="@string/key_view_action_certify" +                android:layout_weight="1" +                android:gravity="center_vertical" /> + +            <ImageView +                android:id="@+id/view_key_action_certify_image" +                android:layout_width="wrap_content" +                android:layout_height="match_parent" +                android:padding="8dp" +                android:src="@drawable/status_signature_verified_cutout" +                android:layout_gravity="center_vertical" /> + +        </LinearLayout>          <TextView              style="@style/SectionHeader" diff --git a/OpenKeychain/src/main/res/layout/view_key_keys_item.xml b/OpenKeychain/src/main/res/layout/view_key_subkey_item.xml index 13feaf2cc..0c0a5d7e6 100644 --- a/OpenKeychain/src/main/res/layout/view_key_keys_item.xml +++ b/OpenKeychain/src/main/res/layout/view_key_subkey_item.xml @@ -1,10 +1,9 @@  <?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"      android:layout_width="match_parent"      android:layout_height="wrap_content"      android:minHeight="?android:attr/listPreferredItemHeight"      android:orientation="horizontal" -    android:paddingRight="3dip"      android:singleLine="true">      <ImageView @@ -13,28 +12,44 @@          android:layout_height="wrap_content"          android:layout_gravity="center_vertical"          android:src="@drawable/key_small" -        android:layout_marginLeft="8dp" /> +        android:padding="8dp" +        android:layout_centerVertical="true" +        android:layout_alignParentLeft="true" +        android:layout_alignParentStart="true" /> + +    <ImageView +        android:layout_width="wrap_content" +        android:layout_height="wrap_content" +        android:id="@+id/edit_image" +        android:src="@drawable/ic_action_edit" +        android:padding="8dp" +        android:layout_centerVertical="true" +        android:layout_alignParentRight="true" +        android:layout_alignParentEnd="true" />      <LinearLayout          android:orientation="vertical" -        android:layout_width="fill_parent" +        android:layout_toRightOf="@id/ic_masterKey" +        android:layout_toLeftOf="@id/edit_image" +        android:layout_centerVertical="true" +        android:layout_width="match_parent"          android:layout_height="wrap_content" -        android:layout_gravity="center_vertical" -        android:layout_marginLeft="8dp" -        android:layout_marginRight="8dp"> +        android:layout_marginRight="8dp" +        android:id="@+id/linearLayout">          <LinearLayout -            android:layout_width="fill_parent" +            android:layout_width="match_parent"              android:layout_height="wrap_content" +            android:layout_gravity="center_vertical"              android:orientation="horizontal"              android:paddingBottom="2dip"              android:paddingTop="2dip">              <TextView                  android:id="@+id/keyId" -                android:layout_width="0dp" +                android:layout_width="match_parent"                  android:layout_height="wrap_content" -                android:text="@string/label_key_id" +                android:text="0x00000000"                  android:textAppearance="?android:attr/textAppearanceMedium"                  android:typeface="monospace"                  android:layout_weight="1" /> @@ -75,8 +90,8 @@          <LinearLayout              android:orientation="horizontal" -            android:layout_width="fill_parent" -            android:layout_height="fill_parent"> +            android:layout_width="match_parent" +            android:layout_height="match_parent">              <TextView                  android:id="@+id/keyDetails" @@ -94,8 +109,9 @@                  android:text="Expiry: 4/7/2016"                  android:textAppearance="?android:attr/textAppearanceSmall"                  android:layout_gravity="right" /> +          </LinearLayout>      </LinearLayout> -</LinearLayout> +</RelativeLayout> diff --git a/OpenKeychain/src/main/res/layout/view_key_userids_item.xml b/OpenKeychain/src/main/res/layout/view_key_user_id_item.xml index 8f036e600..157903000 100644 --- a/OpenKeychain/src/main/res/layout/view_key_userids_item.xml +++ b/OpenKeychain/src/main/res/layout/view_key_user_id_item.xml @@ -24,7 +24,6 @@              android:layout_width="wrap_content"              android:layout_height="wrap_content"              android:id="@+id/certified" -            android:src="@drawable/key_certify_ok_self"              android:layout_gravity="center_horizontal" />      </LinearLayout> @@ -63,11 +62,10 @@      </LinearLayout> -      <ImageView +        android:id="@+id/edit_image"          android:layout_width="wrap_content"          android:layout_height="match_parent" -        android:id="@+id/edit_image"          android:src="@drawable/ic_action_edit"          android:padding="8dp"          android:layout_gravity="center_horizontal" /> diff --git a/OpenKeychain/src/main/res/layout/wizard_activity.xml b/OpenKeychain/src/main/res/layout/wizard_activity.xml deleted file mode 100644 index 299d07a76..000000000 --- a/OpenKeychain/src/main/res/layout/wizard_activity.xml +++ /dev/null @@ -1,98 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" -    android:layout_width="match_parent" -    android:layout_height="match_parent"> - -    <LinearLayout -        android:id="@+id/wizard_buttons" -        android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:layout_alignParentBottom="true" -        android:orientation="horizontal"> - -        <Button -            android:id="@+id/wizard_back" -            android:layout_width="match_parent" -            android:layout_height="wrap_content" -            android:layout_weight="1" -            android:onClick="backOnClick" -            android:text="cancel" -            style="@style/SelectableItem" /> - -        <View -            android:layout_width="1dip" -            android:layout_height="match_parent" -            android:layout_marginBottom="4dip" -            android:layout_marginTop="4dip" -            android:background="?android:attr/listDivider" /> - -        <Button -            android:id="@+id/wizard_next" -            android:layout_width="match_parent" -            android:layout_height="wrap_content" -            android:layout_weight="1" -            android:onClick="nextOnClick" -            android:text="next" -            style="@style/SelectableItem" /> -    </LinearLayout> - -    <View -        android:id="@+id/wizard_progress_line" -        android:layout_width="match_parent" -        android:layout_height="1dip" -        android:layout_above="@+id/wizard_buttons" -        android:layout_marginLeft="4dip" -        android:layout_marginRight="4dip" -        android:background="?android:attr/listDivider" -        android:visibility="gone" /> - -    <LinearLayout -        android:id="@+id/wizard_progress" -        android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:layout_above="@+id/wizard_progress_line" -        android:visibility="gone"> - -        <ProgressBar -            android:id="@+id/wizard_progress_progressbar" -            android:layout_width="wrap_content" -            android:layout_height="wrap_content" /> - -        <ImageView -            android:id="@+id/wizard_progress_image" -            android:layout_width="wrap_content" -            android:layout_height="wrap_content" -            android:src="@drawable/icon_light_refresh" /> - -        <TextView -            android:id="@+id/wizard_progress_text" -            android:layout_width="wrap_content" -            android:layout_height="wrap_content" -            android:layout_gravity="center_vertical" -            android:text="asd" -            android:textAppearance="?android:attr/textAppearanceMedium" /> -    </LinearLayout> - -    <View -        android:id="@+id/wizard_line2" -        android:layout_width="match_parent" -        android:layout_height="1dip" -        android:layout_above="@+id/wizard_progress" -        android:layout_marginLeft="4dip" -        android:layout_marginRight="4dip" -        android:background="?android:attr/listDivider" /> - -    <ScrollView -        android:layout_width="match_parent" -        android:layout_height="match_parent" -        android:layout_above="@+id/wizard_line2"> - -        <LinearLayout -            android:id="@+id/wizard_container" -            android:layout_width="match_parent" -            android:layout_height="wrap_content" -            android:orientation="vertical" -            android:padding="16dp" /> -    </ScrollView> - -</RelativeLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/wizard_create_key_fragment.xml b/OpenKeychain/src/main/res/layout/wizard_create_key_fragment.xml deleted file mode 100644 index 258ea7223..000000000 --- a/OpenKeychain/src/main/res/layout/wizard_create_key_fragment.xml +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" -    android:layout_width="match_parent" -    android:layout_height="wrap_content" -    android:orientation="vertical"> - -    <TextView -        android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:paddingBottom="4dp" -        android:text="Enter Full Name, Email and Passphrase!" -        android:textAppearance="?android:attr/textAppearanceMedium" /> - -    <AutoCompleteTextView -        android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:inputType="textPersonName" -        android:hint="Name" -        android:ems="10" -        android:id="@+id/name" /> - -    <AutoCompleteTextView -        android:id="@+id/email" -        android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:hint="bla@example.com" -        android:layout_weight="1" -        android:ems="10" -        android:inputType="textEmailAddress" /> - -    <EditText -        android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:inputType="textPassword" -        android:hint="passphrase" -        android:ems="10" -        android:id="@+id/passphrase" -        android:layout_gravity="center_horizontal" /> - - -</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/wizard_k9_fragment.xml b/OpenKeychain/src/main/res/layout/wizard_k9_fragment.xml deleted file mode 100644 index 342adc37e..000000000 --- a/OpenKeychain/src/main/res/layout/wizard_k9_fragment.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" -    android:layout_width="match_parent" -    android:layout_height="wrap_content" -    android:orientation="vertical" > - -    <org.sufficientlysecure.htmltextview.HtmlTextView -        android:id="@+id/wizard_k9_text" -        android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:paddingBottom="4dp" -        android:text="Text..." -        android:textAppearance="?android:attr/textAppearanceMedium" /> - -    <RadioGroup -        android:id="@+id/wizard_k9_radio_group" -        android:layout_width="match_parent" -        android:layout_height="wrap_content"> - -        <RadioButton -            android:layout_width="match_parent" -            android:layout_height="?android:attr/listPreferredItemHeight" -            android:checked="true" -            android:textAppearance="?android:attr/textAppearanceMedium" -            style="@style/SelectableItem" -            android:text="install K9" -            android:id="@+id/wizard_k9_install" /> - -        <View -            android:layout_width="match_parent" -            android:layout_height="1dip" -            android:background="?android:attr/listDivider" /> - -        <RadioButton -            android:layout_width="match_parent" -            android:layout_height="?android:attr/listPreferredItemHeight" -            android:textAppearance="?android:attr/textAppearanceMedium" -            style="@style/SelectableItem" -            android:text="skip install" -            android:id="@+id/wizard_k9_skip" /> -    </RadioGroup> - -</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/wizard_start_fragment.xml b/OpenKeychain/src/main/res/layout/wizard_start_fragment.xml deleted file mode 100644 index 9e1403f74..000000000 --- a/OpenKeychain/src/main/res/layout/wizard_start_fragment.xml +++ /dev/null @@ -1,63 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" -    android:layout_width="match_parent" -    android:layout_height="wrap_content" -    android:orientation="vertical"> - -    <TextView -        android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:paddingBottom="4dp" -        android:text="Welcome to OpenKeychain" -        android:textAppearance="?android:attr/textAppearanceMedium" /> - -    <TextView -        style="@style/SectionHeader" -        android:layout_width="wrap_content" -        android:layout_height="0dp" -        android:layout_marginTop="14dp" -        android:text="What you wanna do today?" -        android:layout_weight="1" /> - -    <RadioGroup -        android:id="@+id/wizard_start_radio_group" -        android:layout_width="match_parent" -        android:layout_height="wrap_content"> - -        <RadioButton -            android:layout_width="match_parent" -            android:layout_height="?android:attr/listPreferredItemHeight" -            android:checked="true" -            android:textAppearance="?android:attr/textAppearanceMedium" -            style="@style/SelectableItem" -            android:text="new key" -            android:id="@+id/wizard_start_new_key" /> - -        <View -            android:layout_width="match_parent" -            android:layout_height="1dip" -            android:background="?android:attr/listDivider" /> - -        <RadioButton -            android:layout_width="match_parent" -            android:layout_height="?android:attr/listPreferredItemHeight" -            android:textAppearance="?android:attr/textAppearanceMedium" -            style="@style/SelectableItem" -            android:text="import existing key" -            android:id="@+id/wizard_start_import" /> - -        <View -            android:layout_width="match_parent" -            android:layout_height="1dip" -            android:background="?android:attr/listDivider" /> - -        <RadioButton -            android:layout_width="match_parent" -            android:layout_height="?android:attr/listPreferredItemHeight" -            android:textAppearance="?android:attr/textAppearanceMedium" -            style="@style/SelectableItem" -            android:text="skip wizard" -            android:id="@+id/wizard_start_skip" /> -    </RadioGroup> - -</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/menu/key_list.xml b/OpenKeychain/src/main/res/menu/key_list.xml index ebb7314b8..e865df182 100644 --- a/OpenKeychain/src/main/res/menu/key_list.xml +++ b/OpenKeychain/src/main/res/menu/key_list.xml @@ -27,11 +27,6 @@          android:title="@string/menu_create_key" />      <item -        android:id="@+id/menu_key_list_create_expert" -        app:showAsAction="never" -        android:title="@string/menu_create_key_expert" /> - -    <item          android:id="@+id/menu_key_list_debug_read"          app:showAsAction="never"          android:title="Debug / DB restore" @@ -43,4 +38,10 @@          android:title="Debug / DB backup"          android:visible="false" /> +    <item +        android:id="@+id/menu_key_list_debug_first_time" +        app:showAsAction="never" +        android:title="Debug / Show first time screen" +        android:visible="false" /> +  </menu> diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml index de6962dc9..9d9868457 100644 --- a/OpenKeychain/src/main/res/values-de/strings.xml +++ b/OpenKeychain/src/main/res/values-de/strings.xml @@ -247,8 +247,8 @@    <string name="error_only_files_are_supported">Binäre Daten ohne ohne Datei im Dateisystem werden nicht unterstützt.</string>    <string name="error_jelly_bean_needed">Android 4.1 wird benötigt um Androids NFC Beam nutzen zu können!</string>    <string name="error_nfc_needed">NFC steht auf diesem Gerät nicht zur Verfügung!</string> -  <string name="error_keyserver_insufficient_query">zu kurze Schlüsselanfrage</string> -  <string name="error_keyserver_too_many_responses">Die Schlüsselanfrage liefert zu viele Ergebnisse. Bitte verfeinern sie sie Anfrage.</string> +  <string name="error_query_too_short">zu kurze Schlüsselanfrage</string> +  <string name="error_too_many_responses">Die Schlüsselanfrage liefert zu viele Ergebnisse. Bitte verfeinern sie sie Anfrage.</string>    <string name="error_generic_report_bug">Ein allgemeiner Fehler trat auf, bitte schreiben Sie einen neuen Bugreport für OpenKeychain.</string>    <plurals name="error_import_non_pgp_part">      <item quantity="one">Ein Teil der geladenen Datei ist ein gültiges OpenPGP Objekt aber kein OpenPGP Schlüssel</item> diff --git a/OpenKeychain/src/main/res/values-es/strings.xml b/OpenKeychain/src/main/res/values-es/strings.xml index 50afd5c57..719ddf414 100644 --- a/OpenKeychain/src/main/res/values-es/strings.xml +++ b/OpenKeychain/src/main/res/values-es/strings.xml @@ -250,10 +250,10 @@    <string name="error_jelly_bean_needed">¡Necesita Android 4.1 para usar la característica NFC Beam (haz NFC) de Android!</string>    <string name="error_nfc_needed">¡NFC no está disponible en tu dispositivo!</string>    <string name="error_nothing_import">¡No se encontraron claves!</string> -  <string name="error_keyserver_insufficient_query">Petición de búsqueda de clave demasiado corta</string> +  <string name="error_query_too_short">Petición de búsqueda de clave demasiado corta</string>    <string name="error_searching_keys">Error irrecuperable buscando claves en el servidor</string> -  <string name="error_keyserver_too_many_responses">La petición de búsqueda de clave devolvió demasiados candidatos; por favor refine su petición</string> -  <string name="error_import_file_no_content">El Fichero/Portapapeles está vacío</string> +  <string name="error_too_many_responses">La petición de búsqueda de clave devolvió demasiados candidatos; por favor refine su petición</string> +  <string name="error_import_no_valid_keys">El Fichero/Portapapeles está vacío</string>    <string name="error_generic_report_bug">Ha ocurrido un error genérico, por favor, informa de este bug a OpenKeychain</string>    <plurals name="error_import_non_pgp_part">      <item quantity="one">parte del archivo cargado es un objeto OpenPGP válido pero no una clave OpenPGP</item> @@ -370,7 +370,7 @@      <item quantity="one">Clave%2$s importada con éxito.</item>      <item quantity="other">%1$d claves%2$s importadas con éxito.</item>    </plurals> -  <string name="import_view_log">Ver registro (log)</string> +  <string name="view_log">Ver registro (log)</string>    <string name="import_error_nothing">No hay nada que importar.</string>    <string name="import_error">¡Error importando claves!</string>    <string name="import_with_warnings">, con advertencias</string> diff --git a/OpenKeychain/src/main/res/values-fr/strings.xml b/OpenKeychain/src/main/res/values-fr/strings.xml index f3627e9eb..0c5c5de67 100644 --- a/OpenKeychain/src/main/res/values-fr/strings.xml +++ b/OpenKeychain/src/main/res/values-fr/strings.xml @@ -250,10 +250,10 @@    <string name="error_jelly_bean_needed">Il vous faut Android 4.1 pour utiliser la fonction Beam NFC d\'Android !</string>    <string name="error_nfc_needed">La NFC n\'est pas disponible sur votre appareil !</string>    <string name="error_nothing_import">Aucune clef trouvée !</string> -  <string name="error_keyserver_insufficient_query">La requête de recherche de clef est trop courte</string> +  <string name="error_query_too_short">La requête de recherche de clef est trop courte</string>    <string name="error_searching_keys">Erreur irrécupérable lors de la recherche de clef sur le serveur</string> -  <string name="error_keyserver_too_many_responses">La requête de recherche de clef a retourné trop de candidats. Veuillez raffiner la requête</string> -  <string name="error_import_file_no_content">Le fichier/le presse-papiers est vide</string> +  <string name="error_too_many_responses">La requête de recherche de clef a retourné trop de candidats. Veuillez raffiner la requête</string> +  <string name="error_import_no_valid_keys">Le fichier/le presse-papiers est vide</string>    <string name="error_generic_report_bug">Une erreur générique est survenue, veuillez créer un nouveau rapport de bogue pour OpenKeychain.</string>    <plurals name="error_import_non_pgp_part">      <item quantity="one">une partie du fichier chargé est un objet OpenPGP valide mais pas une clef OpenPGP</item> @@ -358,7 +358,7 @@      <item quantity="one">Clef importée avec succès</item>      <item quantity="other">%1$d clefs importées avec succès</item>    </plurals> -  <string name="import_view_log">Consulter le journal</string> +  <string name="view_log">Consulter le journal</string>    <string name="import_error_nothing">Rien à importer.</string>    <string name="import_error">Erreur lors de l\'importation des clefs !</string>    <string name="import_with_warnings">, avec des avertissements</string> diff --git a/OpenKeychain/src/main/res/values-it/strings.xml b/OpenKeychain/src/main/res/values-it/strings.xml index c8ec2614d..074efb78b 100644 --- a/OpenKeychain/src/main/res/values-it/strings.xml +++ b/OpenKeychain/src/main/res/values-it/strings.xml @@ -250,10 +250,10 @@    <string name="error_jelly_bean_needed">Devi avere Android 4.1 per usare Android NFC Beam!</string>    <string name="error_nfc_needed">NFC non disponibile nel tuo dispositivo!</string>    <string name="error_nothing_import">Nessuna chiave trovata!</string> -  <string name="error_keyserver_insufficient_query">Chiave della query di ricerca troppo corta</string> +  <string name="error_query_too_short">Chiave della query di ricerca troppo corta</string>    <string name="error_searching_keys">Errore irreversibile nella ricerca di chiavi sul server</string> -  <string name="error_keyserver_too_many_responses">Chiave della query di ricerca ha generato troppi candidati; Si prega di perfezionare la ricerca</string> -  <string name="error_import_file_no_content">File/Appunti vuoti</string> +  <string name="error_too_many_responses">Chiave della query di ricerca ha generato troppi candidati; Si prega di perfezionare la ricerca</string> +  <string name="error_import_no_valid_keys">File/Appunti vuoti</string>    <string name="error_generic_report_bug">Si è verificato un errore generico, si prega di creare una nuova segnalazione di errore per OpenKeychain.</string>    <plurals name="error_import_non_pgp_part">      <item quantity="one">parte del file caricato e\' un oggetto OpenPGP valido, ma non una chave OpenPGP</item> @@ -354,7 +354,7 @@    <string name="import_clipboard_button">Ottieni chiave dagli appunti</string>    <string name="import_keybase_button">Ottieni chiave da Keybase.io</string>    <!--Import result toast--> -  <string name="import_view_log">Mostra registro</string> +  <string name="view_log">Mostra registro</string>    <string name="import_error_nothing">Niente da importare</string>    <string name="import_error">Errore di importazione chiavi!</string>    <string name="import_with_warnings">, con avvisi</string> diff --git a/OpenKeychain/src/main/res/values-ja/strings.xml b/OpenKeychain/src/main/res/values-ja/strings.xml index 13463df25..070ee7f5a 100644 --- a/OpenKeychain/src/main/res/values-ja/strings.xml +++ b/OpenKeychain/src/main/res/values-ja/strings.xml @@ -246,10 +246,10 @@    <string name="error_jelly_bean_needed">Android NFC Beam機能を使うにはAndroid 4.1  が必要です!</string>    <string name="error_nfc_needed">あなたのデバイスにはNFCが存在しません!</string>    <string name="error_nothing_import">鍵が見当りません!</string> -  <string name="error_keyserver_insufficient_query">鍵検索のクエリが短かすぎます</string> +  <string name="error_query_too_short">鍵検索のクエリが短かすぎます</string>    <string name="error_searching_keys">サーバでの鍵の検索が回復不可能なエラーになりました</string> -  <string name="error_keyserver_too_many_responses">鍵検索のクエリが沢山の候補を返しました; クエリを精密化してください</string> -  <string name="error_import_file_no_content">ファイル/クリップボードが空です</string> +  <string name="error_too_many_responses">鍵検索のクエリが沢山の候補を返しました; クエリを精密化してください</string> +  <string name="error_import_no_valid_keys">ファイル/クリップボードが空です</string>    <string name="error_generic_report_bug">一般エラーが発生しました、この新しいバグの情報をOpenKeychainプロジェクトに送ってください</string>    <plurals name="error_import_non_pgp_part">      <item quantity="other">読み込んだファイルのOpenPGPオブジェクト部分は正しいですが、OpenPGPの鍵ではありません</item> @@ -358,7 +358,7 @@    <plurals name="import_keys_updated">      <item quantity="other">%1$d の鍵%2$sのアップデートに成功。</item>    </plurals> -  <string name="import_view_log">ログを見る</string> +  <string name="view_log">ログを見る</string>    <string name="import_error_nothing">インポートするものがありません。</string>    <string name="import_error">鍵のインポートのエラー!</string>    <string name="import_with_warnings">、とワーニング</string> diff --git a/OpenKeychain/src/main/res/values-ru/strings.xml b/OpenKeychain/src/main/res/values-ru/strings.xml index 539a431b5..1e697c8db 100644 --- a/OpenKeychain/src/main/res/values-ru/strings.xml +++ b/OpenKeychain/src/main/res/values-ru/strings.xml @@ -254,10 +254,10 @@    <string name="error_jelly_bean_needed">Для использования NFC Beam требуется Android 4.1+ !</string>    <string name="error_nfc_needed">Ваше устройство не поддерживает NFC!</string>    <string name="error_nothing_import">Ключи не найдены!</string> -  <string name="error_keyserver_insufficient_query">Запрос слишком короткий</string> +  <string name="error_query_too_short">Запрос слишком короткий</string>    <string name="error_searching_keys">Ошибка поиска ключей на сервере</string> -  <string name="error_keyserver_too_many_responses">Поиск ключа вернул слишком много вариантов; Пожалуйста, уточните запрос</string> -  <string name="error_import_file_no_content">Файл/Буфер пуст</string> +  <string name="error_too_many_responses">Поиск ключа вернул слишком много вариантов; Пожалуйста, уточните запрос</string> +  <string name="error_import_no_valid_keys">Файл/Буфер пуст</string>    <string name="error_generic_report_bug">Выявлена ошибка. Пожалуйста, сообщите о ней разработчику.</string>    <plurals name="error_import_non_pgp_part">      <item quantity="one">часть загруженного файла содержит данные OpenPGP, но это не ключ</item> @@ -371,7 +371,7 @@      <item quantity="few">и обновлено %1$d ключей%2$s.</item>      <item quantity="other">и обновлено %1$d ключей%2$s.</item>    </plurals> -  <string name="import_view_log">Смотреть журнал</string> +  <string name="view_log">Смотреть журнал</string>    <string name="import_error_nothing">Нет данных для импорта.</string>    <string name="import_error">Ошибка импорта ключей!</string>    <string name="import_with_warnings">, с предупреждениями</string> diff --git a/OpenKeychain/src/main/res/values-sl/strings.xml b/OpenKeychain/src/main/res/values-sl/strings.xml index 61fe6ed86..b9a0c4cb9 100644 --- a/OpenKeychain/src/main/res/values-sl/strings.xml +++ b/OpenKeychain/src/main/res/values-sl/strings.xml @@ -254,9 +254,9 @@    <string name="error_only_files_are_supported">Neposredni binarni podatki brez dejanske datoteke v datotečnem sistemu niso podprti.</string>    <string name="error_jelly_bean_needed">Za uporabo storitve NFC Beam potrebujete najmanj Android 4.1!</string>    <string name="error_nfc_needed">NFC ni na voljo na vaši napravi!</string> -  <string name="error_keyserver_insufficient_query">Iskalni pojem je prekratek</string> +  <string name="error_query_too_short">Iskalni pojem je prekratek</string>    <string name="error_searching_keys">Nepremostljiva napaka pri iskanju ključev na strežniku</string> -  <string name="error_keyserver_too_many_responses">Iskanje ključev je vrnilo preveč zadetkov; prosimo redefinirajte iskalni pojem</string> +  <string name="error_too_many_responses">Iskanje ključev je vrnilo preveč zadetkov; prosimo redefinirajte iskalni pojem</string>    <string name="error_generic_report_bug">Pripetila se je splošna napaka, prosimo ustvarite poročilo o \'hrošču\'.</string>    <plurals name="error_import_non_pgp_part">      <item quantity="one">Del naložene datoteke je veljavnen objekt OpenPGP a ni ključ.</item> diff --git a/OpenKeychain/src/main/res/values-uk/strings.xml b/OpenKeychain/src/main/res/values-uk/strings.xml index 6f5cc7cf1..4e267e431 100644 --- a/OpenKeychain/src/main/res/values-uk/strings.xml +++ b/OpenKeychain/src/main/res/values-uk/strings.xml @@ -252,9 +252,9 @@    <string name="error_jelly_bean_needed">Вам потрібний Android 4.1 для використання функції Androids NFC промінь!</string>    <string name="error_nfc_needed">NFC недоступний на вашому пристрої!</string>    <string name="error_nothing_import">Ключ не знайдено!</string> -  <string name="error_keyserver_insufficient_query">Запит пошуку ключа надто короткий</string> +  <string name="error_query_too_short">Запит пошуку ключа надто короткий</string>    <string name="error_searching_keys">Невиправна помилка пошуку ключів в сервері</string> -  <string name="error_keyserver_too_many_responses">Запит пошуку ключа видав надто багато варіантів. Уточніть пошуковий запит</string> +  <string name="error_too_many_responses">Запит пошуку ключа видав надто багато варіантів. Уточніть пошуковий запит</string>    <string name="error_generic_report_bug">Трапилася загальна помилка, будь ласка, створіть новий звіт про помилку для OpenKeychain.</string>    <plurals name="error_import_non_pgp_part">      <item quantity="one">частина завантаженого файлу є вірним об\'єктом OpenPGP, але не ключем OpenPGP</item> @@ -352,7 +352,7 @@    <string name="import_clipboard_button">Отримати ключ з буфера обміну</string>    <string name="import_keybase_button">Отримати ключ із Keybase.io</string>    <!--Import result toast--> -  <string name="import_view_log">Переглянути журнал</string> +  <string name="view_log">Переглянути журнал</string>    <string name="import_error_nothing">Нема що імпортувати.</string>    <string name="import_error">Помилка імпорту ключів!</string>    <!--Intent labels--> diff --git a/OpenKeychain/src/main/res/values/colors.xml b/OpenKeychain/src/main/res/values/colors.xml index c0042d215..a21f949d1 100644 --- a/OpenKeychain/src/main/res/values/colors.xml +++ b/OpenKeychain/src/main/res/values/colors.xml @@ -5,12 +5,19 @@      <color name="emphasis_dark">#9933cc</color>      <color name="bg_gray">#cecbce</color>      <color name="tertiary_text_light">#808080</color> +    <color name="alert">#ffdd3333</color>      <color name="holo_gray_light">#33999999</color>      <color name="holo_gray_bright">#33CCCCCC</color> -    <color name="result_red">#ffff4444</color> -    <color name="result_orange">#ffffbb33</color> -    <color name="result_green">#ff99cc00</color> -    <color name="result_purple">#aa66cc</color> +    <!-- http://developer.android.com/design/style/color.html --> +    <color name="android_red_light">#ffff4444</color> +    <color name="android_red_dark">#ffCC0000</color> +    <color name="android_orange_light">#ffffbb33</color> +    <color name="android_orange_dark">#ffFF8800</color> +    <color name="android_green_light">#ff99cc00</color> +    <color name="android_green_dark">#ff669900</color> +    <color name="android_purple_light">#ffaa66cc</color> +    <color name="android_purple_dark">#ff9933CC</color> +  </resources> diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index f6c065c42..101a9fa0a 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -9,7 +9,6 @@      <string name="title_authentication">Passphrase</string>      <string name="title_create_key">Create Key</string>      <string name="title_edit_key">Edit Key</string> -    <string name="title_wizard">Welcome to OpenKeychain</string>      <string name="title_preferences">Preferences</string>      <string name="title_api_registered_apps">Apps</string>      <string name="title_key_server_preference">Keyserver Preference</string> @@ -110,9 +109,11 @@      <string name="label_file_colon">File:</string>      <string name="label_no_passphrase">No Passphrase</string>      <string name="label_passphrase">Passphrase</string> -    <string name="label_passphrase_again">Again</string> +    <string name="label_passphrase_again">Repeat Passphrase</string>      <string name="label_algorithm">Algorithm</string>      <string name="label_ascii_armor">ASCII Armor</string> +    <string name="label_conceal_pgp_application">Let others know that you\'re using OpenKeychain</string> +    <string name="label_conceal_pgp_application_summary">Writes \'OpenKeychain v2.7\' to OpenPGP signatures, ciphertext, and exported keys</string>      <string name="label_select_public_keys">Recipients</string>      <string name="label_to">To</string>      <string name="label_delete_after_encryption">Delete After Encryption</string> @@ -275,10 +276,12 @@      <string name="error_jelly_bean_needed">You need Android 4.1 to use Android\'s NFC Beam feature!</string>      <string name="error_nfc_needed">NFC is not available on your device!</string>      <string name="error_nothing_import">No keys found!</string> -    <string name="error_keyserver_insufficient_query">Key search query too short</string> -    <string name="error_searching_keys">Unrecoverable error searching for keys at server</string> -    <string name="error_keyserver_too_many_responses">Key search query returned too many candidates; Please refine query</string> -    <string name="error_import_file_no_content">File/Clipboard is empty</string> +    <string name="error_query_too_short">Search query too short. Please refine your query!</string> +    <string name="error_searching_keys">An error occurred when searching for keys.</string> +    <string name="error_too_many_responses">Key search query returned too many candidates. Please refine your query!</string> +    <string name="error_too_short_or_too_many_responses">Either no keys or too many have been found. Please improve your query!</string> + +    <string name="error_import_no_valid_keys">No valid keys found in File/Clipboard!</string>      <string name="error_generic_report_bug">A generic error occurred, please create a new bug report for OpenKeychain.</string>      <plurals name="error_import_non_pgp_part">          <item quantity="one">part of the loaded file is a valid OpenPGP object but not a OpenPGP key</item> @@ -307,6 +310,20 @@      <string name="progress_building_master_key">building master ring…</string>      <string name="progress_adding_sub_keys">adding sub keys…</string>      <string name="progress_saving_key_ring">saving key…</string> +    <string name="progress_generating_rsa">generating new RSA key…</string> +    <string name="progress_generating_dsa">generating new DSA key…</string> +    <string name="progress_generating_elgamal">generating new ElGamal key…</string> + +    <string name="progress_modify">modifying keyring…</string> + +    <string name="progress_modify_unlock">unlocking keyring…</string> +    <string name="progress_modify_adduid">adding user ids…</string> +    <string name="progress_modify_revokeuid">revoking user ids…</string> +    <string name="progress_modify_primaryuid">changing primary user id…</string> +    <string name="progress_modify_subkeychange">modifying subkeys…</string> +    <string name="progress_modify_subkeyrevoke">revoking subkeys…</string> +    <string name="progress_modify_subkeyadd">adding subkeys…</string> +    <string name="progress_modify_passphrase">changing passphrase…</string>      <plurals name="progress_exporting_key">          <item quantity="one">exporting key…</item> @@ -397,8 +414,8 @@          <item quantity="other">Successfully imported %1$d keys</item>      </plurals>      <plurals name="import_keys_added_and_updated_2"> -        <item quantity="one"> and updated key%2$s.</item> -        <item quantity="other"> and updated %1$d keys%2$s.</item> +        <item quantity="one">and updated key%2$s.</item> +        <item quantity="other">and updated %1$d keys%2$s.</item>      </plurals>      <plurals name="import_keys_added">          <item quantity="one">Successfully imported key%2$s.</item> @@ -408,7 +425,7 @@          <item quantity="one">Successfully updated key%2$s.</item>          <item quantity="other">Successfully updated %1$d keys%2$s.</item>      </plurals> -    <string name="import_view_log">View Log</string> +    <string name="view_log">View Log</string>      <string name="import_error_nothing">Nothing to import.</string>      <string name="import_error">Error importing keys!</string>      <string name="import_with_warnings">, with warnings</string> @@ -486,6 +503,23 @@          <item>Change to Primary Identity</item>          <item>Revoke Identity</item>      </string-array> +    <string name="edit_key_edit_subkey_title">Select an action!</string> +    <string-array name="edit_key_edit_subkey"> +        <item>Change Expiry</item> +        <item>Revoke Subkey</item> +    </string-array> + +    <!-- Create key --> +    <string name="create_key_text">Enter Full Name, Email and a Passphrase.</string> +    <string name="create_key_upload">Upload key to keyserver</string> +    <string name="create_key_empty">This field is required</string> +    <string name="create_key_passphrases_not_equal">Passphrases do not match</string> +    <string name="create_key_final_text">You entered the following credentials:</string> +    <string name="create_key_final_robot_text">Creating a key may take a while, have a cup of coffee in the meantime…\n(3 subkeys, RSA, 4096 bit)</string> + +    <!-- View key --> +    <string name="view_key_revoked">This key has been revoked!</string> +    <string name="view_key_expired">This key is expired!</string>      <!-- Navigation Drawer -->      <string name="nav_keys">Keys</string> @@ -511,7 +545,7 @@      <string name="cert_casual">casual</string>      <string name="cert_positive">positive</string>      <string name="cert_revoke">revoked</string> -    <string name="cert_verify_ok">ok</string> +    <string name="cert_verify_ok">OK</string>      <string name="cert_verify_failed">failed!</string>      <string name="cert_verify_error">error!</string>      <string name="cert_verify_unavailable">key unavailable</string> @@ -556,7 +590,7 @@      <string name="msg_ip_reinsert_secret">Re-inserting secret key</string>      <string name="msg_ip_uid_cert_bad">Encountered bad certificate!</string>      <string name="msg_ip_uid_cert_error">Error processing certificate!</string> -    <string name="msg_ip_uid_cert_good">User id is certified by %1$s (%2$s)</string> +    <string name="msg_ip_uid_cert_good">User id is certified by %1$s</string>      <plurals name="msg_ip_uid_certs_unknown">          <item quantity="one">Ignoring one certificate issued by an unknown public key</item>          <item quantity="other">Ignoring %s certificates issued by unknown public keys</item> @@ -570,6 +604,7 @@      <string name="msg_ip_uid_processing">Processing user id %s</string>      <string name="msg_ip_uid_revoked">User id is revoked</string>      <string name="msg_is_bad_type_public">Tried to import public keyring as secret. This is a bug, please file a report!</string> +    <string name="msg_is_bad_type_uncanon">Tried to import a keyring without canonicalization. This is a bug, please file a report!</string>      <!-- Import Secret log entries -->      <string name="msg_is">Importing secret key %s</string> @@ -592,6 +627,7 @@      <string name="msg_kc_revoke_bad_local">Removing keyring revocation certificate with "local" flag</string>      <string name="msg_kc_revoke_bad_time">Removing keyring revocation certificate with future timestamp</string>      <string name="msg_kc_revoke_bad_type">Removing master key certificate of unknown type (%s)</string> +    <string name="msg_kc_revoke_bad_type_uid">Removing user id certification in bad position</string>      <string name="msg_kc_revoke_bad">Removing bad keyring revocation certificate</string>      <string name="msg_kc_revoke_dup">Removing redundant keyring revocation certificate</string>      <string name="msg_kc_sub">Processing subkey %s</string> @@ -629,6 +665,7 @@      <string name="msg_kc_uid_revoke_dup">Removing redundant revocation certificate for user id "%s"</string>      <string name="msg_kc_uid_revoke_old">Removing outdated revocation certificate for user id "%s"</string>      <string name="msg_kc_uid_no_cert">No valid self-certificate found for user id %s, removing from ring</string> +    <string name="msg_kc_uid_remove">Removing invalid user id %s</string>      <!-- Keyring merging log entries -->      <string name="msg_mg_public">Merging into public keyring %s</string> @@ -637,6 +674,17 @@      <string name="msg_mg_heterogeneous">Tried to consolidate heterogeneous keyrings</string>      <string name="msg_mg_new_subkey">Adding new subkey %s</string>      <string name="msg_mg_found_new">Found %s new certificates in keyring</string> +    <string name="msg_mg_unchanged">No new certificates</string> + +    <!-- createSecretKeyRing --> +    <string name="msg_cr">Generating new master key</string> +    <string name="msg_cr_error_no_master">No master key options specified!</string> +    <string name="msg_cr_error_no_user_id">Keyrings must be created with at least one user id!</string> +    <string name="msg_cr_error_no_certify">Master key must have certify flag!</string> +    <string name="msg_cr_error_keysize_512">Key size must be greater or equal 512!</string> +    <string name="msg_cr_error_internal_pgp">Internal PGP error!</string> +    <string name="msg_cr_error_unknown_algo">Bad algorithm choice!</string> +    <string name="msg_cr_error_master_elgamal">Master key must not be of type ElGamal!</string>      <!-- modifySecretKeyRing -->      <string name="msg_mr">Modifying keyring %s</string> @@ -644,10 +692,13 @@      <string name="msg_mf_error_fingerprint">Actual key fingerprint does not match the expected one!</string>      <string name="msg_mf_error_keyid">No key ID. This is an internal error, please file a bug report!</string>      <string name="msg_mf_error_integrity">Internal error, integrity check failed!</string> +    <string name="msg_mf_error_noexist_primary">Bad primary user id specified!</string>      <string name="msg_mf_error_revoked_primary">Revoked user ids cannot be primary!</string>      <string name="msg_mf_error_pgp">PGP internal exception!</string>      <string name="msg_mf_error_sig">Signature exception!</string>      <string name="msg_mf_passphrase">Changing passphrase</string> +    <string name="msg_mf_primary_replace_old">Replacing certificate of previous primary user id</string> +    <string name="msg_mf_primary_new">Generating new certificate for new primary user id</string>      <string name="msg_mf_subkey_change">Modifying subkey %s</string>      <string name="msg_mf_subkey_missing">Tried to operate on missing subkey %s!</string>      <string name="msg_mf_subkey_new">Generating new %1$s bit %2$s subkey</string> @@ -658,10 +709,19 @@      <string name="msg_mf_uid_add">Adding user id %s</string>      <string name="msg_mf_uid_primary">Changing primary uid to %s</string>      <string name="msg_mf_uid_revoke">Revoking user id %s</string> +    <string name="msg_mf_uid_error_empty">User ID must not be empty!</string>      <string name="msg_mf_unlock_error">Error unlocking keyring!</string>      <string name="msg_mf_unlock">Unlocking keyring</string> +    <!-- PassphraseCache --> +    <string name="passp_cache_notif_click_to_clear">Click to clear cached passphrases</string> +    <string name="passp_cache_notif_n_keys">OpenKeychain has cached %d passphrases</string> +    <string name="passp_cache_notif_keys">Cached Passphrases:</string> +    <string name="passp_cache_notif_clear">Clear Cache</string> +    <string name="passp_cache_notif_pwd">Password</string> +      <!-- unsorted --> +    <string name="internal_error">Internal error!</string>      <string name="section_certifier_id">Certifier</string>      <string name="section_cert">Certificate Details</string>      <string name="label_user_id">Identity</string> @@ -684,4 +744,10 @@      <string name="info_no_manual_account_creation">Do not create OpenKeychain-Accounts manually.\nFor more information, see Help.</string>      <string name="contact_show_key">Show key (%s)</string> +    <!-- First Time --> +    <string name="first_time_text1">Take back your privacy with OpenKeychain!</string> +    <string name="first_time_create_key">Create Key</string> +    <string name="first_time_import_key">Import Key</string> +    <string name="first_time_skip">Skip Setup</string> +  </resources> diff --git a/OpenKeychain/src/main/res/xml/adv_preferences.xml b/OpenKeychain/src/main/res/xml/adv_preferences.xml index fa3974199..a07ae06bb 100644 --- a/OpenKeychain/src/main/res/xml/adv_preferences.xml +++ b/OpenKeychain/src/main/res/xml/adv_preferences.xml @@ -38,11 +38,17 @@              android:key="defaultAsciiArmor"              android:persistent="false"              android:title="@string/label_ascii_armor" /> + +        <CheckBoxPreference +            android:key="concealPgpApplication" +            android:persistent="false" +            android:title="@string/label_conceal_pgp_application" +            android:summary="@string/label_conceal_pgp_application_summary" />      </PreferenceCategory>      <PreferenceCategory android:title="@string/section_advanced" >          <CheckBoxPreference              android:key="forceV3Signatures"              android:persistent="false" -            android:title="@string/label_force_v3_signature" /> +            android:title="@string/label_force_v3_signature"/>      </PreferenceCategory>  </PreferenceScreen> diff --git a/OpenKeychain/src/test/java/tests/PgpDecryptVerifyTest.java b/OpenKeychain/src/test/java/tests/PgpDecryptVerifyTest.java deleted file mode 100644 index d759bce05..000000000 --- a/OpenKeychain/src/test/java/tests/PgpDecryptVerifyTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package tests; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.*; -import org.openintents.openpgp.OpenPgpSignatureResult; -import org.sufficientlysecure.keychain.testsupport.PgpVerifyTestingHelper; - -@RunWith(RobolectricTestRunner.class) -@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19 -public class PgpDecryptVerifyTest { - -    @Test -    public void testVerifySuccess() throws Exception { - -        String testFileName = "/sample.txt"; -        int expectedSignatureResult = OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED; - -        int status = new PgpVerifyTestingHelper(Robolectric.application).doTestFile(testFileName); - -        Assert.assertEquals(expectedSignatureResult, status); -    } - -    @Test -    public void testVerifyFailure() throws Exception { - -        String testFileName = "/sample-altered.txt"; -        int expectedSignatureResult = OpenPgpSignatureResult.SIGNATURE_ERROR; - -        int status = new PgpVerifyTestingHelper(Robolectric.application).doTestFile(testFileName); - -        Assert.assertEquals(expectedSignatureResult, status); -    } - -} diff --git a/OpenKeychain/src/test/java/tests/ProviderHelperKeyringTest.java b/OpenKeychain/src/test/java/tests/ProviderHelperKeyringTest.java deleted file mode 100644 index 3d48c2f97..000000000 --- a/OpenKeychain/src/test/java/tests/ProviderHelperKeyringTest.java +++ /dev/null @@ -1,86 +0,0 @@ -package tests; - -import java.util.Collections; -import java.util.Arrays; -import java.util.Collection; -import java.util.ArrayList; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.*; -import org.openintents.openpgp.OpenPgpSignatureResult; -import org.sufficientlysecure.keychain.testsupport.KeyringTestingHelper; -import org.sufficientlysecure.keychain.testsupport.PgpVerifyTestingHelper; - -@RunWith(RobolectricTestRunner.class) -@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19 -public class ProviderHelperKeyringTest { - -    @Test -    public void testSavePublicKeyring() throws Exception { -        Assert.assertTrue(new KeyringTestingHelper(Robolectric.application).addKeyring(Collections.singleton( -                "/public-key-for-sample.blob" -        ))); -    } - -    @Test -    public void testSavePublicKeyringRsa() throws Exception { -        Assert.assertTrue(new KeyringTestingHelper(Robolectric.application).addKeyring(prependResourcePath(Arrays.asList( -                        "000001-006.public_key", -                        "000002-013.user_id", -                        "000003-002.sig", -                        "000004-012.ring_trust", -                        "000005-002.sig", -                        "000006-012.ring_trust", -                        "000007-002.sig", -                        "000008-012.ring_trust", -                        "000009-002.sig", -                        "000010-012.ring_trust", -                        "000011-002.sig", -                        "000012-012.ring_trust", -                        "000013-014.public_subkey", -                        "000014-002.sig", -                        "000015-012.ring_trust" -                )))); -    } - -    @Test -    public void testSavePublicKeyringDsa() throws Exception { -        Assert.assertTrue(new KeyringTestingHelper(Robolectric.application).addKeyring(prependResourcePath(Arrays.asList( -                        "000016-006.public_key", -                        "000017-002.sig", -                        "000018-012.ring_trust", -                        "000019-013.user_id", -                        "000020-002.sig", -                        "000021-012.ring_trust", -                        "000022-002.sig", -                        "000023-012.ring_trust", -                        "000024-014.public_subkey", -                        "000025-002.sig", -                        "000026-012.ring_trust" -                )))); -    } - -    @Test -    public void testSavePublicKeyringDsa2() throws Exception { -        Assert.assertTrue(new KeyringTestingHelper(Robolectric.application).addKeyring(prependResourcePath(Arrays.asList( -                        "000027-006.public_key", -                        "000028-002.sig", -                        "000029-012.ring_trust", -                        "000030-013.user_id", -                        "000031-002.sig", -                        "000032-012.ring_trust", -                        "000033-002.sig", -                        "000034-012.ring_trust" -                )))); -    } - -    private static Collection<String> prependResourcePath(Collection<String> files) { -        Collection<String> prependedFiles = new ArrayList<String>(); -        for (String file: files) { -            prependedFiles.add("/extern/OpenPGP-Haskell/tests/data/" + file); -        } -        return prependedFiles; -    } -} diff --git a/OpenKeychain/src/test/resources/extern/OpenPGP-Haskell b/OpenKeychain/src/test/resources/extern/OpenPGP-Haskell deleted file mode 160000 -Subproject eba7e4fdce3de6622b4ec3862b405b0acd01637 diff --git a/OpenKeychain/src/test/resources/public-key-for-sample.blob b/OpenKeychain/src/test/resources/public-key-for-sample.blob Binary files differdeleted file mode 100644 index 4aa91510b..000000000 --- a/OpenKeychain/src/test/resources/public-key-for-sample.blob +++ /dev/null diff --git a/OpenKeychain/src/test/resources/sample-altered.txt b/OpenKeychain/src/test/resources/sample-altered.txt deleted file mode 100644 index 458821f81..000000000 --- a/OpenKeychain/src/test/resources/sample-altered.txt +++ /dev/null @@ -1,26 +0,0 @@ ------BEGIN PGP SIGNED MESSAGE----- -Hash: SHA1 - -This is a simple text document, which is used to illustrate -the concept of signing simple text files.  There are no -control characters or special formatting commands in this -text, just simple printable ASCII characters. -MALICIOUS TEXT -To make this a slightly less uninteresting document, there -follows a short shopping list. - -    eggs, 1 doz -    milk, 1 gal -    bacon, 1 lb -    olive oil -    bread, 1 loaf - -That's all there is to this document. - ------BEGIN PGP SIGNATURE----- -Version: PGPfreeware 5.5.5 for non-commercial use <http://www.nai.com> - -iQA/AwUBN78ib3S9RCOKzj55EQKqDACg1NV2/iyPKrDlOVJvJwz6ArcQ0UQAnjNC -CDxKAFyaaGa835l1vpbFkAJk -=3r/N ------END PGP SIGNATURE----- diff --git a/OpenKeychain/src/test/resources/sample.txt b/OpenKeychain/src/test/resources/sample.txt deleted file mode 100644 index c0065f78d..000000000 --- a/OpenKeychain/src/test/resources/sample.txt +++ /dev/null @@ -1,26 +0,0 @@ ------BEGIN PGP SIGNED MESSAGE----- -Hash: SHA1 - -This is a simple text document, which is used to illustrate -the concept of signing simple text files.  There are no -control characters or special formatting commands in this -text, just simple printable ASCII characters. - -To make this a slightly less uninteresting document, there -follows a short shopping list. - -    eggs, 1 doz -    milk, 1 gal -    bacon, 1 lb -    olive oil -    bread, 1 loaf - -That's all there is to this document. - ------BEGIN PGP SIGNATURE----- -Version: PGPfreeware 5.5.5 for non-commercial use <http://www.nai.com> - -iQA/AwUBN78ib3S9RCOKzj55EQKqDACg1NV2/iyPKrDlOVJvJwz6ArcQ0UQAnjNC -CDxKAFyaaGa835l1vpbFkAJk -=3r/N ------END PGP SIGNATURE-----  | 
