diff options
41 files changed, 2840 insertions, 2595 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 03a653c66..f9a9e3f13 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -17,7 +17,7 @@  <manifest      xmlns:android="http://schemas.android.com/apk/res/android"      package="org.thialfihar.android.apg" -    android:versionName="0.9.5" android:versionCode="10"> +    android:versionCode="15" android:versionName="1.0.1">      <application          android:icon="@drawable/icon" @@ -53,12 +53,28 @@          <activity              android:name=".SelectPublicKeyListActivity"              android:label="@string/title_selectRecipients" -            android:configChanges="keyboardHidden|orientation|keyboard"/> +            android:configChanges="keyboardHidden|orientation|keyboard"> + +            <intent-filter> +                <action android:name="org.thialfihar.android.apg.intent.SELECT_PUBLIC_KEYS" /> +                <category android:name="android.intent.category.DEFAULT"/> +                <data android:mimeType="text/*"/> +            </intent-filter> + +        </activity>          <activity              android:name=".SelectSecretKeyListActivity"              android:label="@string/title_selectSignature" -            android:configChanges="keyboardHidden|orientation|keyboard"/> +            android:configChanges="keyboardHidden|orientation|keyboard"> + +            <intent-filter> +                <action android:name="org.thialfihar.android.apg.intent.SELECT_SECRET_KEY" /> +                <category android:name="android.intent.category.DEFAULT"/> +                <data android:mimeType="text/*"/> +            </intent-filter> + +        </activity>          <activity              android:name=".EncryptActivity" @@ -68,6 +84,9 @@              <intent-filter>                  <action android:name="org.thialfihar.android.apg.intent.ENCRYPT" />                  <action android:name="org.thialfihar.android.apg.intent.ENCRYPT_FILE" /> +                <action android:name="org.thialfihar.android.apg.intent.ENCRYPT_AND_RETURN" /> +                <category android:name="android.intent.category.DEFAULT"/> +                <data android:mimeType="text/*"/>              </intent-filter>          </activity> @@ -92,6 +111,9 @@              <intent-filter>                  <action android:name="org.thialfihar.android.apg.intent.DECRYPT" />                  <action android:name="org.thialfihar.android.apg.intent.DECRYPT_FILE" /> +                <action android:name="org.thialfihar.android.apg.intent.DECRYPT_AND_RETURN" /> +                <category android:name="android.intent.category.DEFAULT"/> +                <data android:mimeType="text/*"/>              </intent-filter>          </activity> @@ -106,13 +128,22 @@              android:label="@string/title_preferences"              android:configChanges="keyboardHidden|orientation|keyboard"/> +        <service android:name=".Service" /> +          <provider +            android:readPermission="org.thialfihar.android.apg.permission.READ_KEY_DETAILS"              android:name="org.thialfihar.android.apg.provider.DataProvider" -            android:authorities="org.thialfihar.android.apg.provider" /> +            android:authorities="org.thialfihar.android.apg.provider"/>      </application>      <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="5" /> -<uses-permission android:name="com.google.android.providers.gmail.permission.READ_GMAIL" /> -<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> -</manifest>
\ No newline at end of file +    <permission android:name="org.thialfihar.android.apg.permission.READ_KEY_DETAILS" +                android:protectionLevel="dangerous" +                android:label="@string/permission_read_key_details_label" +                android:description="@string/permission_read_key_details_description"/> + +    <uses-permission android:name="com.google.android.providers.gmail.permission.READ_GMAIL" /> +    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + +</manifest> diff --git a/res/layout/decrypt.xml b/res/layout/decrypt.xml index 03e2f6311..41bbee479 100644 --- a/res/layout/decrypt.xml +++ b/res/layout/decrypt.xml @@ -77,6 +77,7 @@                      <EditText                          android:id="@+id/message"                          android:inputType="text|textCapSentences|textMultiLine|textLongMessage" +                        android:scrollHorizontally="true"                          android:layout_width="fill_parent"                          android:layout_height="fill_parent"                          android:gravity="top"/> diff --git a/res/layout/select_public_key_item.xml b/res/layout/select_public_key_item.xml index bb0dd30a4..beca23176 100644 --- a/res/layout/select_public_key_item.xml +++ b/res/layout/select_public_key_item.xml @@ -70,20 +70,6 @@              android:layout_height="fill_parent"/>          <TextView -            android:id="@+id/creation" -            android:textAppearance="?android:attr/textAppearanceSmall" -            android:text="31.12.2009" -            android:layout_width="wrap_content" -            android:layout_height="wrap_content"/> - -        <TextView -            android:id="@+id/expiry" -            android:textAppearance="?android:attr/textAppearanceSmall" -            android:text="31.12.2010" -            android:layout_width="wrap_content" -            android:layout_height="wrap_content"/> - -        <TextView              android:id="@+id/status"              android:textAppearance="?android:attr/textAppearanceSmall"              android:text="expired" diff --git a/res/layout/select_secret_key_item.xml b/res/layout/select_secret_key_item.xml index 35bf58e23..022545152 100644 --- a/res/layout/select_secret_key_item.xml +++ b/res/layout/select_secret_key_item.xml @@ -63,20 +63,6 @@              android:layout_height="fill_parent"/>          <TextView -            android:id="@+id/creation" -            android:textAppearance="?android:attr/textAppearanceSmall" -            android:text="31.12.2009" -            android:layout_width="wrap_content" -            android:layout_height="wrap_content"/> - -        <TextView -            android:id="@+id/expiry" -            android:textAppearance="?android:attr/textAppearanceSmall" -            android:text="31.12.2010" -            android:layout_width="wrap_content" -            android:layout_height="wrap_content"/> - -        <TextView              android:id="@+id/status"              android:textAppearance="?android:attr/textAppearanceSmall"              android:text="expired" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 5bb4dc2ba..008981b0e 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -38,10 +38,12 @@      <string name="title_importKeys">Import Keys</string>      <string name="title_exportKey">Export Key</string>      <string name="title_exportKeys">Export Keys</string> +    <string name="title_keyNotFound">Key Not Found</string>      <!-- section_lowerCase: capitalized words, no punctuation -->      <string name="section_userIds">User IDs</string>      <string name="section_keys">Keys</string> +    <string name="section_general">General</string>      <string name="section_defaults">Defaults</string>      <!-- btn_lowerCase: capitalized words, no punctuation --> @@ -92,6 +94,9 @@      <string name="label_hashAlgorithm">Hash Algorithm</string>      <string name="label_asymmetric">Public Key</string>      <string name="label_symmetric">Pass Phrase</string> +    <string name="label_passPhraseCacheTtl">Pass Phrase Cache</string> +    <string name="label_messageCompression">Message Compression</string> +    <string name="label_fileCompression">File Compression</string>      <string name="noKeysSelected">Select</string>      <string name="oneKeySelected">1 Selected</string> @@ -108,9 +113,16 @@      <string name="notValid">not valid</string>      <!-- choice_lowerCase: capitalized firwst word, no punctuation --> +    <string name="choice_none">None</string>      <string name="choice_signOnly">Sign only</string>      <string name="choice_encryptOnly">Encrypt only</string>      <string name="choice_signAndEncrypt">Sign and Encrypt</string> +    <string name="choice_15secs">15 secs</string> +    <string name="choice_1min">1 min</string> +    <string name="choice_3mins">3 mins</string> +    <string name="choice_5mins">5 mins</string> +    <string name="choice_10mins">10 mins</string> +    <string name="choice_untilQuit">until quit</string>      <string name="dsa">DSA</string>      <string name="elgamal">ElGamal</string> @@ -133,7 +145,7 @@      <string name="usingClipboardContent">Using clipboard content.</string>      <string name="keySaved">Key saved.</string>      <string name="setAPassPhrase">Set a pass phrase via the option menu first.</string> -    <string name="oiFilemanagerNotInstalled">OI File Manager not installed.</string> +    <string name="noFilemanagerInstalled">No compatible file manager installed.</string>      <string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string>      <string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>      <string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string> @@ -150,19 +162,20 @@      <string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string>      <string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string>      <string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string> -    <string name="specifyFileToImportFrom">Please specify which file to import from.</string> +    <string name="specifyFileToImportFrom">Please specify which file to import keys from. (.asc or .gpg)</string>      <string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string>      <string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string>      <string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string>      <string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string> -    <string name="keysAddedAndUpdated">Succssfully added %s keys and updated %s keys."</string> -    <string name="keysAdded">Succssfully added %s keys.</string> -    <string name="keysUpdated">Succssfully updated %s keys.</string> +    <string name="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string> +    <string name="keysAdded">Succssfully added %s key(s).</string> +    <string name="keysUpdated">Succssfully updated %s key(s).</string>      <string name="noKeysAddedOrUpdated">No keys added or updated.</string>      <string name="keyExported">Succssfully exported 1 key.</string>      <string name="keysExported">Succssfully exported %s keys.</string>      <string name="noKeysExported">No keys exported.</string>      <string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string> +    <string name="keyNotFound">Couldn't find key %08X.</string>      <!-- error_lowerCase: phrases, no punctuation, all lowercase,           they will be put after "errorMessage", e.g. "Error: file not found" --> @@ -227,4 +240,3 @@      <string name="progress_verifyingIntegrity">verifying integrity...</string>  </resources> - diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 5bb4dc2ba..008981b0e 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -38,10 +38,12 @@      <string name="title_importKeys">Import Keys</string>      <string name="title_exportKey">Export Key</string>      <string name="title_exportKeys">Export Keys</string> +    <string name="title_keyNotFound">Key Not Found</string>      <!-- section_lowerCase: capitalized words, no punctuation -->      <string name="section_userIds">User IDs</string>      <string name="section_keys">Keys</string> +    <string name="section_general">General</string>      <string name="section_defaults">Defaults</string>      <!-- btn_lowerCase: capitalized words, no punctuation --> @@ -92,6 +94,9 @@      <string name="label_hashAlgorithm">Hash Algorithm</string>      <string name="label_asymmetric">Public Key</string>      <string name="label_symmetric">Pass Phrase</string> +    <string name="label_passPhraseCacheTtl">Pass Phrase Cache</string> +    <string name="label_messageCompression">Message Compression</string> +    <string name="label_fileCompression">File Compression</string>      <string name="noKeysSelected">Select</string>      <string name="oneKeySelected">1 Selected</string> @@ -108,9 +113,16 @@      <string name="notValid">not valid</string>      <!-- choice_lowerCase: capitalized firwst word, no punctuation --> +    <string name="choice_none">None</string>      <string name="choice_signOnly">Sign only</string>      <string name="choice_encryptOnly">Encrypt only</string>      <string name="choice_signAndEncrypt">Sign and Encrypt</string> +    <string name="choice_15secs">15 secs</string> +    <string name="choice_1min">1 min</string> +    <string name="choice_3mins">3 mins</string> +    <string name="choice_5mins">5 mins</string> +    <string name="choice_10mins">10 mins</string> +    <string name="choice_untilQuit">until quit</string>      <string name="dsa">DSA</string>      <string name="elgamal">ElGamal</string> @@ -133,7 +145,7 @@      <string name="usingClipboardContent">Using clipboard content.</string>      <string name="keySaved">Key saved.</string>      <string name="setAPassPhrase">Set a pass phrase via the option menu first.</string> -    <string name="oiFilemanagerNotInstalled">OI File Manager not installed.</string> +    <string name="noFilemanagerInstalled">No compatible file manager installed.</string>      <string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string>      <string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>      <string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string> @@ -150,19 +162,20 @@      <string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string>      <string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string>      <string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string> -    <string name="specifyFileToImportFrom">Please specify which file to import from.</string> +    <string name="specifyFileToImportFrom">Please specify which file to import keys from. (.asc or .gpg)</string>      <string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string>      <string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string>      <string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string>      <string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string> -    <string name="keysAddedAndUpdated">Succssfully added %s keys and updated %s keys."</string> -    <string name="keysAdded">Succssfully added %s keys.</string> -    <string name="keysUpdated">Succssfully updated %s keys.</string> +    <string name="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string> +    <string name="keysAdded">Succssfully added %s key(s).</string> +    <string name="keysUpdated">Succssfully updated %s key(s).</string>      <string name="noKeysAddedOrUpdated">No keys added or updated.</string>      <string name="keyExported">Succssfully exported 1 key.</string>      <string name="keysExported">Succssfully exported %s keys.</string>      <string name="noKeysExported">No keys exported.</string>      <string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string> +    <string name="keyNotFound">Couldn't find key %08X.</string>      <!-- error_lowerCase: phrases, no punctuation, all lowercase,           they will be put after "errorMessage", e.g. "Error: file not found" --> @@ -227,4 +240,3 @@      <string name="progress_verifyingIntegrity">verifying integrity...</string>  </resources> - diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 5bb4dc2ba..008981b0e 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -38,10 +38,12 @@      <string name="title_importKeys">Import Keys</string>      <string name="title_exportKey">Export Key</string>      <string name="title_exportKeys">Export Keys</string> +    <string name="title_keyNotFound">Key Not Found</string>      <!-- section_lowerCase: capitalized words, no punctuation -->      <string name="section_userIds">User IDs</string>      <string name="section_keys">Keys</string> +    <string name="section_general">General</string>      <string name="section_defaults">Defaults</string>      <!-- btn_lowerCase: capitalized words, no punctuation --> @@ -92,6 +94,9 @@      <string name="label_hashAlgorithm">Hash Algorithm</string>      <string name="label_asymmetric">Public Key</string>      <string name="label_symmetric">Pass Phrase</string> +    <string name="label_passPhraseCacheTtl">Pass Phrase Cache</string> +    <string name="label_messageCompression">Message Compression</string> +    <string name="label_fileCompression">File Compression</string>      <string name="noKeysSelected">Select</string>      <string name="oneKeySelected">1 Selected</string> @@ -108,9 +113,16 @@      <string name="notValid">not valid</string>      <!-- choice_lowerCase: capitalized firwst word, no punctuation --> +    <string name="choice_none">None</string>      <string name="choice_signOnly">Sign only</string>      <string name="choice_encryptOnly">Encrypt only</string>      <string name="choice_signAndEncrypt">Sign and Encrypt</string> +    <string name="choice_15secs">15 secs</string> +    <string name="choice_1min">1 min</string> +    <string name="choice_3mins">3 mins</string> +    <string name="choice_5mins">5 mins</string> +    <string name="choice_10mins">10 mins</string> +    <string name="choice_untilQuit">until quit</string>      <string name="dsa">DSA</string>      <string name="elgamal">ElGamal</string> @@ -133,7 +145,7 @@      <string name="usingClipboardContent">Using clipboard content.</string>      <string name="keySaved">Key saved.</string>      <string name="setAPassPhrase">Set a pass phrase via the option menu first.</string> -    <string name="oiFilemanagerNotInstalled">OI File Manager not installed.</string> +    <string name="noFilemanagerInstalled">No compatible file manager installed.</string>      <string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string>      <string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>      <string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string> @@ -150,19 +162,20 @@      <string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string>      <string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string>      <string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string> -    <string name="specifyFileToImportFrom">Please specify which file to import from.</string> +    <string name="specifyFileToImportFrom">Please specify which file to import keys from. (.asc or .gpg)</string>      <string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string>      <string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string>      <string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string>      <string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string> -    <string name="keysAddedAndUpdated">Succssfully added %s keys and updated %s keys."</string> -    <string name="keysAdded">Succssfully added %s keys.</string> -    <string name="keysUpdated">Succssfully updated %s keys.</string> +    <string name="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string> +    <string name="keysAdded">Succssfully added %s key(s).</string> +    <string name="keysUpdated">Succssfully updated %s key(s).</string>      <string name="noKeysAddedOrUpdated">No keys added or updated.</string>      <string name="keyExported">Succssfully exported 1 key.</string>      <string name="keysExported">Succssfully exported %s keys.</string>      <string name="noKeysExported">No keys exported.</string>      <string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string> +    <string name="keyNotFound">Couldn't find key %08X.</string>      <!-- error_lowerCase: phrases, no punctuation, all lowercase,           they will be put after "errorMessage", e.g. "Error: file not found" --> @@ -227,4 +240,3 @@      <string name="progress_verifyingIntegrity">verifying integrity...</string>  </resources> - diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml new file mode 100644 index 000000000..8bb198ad6 --- /dev/null +++ b/res/values-sl/strings.xml @@ -0,0 +1,242 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org> + +     Licensed under the Apache License, Version 2.0 (the "License"); +     you may not use this file except in compliance with the License. +     You may obtain a copy of the License at + +          http://www.apache.org/licenses/LICENSE-2.0 + +     Unless required by applicable law or agreed to in writing, software +     distributed under the License is distributed on an "AS IS" BASIS, +     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +     See the License for the specific language governing permissions and +     limitations under the License. +--> + +<resources> +    <string name="app_name">APG</string> + +    <!-- title_lowerCase: capitalized words, no punctuation --> +    <string name="title_mailInbox">Poštni nabiralnik</string> +    <string name="title_managePublicKeys">Upravljanje javnih ključev</string> +    <string name="title_manageSecretKeys">Upravljanje zasebnih ključev</string> +    <string name="title_selectRecipients">Izberi prejemnike</string> +    <string name="title_selectSignature">Izberi podpis</string> +    <string name="title_encrypt">Šifriraj</string> +    <string name="title_decrypt">Dešifriraj</string> +    <string name="title_authentification">Avtentikacija</string> +    <string name="title_createKey">Ustvari ključ</string> +    <string name="title_editKey">Uredi ključ</string> +    <string name="title_preferences">Nastavitve</string> +    <string name="title_changePassPhrase">Spremeni geslo</string> +    <string name="title_setPassPhrase">Določi geslo</string> +    <string name="title_sendEmail">"Pošlji e-pošto..."</string> +    <string name="title_encryptToFile">Šifriraj v datoteko</string> +    <string name="title_decryptToFile">Dešifriraj v datoteko</string> +    <string name="title_addAccount">Dodaj račun</string> +    <string name="title_importKeys">Uvozi ključe</string> +    <string name="title_exportKey">Izvozi ključ</string> +    <string name="title_exportKeys">Izvozi ključe</string> +    <string name="title_keyNotFound">Ključ ni bil najden</string> + +    <!-- section_lowerCase: capitalized words, no punctuation --> +    <string name="section_userIds">Uporabniške identitete</string> +    <string name="section_keys">Ključi</string> +    <string name="section_general">Splošno</string> +    <string name="section_defaults">Privzete nastavitve</string> + +    <!-- btn_lowerCase: capitalized words, no punctuation --> +    <string name="btn_encryptToClipboard">Šifriraj v odložišče</string> +    <string name="btn_send">Šifriraj in pošlji</string> +    <string name="btn_encrypt">Šifriraj</string> +    <string name="btn_decrypt">Dešifriraj</string> +    <string name="btn_verify">Overi</string> +    <string name="btn_selectEncryptKeys">Izberi prejemnike</string> +    <string name="btn_reply">Odgovori</string> +    <string name="btn_encryptMessage">Šifriraj sporočilo</string> +    <string name="btn_decryptMessage">Dešifriraj sporočilo</string> +    <string name="btn_encryptFile">Šifriraj datoteko</string> +    <string name="btn_decryptFile">Dešifriraj datoteko</string> +    <string name="btn_save">Shrani</string> +    <string name="btn_doNotSave">Prekliči</string> +    <string name="btn_delete">Izbriši</string> +    <string name="btn_noDate">Brez</string> + +    <!-- menu_lowerCase: capitalized words, no punctuation --> +    <string name="menu_about">O programu</string> +    <string name="menu_addAccount">Dodaj GMail račun</string> +    <string name="menu_deleteAccount">Izbriši račun</string> +    <string name="menu_managePublicKeys">Upravljanje javnih ključev</string> +    <string name="menu_manageSecretKeys">Upravljanje zasebnih ključev</string> +    <string name="menu_preferences">Nastavitve</string> +    <string name="menu_changePassPhrase">Spremeni geslo</string> +    <string name="menu_setPassPhrase">Določi geslo</string> +    <string name="menu_importKeys">Uvozi ključe</string> +    <string name="menu_exportKeys">Izvozi ključe</string> +    <string name="menu_exportKey">Izvozi ključ</string> +    <string name="menu_deleteKey">Izbriši ključ</string> +    <string name="menu_createKey">Ustvari ključ</string> +    <string name="menu_editKey">Uredi ključ</string> + +    <!-- label_lowerCase: capitalized words, no punctuation --> +    <string name="label_sign">Podpiši</string> +    <string name="label_message">Sporočilo</string> +    <string name="label_file">Datoteka</string> +    <string name="label_passPhrase">Geslo</string> +    <string name="label_passPhraseAgain">Ponovi</string> +    <string name="label_algorithm">Algoritem</string> +    <string name="label_asciiArmour">ASCII Armour</string> +    <string name="label_selectPublicKeys">Javni ključ(i)</string> +    <string name="label_deleteAfterEncryption">Po šifriranju izbriši</string> +    <string name="label_deleteAfterDecryption">Po dešifriranju izbriši</string> +    <string name="label_encryptionAlgorithm">Šifrirni algoritem</string> +    <string name="label_hashAlgorithm">Hash algoritem</string> +    <string name="label_asymmetric">Javni ključ</string> +    <string name="label_symmetric">Geslo</string> +    <string name="label_passPhraseCacheTtl">Predpomnilnik gesel</string> +    <string name="label_messageCompression">Zgoščevanje sporočil</string> +    <string name="label_fileCompression">Zgoščevanje datotek</string> + +    <string name="noKeysSelected">Izberi</string> +    <string name="oneKeySelected">1 izbran</string> +    <string name="nKeysSelected">Izbrani</string> +    <string name="unknownUserId"><nepoznan></string> +    <string name="none"><brez></string> +    <string name="noKey"><brez ključa></string> +    <string name="noDate">-</string> +    <string name="noExpiry"><nikoli></string> +    <string name="unknownStatus"></string> +    <string name="canEncrypt">lahko šifrira</string> +    <string name="canSign">lahko podpiše</string> +    <string name="expired">potečeno</string> +    <string name="notValid">neveljavno</string> + +    <!-- choice_lowerCase: capitalized firwst word, no punctuation --> +    <string name="choice_none">Brez</string> +    <string name="choice_signOnly">Samo podpis</string> +    <string name="choice_encryptOnly">Samo šifriranje</string> +    <string name="choice_signAndEncrypt">Podpis in šifriranje</string> +    <string name="choice_15secs">15 sek</string> +    <string name="choice_1min">1 min</string> +    <string name="choice_3mins">3 min</string> +    <string name="choice_5mins">5 min</string> +    <string name="choice_10mins">10 min</string> +    <string name="choice_untilQuit">do izhoda</string> + +    <string name="dsa">DSA</string> +    <string name="elgamal">ElGamal</string> +    <string name="rsa">RSA</string> + +    <string name="filemanager_titleOpen">Odpri...</string> +    <string name="filemanager_titleSave">Shrani kot...</string> +    <string name="filemanager_titleEncrypt">Izberi datoteko za šifriranje...</string> +    <string name="filemanager_titleDecrypt">Izberi datoteko za dešifriranje...</string> +    <string name="filemanager_btnOpen">Odpri</string> +    <string name="filemanager_btnSave">Shrani</string> + +    <string name="warning">Opozorilo</string> +    <string name="error">Napaka</string> +    <string name="warningMessage">Opozorilo: %s</string> +    <string name="errorMessage">Napaka: %s</string> + +    <!-- sentences --> +    <string name="wrongPassPhrase">Napačno geslo.</string> +    <string name="usingClipboardContent">Uporabljam vsebino odložišča.</string> +    <string name="keySaved">Ključ shranjen.</string> +    <string name="setAPassPhrase">Najprej preko menija možnosti določite geslo.</string> +    <string name="noFilemanagerInstalled">Nameščen ni noben združljiv upravitelj datotek.</string> +    <string name="passPhrasesDoNotMatch">Gesli se ne ujemata.</string> +    <string name="passPhraseMustNotBeEmpty">Prazna gesla niso dovoljena.</string> +    <string name="passPhraseForSymmetricEncryption">Geslo za simetrično enkripcijo:</string> +    <string name="passPhraseFor">Geslo za %s:</string> +    <string name="fileDeleteConfirmation">Ali ste prepričani, da želite izbrisati\n%s?</string> +    <string name="fileDeleteSuccessful">Uspešno izbrisano.</string> +    <string name="noFileSelected">Najprej izberite datoteko.</string> +    <string name="decryptionSuccessful">Uspešno dešifrirano.</string> +    <string name="encryptionSuccessful">Uspešno šifrirano.</string> +    <string name="encryptionToClipboardSuccessful">Uspešno šifrirano v odložišče.</string> +    <string name="enterPassPhraseTwice">Vstavite geslo dvakrat.</string> +    <string name="selectEncryptionKey">Izberite vsaj en šifrirni ključ.</string> +    <string name="selectEncryptionOrSignatureKey">Izberite vsaj en šifrirni ključ ali ključ za podpis.</string> +    <string name="specifyFileToEncryptTo">Določite datoteko v katero želite šifrirati.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.</string> +    <string name="specifyFileToDecryptTo">Določite datoteko v katero želite dešifrirati.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.</string> +    <string name="specifyGoogleMailAccount">Določite Google Mail račun, ki ga želite dodati.</string> +    <string name="specifyFileToImportFrom">Določite iz katere datoteke želite uvoziti ključe. (.asc ali .gpg)</string> +    <string name="specifyFileToExportTo">Določite v katero datoteko želite izvoziti.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.</string> +    <string name="specifyFileToExportSecretKeysTo">Določite v katero datoteko želite izvoziti.\nPOZOR! Izvozili boste ZASEBNI ključ.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.</string> +    <string name="keyDeletionConfirmation">Ali zares želite izbrisati ključ '%s'?\nTega ne boste mogli popraviti!</string> +    <string name="secretKeyDeletionConfirmation">Ali zares želite izbrisati ZASEBNI ključ '%s'?\nTega ne boste mogli popraviti!</string> +    <string name="keysAddedAndUpdated">Uspešno dodani ključi: %s. Uspešno posodobljeni ključi: %s."</string> +    <string name="keysAdded">Uspešno dodani ključi: %s.</string> +    <string name="keysUpdated">Uspešno posodobljeni ključi: %s.</string> +    <string name="noKeysAddedOrUpdated">Noben ključ ni bil dodan ali posodobljen.</string> +    <string name="keyExported">Uspešno izvožen 1 ključ.</string> +    <string name="keysExported">Uspešno izvoženi ključi: %s</string> +    <string name="noKeysExported">Noben ključ ni bil izvožen.</string> +    <string name="keyCreationElGamalInfo">Opomba: le podključi podpirajo ElGamal. Za ElGamal bo uporabljena velikost najbližja 1536, 2048, 3072, 4096, ali 8192.</string> +    <string name="keyNotFound">Ne najdem ključa %08X.</string> + +    <!-- error_lowerCase: phrases, no punctuation, all lowercase, +         they will be put after "errorMessage", e.g. "Error: file not found" --> +    <string name="error_fileDeleteFailed">izbris '%s' ni uspel</string> +    <string name="error_fileNotFound">ne najdem datoteke</string> +    <string name="error_noSecretKeyFound">najden ni bil noben ustrezen zasebni kluč</string> +    <string name="error_noKnownEncryptionFound">najdena ni bila nobena poznana vrsta enkripcije</string> +    <string name="error_externalStorageNotReady">zunanji pomnilnik ni pripravljen</string> +    <string name="error_accountNotFound">račun '%s' ni najden</string> +    <string name="error_addingAccountFailed">dodajanje računa '%s' ni uspelo</string> +    <string name="error_invalidEmail">neveljaven e-naslov '%s'</string> +    <string name="error_keySizeMinimum512bit">velikost ključa mora biti vsaj 512bit</string> +    <string name="error_masterKeyMustNotBeElGamal">statični ključ ne more biti ključ ElGamal</string> +    <string name="error_unknownAlgorithmChoice">neznana izbira algoritma</string> +    <string name="error_userIdNeedsAName">določiti morate ime</string> +    <string name="error_userIdNeedsAnEmailAddress">določiti morate naslov e-pošte</string> +    <string name="error_keyNeedsAUserId">potrebujem vsaj eno uporabniško identiteto</string> +    <string name="error_mainUserIdMustNotBeEmpty">glavna uporabniška identiteta ne more biti prazna</string> +    <string name="error_keyNeedsMasterKey">potrebujem vsaj statični ključ</string> +    <string name="error_expiryMustComeAfterCreation">datum poteka mora biti kasnejši od datuma nastanka</string> +    <string name="error_noEncryptionKeysOrPassPhrase">dan ni bil noben šifrirni ključ ali geslo</string> +    <string name="error_signatureFailed">podpis ni bil uspešen</string> +    <string name="error_noSignaturePassPhrase">dano ni bilo nobeno geslo</string> +    <string name="error_noSignatureKey">dan ni bil noben podpisni ključ</string> +    <string name="error_invalidData">neveljavni šifrirni podatki</string> +    <string name="error_corruptData">pokvarjeni podatki</string> +    <string name="error_noSymmetricEncryptionPacket">ne najdem podatkov s simetrično enkripcijo</string> +    <string name="error_wrongPassPhrase">napačno geslo</string> +    <string name="error_savingKeys">napaka pri shranjevanju nakaterih ključev</string> + +    <!-- progress_lowerCase: lowercase, phrases, usually ending in '...' --> +    <string name="progress_done">končano.</string> +    <string name="progress_initializing">inicializiram...</string> +    <string name="progress_saving">shranjujem...</string> +    <string name="progress_importing">uvažam...</string> +    <string name="progress_exporting">izvažam...</string> +    <string name="progress_generating">generiram ključ, to lahko traja nekaj časa...</string> +    <string name="progress_buildingKey">gradim ključ...</string> +    <string name="progress_preparingMasterKey">pripravljam statični ključ...</string> +    <string name="progress_certifyingMasterKey">potrjujem statični ključ...</string> +    <string name="progress_buildingMasterKeyRing">gradim datoteko s statičnimi ključi...</string> +    <string name="progress_addingSubKeys">dodajam podključe...</string> +    <string name="progress_savingKeyRing">shranjujem datoteko s ključi...</string> +    <string name="progress_importingSecretKeys">uvažam zasebne ključe...</string> +    <string name="progress_importingPublicKeys">uvažam javne ključe...</string> +    <string name="progress_reloadingKeys">reloading keys...</string> +    <string name="progress_exportingKey">izvažam ključ...</string> +    <string name="progress_exportingKeys">izvažam ključe...</string> +    <string name="progress_extractingSignatureKey">izvlačim podpisni kluč...</string> +    <string name="progress_extractingKey">izvlačim ključ...</string> +    <string name="progress_preparingStreams">pripravljam tok...</string> +    <string name="progress_encrypting">šifriram podatke...</string> +    <string name="progress_decrypting">dešifriram podatke...</string> +    <string name="progress_preparingSignature">pripravljam podpis...</string> +    <string name="progress_generatingSignature">generiram podpis...</string> +    <string name="progress_processingSignature">obdelujem podpis...</string> +    <string name="progress_verifyingSignature">overovljam podpis...</string> +    <string name="progress_signing">podpisujem...</string> +    <string name="progress_readingData">berem podatke...</string> +    <string name="progress_findingKey">iščem ključ...</string> +    <string name="progress_decompressingData">raztezam podatke...</string> +    <string name="progress_verifyingIntegrity">overovljam integriteto...</string> + +</resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index 856ed0c4e..72c1d1f9c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -38,6 +38,7 @@      <string name="title_importKeys">Import Keys</string>      <string name="title_exportKey">Export Key</string>      <string name="title_exportKeys">Export Keys</string> +    <string name="title_keyNotFound">Key Not Found</string>      <!-- section_lowerCase: capitalized words, no punctuation -->      <string name="section_userIds">User IDs</string> @@ -144,7 +145,7 @@      <string name="usingClipboardContent">Using clipboard content.</string>      <string name="keySaved">Key saved.</string>      <string name="setAPassPhrase">Set a pass phrase via the option menu first.</string> -    <string name="oiFilemanagerNotInstalled">OI File Manager not installed.</string> +    <string name="noFilemanagerInstalled">No compatible file manager installed.</string>      <string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string>      <string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>      <string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string> @@ -161,19 +162,20 @@      <string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string>      <string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string>      <string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string> -    <string name="specifyFileToImportFrom">Please specify which file to import from.</string> +    <string name="specifyFileToImportFrom">Please specify which file to import keys from. (.asc or .gpg)</string>      <string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string>      <string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string>      <string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string>      <string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string> -    <string name="keysAddedAndUpdated">Succssfully added %s keys and updated %s keys."</string> -    <string name="keysAdded">Succssfully added %s keys.</string> -    <string name="keysUpdated">Succssfully updated %s keys.</string> +    <string name="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string> +    <string name="keysAdded">Succssfully added %s key(s).</string> +    <string name="keysUpdated">Succssfully updated %s key(s).</string>      <string name="noKeysAddedOrUpdated">No keys added or updated.</string>      <string name="keyExported">Succssfully exported 1 key.</string>      <string name="keysExported">Succssfully exported %s keys.</string>      <string name="noKeysExported">No keys exported.</string>      <string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string> +    <string name="keyNotFound">Couldn't find key %08X.</string>      <!-- error_lowerCase: phrases, no punctuation, all lowercase,           they will be put after "errorMessage", e.g. "Error: file not found" --> @@ -237,5 +239,9 @@      <string name="progress_decompressingData">decompressing data...</string>      <string name="progress_verifyingIntegrity">verifying integrity...</string> +    <!-- permission strings --> +    <string name="permission_read_key_details_label">Read key details from APG.</string> +    <string name="permission_read_key_details_description">Read of public and secret keys stored in APG, such as key ID and user IDs. The keys themselves can NOT be read.</string> +  </resources> diff --git a/src/org/openintents/intents/FileManager.java b/src/org/openintents/intents/FileManager.java deleted file mode 100644 index 3a5cc0d86..000000000 --- a/src/org/openintents/intents/FileManager.java +++ /dev/null @@ -1,78 +0,0 @@ -/*
 - * Copyright (C) 2008 OpenIntents.org
 - *
 - * Licensed under the Apache License, Version 2.0 (the "License");
 - * you may not use this file except in compliance with the License.
 - * You may obtain a copy of the License at
 - *
 - *      http://www.apache.org/licenses/LICENSE-2.0
 - *
 - * Unless required by applicable law or agreed to in writing, software
 - * distributed under the License is distributed on an "AS IS" BASIS,
 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 - * See the License for the specific language governing permissions and
 - * limitations under the License.
 - */
 -
 -package org.openintents.intents;
 -
 -// Version Dec 9, 2008
 -
 -/**
 - * Provides OpenIntents actions, extras, and categories used by providers.
 - * <p>
 - * These specifiers extend the standard Android specifiers.
 - * </p>
 - */
 -public final class FileManager {
 -
 -    /**
 -     * Activity Action: Pick a file through the file manager, or let user
 -     * specify a custom file name. Data is the current file name or file name
 -     * suggestion. Returns a new file name as file URI in data.
 -     *
 -     * <p>
 -     * Constant Value: "org.openintents.action.PICK_FILE"
 -     * </p>
 -     */
 -    public static final String ACTION_PICK_FILE = "org.openintents.action.PICK_FILE";
 -
 -    /**
 -     * Activity Action: Pick a directory through the file manager, or let user
 -     * specify a custom file name. Data is the current directory name or
 -     * directory name suggestion. Returns a new directory name as file URI in
 -     * data.
 -     *
 -     * <p>
 -     * Constant Value: "org.openintents.action.PICK_DIRECTORY"
 -     * </p>
 -     */
 -    public static final String ACTION_PICK_DIRECTORY = "org.openintents.action.PICK_DIRECTORY";
 -
 -    /**
 -     * The title to display.
 -     *
 -     * <p>
 -     * This is shown in the title bar of the file manager.
 -     * </p>
 -     *
 -     * <p>
 -     * Constant Value: "org.openintents.extra.TITLE"
 -     * </p>
 -     */
 -    public static final String EXTRA_TITLE = "org.openintents.extra.TITLE";
 -
 -    /**
 -     * The text on the button to display.
 -     *
 -     * <p>
 -     * Depending on the use, it makes sense to set this to "Open" or "Save".
 -     * </p>
 -     *
 -     * <p>
 -     * Constant Value: "org.openintents.extra.BUTTON_TEXT"
 -     * </p>
 -     */
 -    public static final String EXTRA_BUTTON_TEXT = "org.openintents.extra.BUTTON_TEXT";
 -
 -}
 diff --git a/src/org/thialfihar/android/apg/Apg.java b/src/org/thialfihar/android/apg/Apg.java index e290a501e..3c35064a4 100644 --- a/src/org/thialfihar/android/apg/Apg.java +++ b/src/org/thialfihar/android/apg/Apg.java @@ -19,6 +19,7 @@ package org.thialfihar.android.apg;  import java.io.BufferedInputStream;
  import java.io.ByteArrayInputStream;
  import java.io.ByteArrayOutputStream;
 +import java.io.EOFException;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileNotFoundException;
 @@ -35,8 +36,6 @@ import java.security.SecureRandom;  import java.security.Security;
  import java.security.SignatureException;
  import java.util.Calendar;
 -import java.util.Collections;
 -import java.util.Comparator;
  import java.util.Date;
  import java.util.GregorianCalendar;
  import java.util.HashMap;
 @@ -80,18 +79,19 @@ import org.bouncycastle2.openpgp.PGPSignatureList;  import org.bouncycastle2.openpgp.PGPSignatureSubpacketGenerator;
  import org.bouncycastle2.openpgp.PGPSignatureSubpacketVector;
  import org.bouncycastle2.openpgp.PGPUtil;
 -import org.thialfihar.android.apg.provider.PublicKeys;
 -import org.thialfihar.android.apg.provider.SecretKeys;
 +import org.thialfihar.android.apg.provider.Database;
 +import org.thialfihar.android.apg.provider.KeyRings;
 +import org.thialfihar.android.apg.provider.Keys;
 +import org.thialfihar.android.apg.provider.UserIds;
  import org.thialfihar.android.apg.ui.widget.KeyEditor;
  import org.thialfihar.android.apg.ui.widget.SectionView;
  import org.thialfihar.android.apg.ui.widget.UserIdEditor;
  import org.thialfihar.android.apg.utils.IterableIterator;
  import android.app.Activity;
 -import android.content.ContentValues;
  import android.content.Context;
  import android.database.Cursor;
 -import android.net.Uri;
 +import android.database.sqlite.SQLiteDatabase;
  import android.os.Bundle;
  import android.os.Environment;
  import android.view.ViewGroup;
 @@ -102,9 +102,35 @@ public class Apg {          public static final String ENCRYPT = "org.thialfihar.android.apg.intent.ENCRYPT";
          public static final String DECRYPT_FILE = "org.thialfihar.android.apg.intent.DECRYPT_FILE";
          public static final String ENCRYPT_FILE = "org.thialfihar.android.apg.intent.ENCRYPT_FILE";
 -    }
 -
 -    public static String VERSION = "0.9.5";
 +        public static final String DECRYPT_AND_RETURN = "org.thialfihar.android.apg.intent.DECRYPT_AND_RETURN";
 +        public static final String ENCRYPT_AND_RETURN = "org.thialfihar.android.apg.intent.ENCRYPT_AND_RETURN";
 +        public static final String SELECT_PUBLIC_KEYS = "org.thialfihar.android.apg.intent.SELECT_PUBLIC_KEYS";
 +        public static final String SELECT_SECRET_KEY = "org.thialfihar.android.apg.intent.SELECT_SECRET_KEY";
 +    }
 +
 +    public static final String EXTRA_DATA = "data";
 +    public static final String EXTRA_STATUS = "status";
 +    public static final String EXTRA_ERROR = "error";
 +    public static final String EXTRA_DECRYPTED_MESSAGE = "decryptedMessage";
 +    public static final String EXTRA_ENCRYPTED_MESSAGE = "decryptedMessage";
 +    public static final String EXTRA_SIGNATURE = "signature";
 +    public static final String EXTRA_SIGNATURE_KEY_ID = "signatureKeyId";
 +    public static final String EXTRA_SIGNATURE_USER_ID = "signatureUserId";
 +    public static final String EXTRA_SIGNATURE_SUCCESS = "signatureSuccess";
 +    public static final String EXTRA_SIGNATURE_UNKNOWN = "signatureUnknown";
 +    public static final String EXTRA_USER_ID = "userId";
 +    public static final String EXTRA_KEY_ID = "keyId";
 +    public static final String EXTRA_REPLY_TO = "replyTo";
 +    public static final String EXTRA_SEND_TO = "sendTo";
 +    public static final String EXTRA_SUBJECT = "subject";
 +    public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryptionKeyIds";
 +    public static final String EXTRA_SELECTION = "selection";
 +    public static final String EXTRA_MESSAGE = "message";
 +    public static final String EXTRA_PROGRESS = "progress";
 +    public static final String EXTRA_MAX = "max";
 +    public static final String EXTRA_ACCOUNT = "account";
 +
 +    public static String VERSION = "1.0.1";
      public static String FULL_VERSION = "APG v" + VERSION;
      private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS =
 @@ -125,41 +151,20 @@ public class Apg {                      CompressionAlgorithmTags.BZIP2,
                      CompressionAlgorithmTags.ZIP };
 -    protected static Vector<PGPPublicKeyRing> mPublicKeyRings = new Vector<PGPPublicKeyRing>();
 -    protected static Vector<PGPSecretKeyRing> mSecretKeyRings = new Vector<PGPSecretKeyRing>();
 -
      public static Pattern PGP_MESSAGE =
              Pattern.compile(".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*",
                              Pattern.DOTALL);
      public static Pattern PGP_SIGNED_MESSAGE =
 -        Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
 -                        Pattern.DOTALL);
 -
 -    protected static boolean mInitialized = false;
 -
 -    protected static HashMap<Long, Integer> mSecretKeyIdToIdMap;
 -    protected static HashMap<Long, PGPSecretKeyRing> mSecretKeyIdToKeyRingMap;
 -    protected static HashMap<Long, Integer> mPublicKeyIdToIdMap;
 -    protected static HashMap<Long, PGPPublicKeyRing> mPublicKeyIdToKeyRingMap;
 -
 -    public static final String PUBLIC_KEY_PROJECTION[] =
 -            new String[] {
 -                    PublicKeys._ID,
 -                    PublicKeys.KEY_ID,
 -                    PublicKeys.KEY_DATA,
 -                    PublicKeys.WHO_ID, };
 -    public static final String SECRET_KEY_PROJECTION[] =
 -            new String[] {
 -                    PublicKeys._ID,
 -                    PublicKeys.KEY_ID,
 -                    PublicKeys.KEY_DATA,
 -                    PublicKeys.WHO_ID, };
 +            Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
 +                            Pattern.DOTALL);
      private static HashMap<Long, CachedPassPhrase> mPassPhraseCache =
              new HashMap<Long, CachedPassPhrase>();
      private static String mEditPassPhrase = null;
 +    private static Database mDatabase = null;
 +
      public static class GeneralException extends Exception {
          static final long serialVersionUID = 0xf812773342L;
 @@ -176,102 +181,14 @@ public class Apg {          }
      }
 -    static {
 -        mPublicKeyRings = new Vector<PGPPublicKeyRing>();
 -        mSecretKeyRings = new Vector<PGPSecretKeyRing>();
 -        mSecretKeyIdToIdMap = new HashMap<Long, Integer>();
 -        mSecretKeyIdToKeyRingMap = new HashMap<Long, PGPSecretKeyRing>();
 -        mPublicKeyIdToIdMap = new HashMap<Long, Integer>();
 -        mPublicKeyIdToKeyRingMap = new HashMap<Long, PGPPublicKeyRing>();
 -    }
 -
 -    public static void initialize(Activity context) {
 -        if (mInitialized) {
 -            return;
 -        }
 -
 -        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
 -            File dir = new File(Constants.path.app_dir);
 -            if (!dir.exists() && !dir.mkdirs()) {
 -                // ignore this for now, it's not crucial
 -                // that the directory doesn't exist at this point
 -            }
 -        }
 -
 -        loadKeyRings(context, Id.type.public_key);
 -        loadKeyRings(context, Id.type.secret_key);
 -
 -        mInitialized = true;
 -    }
 -
 -    public static class PublicKeySorter implements Comparator<PGPPublicKeyRing> {
 -        @Override
 -        public int compare(PGPPublicKeyRing object1, PGPPublicKeyRing object2) {
 -            PGPPublicKey key1 = getMasterKey(object1);
 -            PGPPublicKey key2 = getMasterKey(object2);
 -            if (key1 == null && key2 == null) {
 -                return 0;
 -            }
 -
 -            if (key1 == null) {
 -                return -1;
 -            }
 -
 -            if (key2 == null) {
 -                return 1;
 -            }
 -
 -            String uid1 = getMainUserId(key1);
 -            String uid2 = getMainUserId(key2);
 -            if (uid1 == null && uid2 == null) {
 -                return 0;
 -            }
 -
 -            if (uid1 == null) {
 -                return -1;
 -            }
 -
 -            if (uid2 == null) {
 -                return 1;
 -            }
 -
 -            return uid1.compareTo(uid2);
 +    public static void initialize(Context context) {
 +        if (mDatabase == null) {
 +            mDatabase = new Database(context);
          }
      }
 -    public static class SecretKeySorter implements Comparator<PGPSecretKeyRing> {
 -        @Override
 -        public int compare(PGPSecretKeyRing object1, PGPSecretKeyRing object2) {
 -            PGPSecretKey key1 = getMasterKey(object1);
 -            PGPSecretKey key2 = getMasterKey(object2);
 -            if (key1 == null && key2 == null) {
 -                return 0;
 -            }
 -
 -            if (key1 == null) {
 -                return -1;
 -            }
 -
 -            if (key2 == null) {
 -                return 1;
 -            }
 -
 -            String uid1 = getMainUserId(key1);
 -            String uid2 = getMainUserId(key2);
 -            if (uid1 == null && uid2 == null) {
 -                return 0;
 -            }
 -
 -            if (uid1 == null) {
 -                return -1;
 -            }
 -
 -            if (uid2 == null) {
 -                return 1;
 -            }
 -
 -            return uid1.compareTo(uid2);
 -        }
 +    public static Database getDatabase() {
 +        return mDatabase;
      }
      public static void setEditPassPhrase(String passPhrase) {
 @@ -289,7 +206,7 @@ public class Apg {      public static String getCachedPassPhrase(long keyId) {
          long realId = keyId;
          if (realId != Id.key.symmetric) {
 -            PGPSecretKeyRing keyRing = findSecretKeyRing(keyId);
 +            PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
              if (keyRing == null) {
                  return null;
              }
 @@ -308,19 +225,30 @@ public class Apg {          return cpp.passPhrase;
      }
 -    public static void cleanUpCache(int ttl) {
 +    public static int cleanUpCache(int ttl, int initialDelay) {
 +        int delay = initialDelay;
 +        long realTtl = ttl * 1000;
          long now = new Date().getTime();
 -
          Vector<Long> oldKeys = new Vector<Long>();
          for (Map.Entry<Long, CachedPassPhrase> pair : mPassPhraseCache.entrySet()) {
 -            if ((now - pair.getValue().timestamp) >= 1000 * ttl) {
 +            long lived = now - pair.getValue().timestamp;
 +            if (lived >= realTtl) {
                  oldKeys.add(pair.getKey());
 +            } else {
 +                // see, whether the remaining time for this cache entry improves our
 +                // check delay
 +                long nextCheck = realTtl - lived + 1000;
 +                if (nextCheck < delay) {
 +                    delay = (int)nextCheck;
 +                }
              }
          }
          for (long keyId : oldKeys) {
              mPassPhraseCache.remove(keyId);
          }
 +
 +        return delay;
      }
      public static PGPSecretKey createKey(Context context,
 @@ -413,11 +341,10 @@ public class Apg {              secretKey = (PGPSecretKey) it.next();
          }
 -
          return secretKey;
      }
 -    private static long getNumDatesBetween(GregorianCalendar first, GregorianCalendar second) {
 +    private static long getNumDaysBetween(GregorianCalendar first, GregorianCalendar second) {
          GregorianCalendar tmp = new GregorianCalendar();
          tmp.setTime(first.getTime());
          long numDays = (second.getTimeInMillis() - first.getTimeInMillis()) / 1000 / 86400;
 @@ -434,7 +361,7 @@ public class Apg {                                        String oldPassPhrase, String newPassPhrase,
                                        ProgressDialogUpdater progress)
              throws Apg.GeneralException, NoSuchProviderException, PGPException,
 -            NoSuchAlgorithmException, SignatureException {
 +            NoSuchAlgorithmException, SignatureException, IOException, Database.GeneralException {
          progress.setProgress(R.string.progress_buildingKey, 0, 100);
 @@ -465,7 +392,7 @@ public class Apg {              } catch (UserIdEditor.NoEmailException e) {
                  throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAnEmailAddress));
              } catch (UserIdEditor.InvalidEmailException e) {
 -                throw new Apg.GeneralException(e.getMessage());
 +                throw new Apg.GeneralException("" + e);
              }
              if (userId.equals("")) {
 @@ -549,11 +476,12 @@ public class Apg {          hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
          hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
 +        // TODO: this doesn't work quite right yet
          if (keyEditor.getExpiryDate() != null) {
              GregorianCalendar creationDate = new GregorianCalendar();
              creationDate.setTime(getCreationDate(masterKey));
              GregorianCalendar expiryDate = keyEditor.getExpiryDate();
 -            long numDays = getNumDatesBetween(creationDate, expiryDate);
 +            long numDays = getNumDaysBetween(creationDate, expiryDate);
              if (numDays <= 0) {
                  throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
              }
 @@ -600,11 +528,12 @@ public class Apg {              }
              hashedPacketsGen.setKeyFlags(true, keyFlags);
 +            // TODO: this doesn't work quite right yet
              if (keyEditor.getExpiryDate() != null) {
                  GregorianCalendar creationDate = new GregorianCalendar();
                  creationDate.setTime(getCreationDate(masterKey));
                  GregorianCalendar expiryDate = keyEditor.getExpiryDate();
 -                long numDays = getNumDatesBetween(creationDate, expiryDate);
 +                long numDays = getNumDaysBetween(creationDate, expiryDate);
                  if (numDays <= 0) {
                      throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
                  }
 @@ -619,79 +548,16 @@ public class Apg {          PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
          progress.setProgress(R.string.progress_savingKeyRing, 90, 100);
 -        saveKeyRing(context, secretKeyRing);
 -        saveKeyRing(context, publicKeyRing);
 +        mDatabase.saveKeyRing(secretKeyRing);
 +        mDatabase.saveKeyRing(publicKeyRing);
 -        loadKeyRings(context, Id.type.public_key);
 -        loadKeyRings(context, Id.type.secret_key);
          progress.setProgress(R.string.progress_done, 100, 100);
      }
 -    private static int saveKeyRing(Activity context, PGPPublicKeyRing keyRing) {
 -        ByteArrayOutputStream out = new ByteArrayOutputStream();
 -        ContentValues values = new ContentValues();
 -
 -        PGPPublicKey masterKey = getMasterKey(keyRing);
 -        if (masterKey == null) {
 -            return Id.return_value.no_master_key;
 -        }
 -
 -        try {
 -            keyRing.encode(out);
 -            out.close();
 -        } catch (IOException e) {
 -            return Id.return_value.error;
 -        }
 -
 -        values.put(PublicKeys.KEY_ID, masterKey.getKeyID());
 -        values.put(PublicKeys.KEY_DATA, out.toByteArray());
 -
 -        Uri uri = Uri.withAppendedPath(PublicKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
 -        Cursor cursor = context.managedQuery(uri, PUBLIC_KEY_PROJECTION, null, null, null);
 -        if (cursor != null && cursor.getCount() > 0) {
 -            context.getContentResolver().update(uri, values, null, null);
 -            return Id.return_value.updated;
 -        } else {
 -            context.getContentResolver().insert(PublicKeys.CONTENT_URI, values);
 -            return Id.return_value.ok;
 -        }
 -    }
 -
 -    private static int saveKeyRing(Activity context, PGPSecretKeyRing keyRing) {
 -        ByteArrayOutputStream out = new ByteArrayOutputStream();
 -        ContentValues values = new ContentValues();
 -
 -        PGPSecretKey masterKey = getMasterKey(keyRing);
 -        if (masterKey == null) {
 -            return Id.return_value.no_master_key;
 -        }
 -
 -        try {
 -            keyRing.encode(out);
 -            out.close();
 -        } catch (IOException e) {
 -            return Id.return_value.error;
 -        }
 -
 -        values.put(SecretKeys.KEY_ID, masterKey.getKeyID());
 -        values.put(SecretKeys.KEY_DATA, out.toByteArray());
 -
 -        Uri uri = Uri.withAppendedPath(SecretKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
 -        Cursor cursor = context.managedQuery(uri, SECRET_KEY_PROJECTION, null, null, null);
 -        if (cursor != null && cursor.getCount() > 0) {
 -            context.getContentResolver().update(uri, values, null, null);
 -            return Id.return_value.updated;
 -        } else {
 -            context.getContentResolver().insert(SecretKeys.CONTENT_URI, values);
 -            return Id.return_value.ok;
 -        }
 -    }
 -
      public static Bundle importKeyRings(Activity context, int type, String filename,
                                          ProgressDialogUpdater progress)
              throws GeneralException, FileNotFoundException, PGPException, IOException {
          Bundle returnData = new Bundle();
 -        PGPObjectFactory objectFactory = null;
          if (type == Id.type.secret_key) {
              progress.setProgress(R.string.progress_importingSecretKeys, 0, 100);
 @@ -704,53 +570,61 @@ public class Apg {          }
          FileInputStream fileIn = new FileInputStream(filename);
 -        InputStream in = PGPUtil.getDecoderStream(fileIn);
 -        objectFactory = new PGPObjectFactory(in);
 -
 -        Vector<Object> objects = new Vector<Object>();
 -        Object obj = objectFactory.nextObject();
 -        while (obj != null) {
 -            objects.add(obj);
 -            obj = objectFactory.nextObject();
 -        }
 -
 +        long fileSize = new File(filename).length();
 +        PositionAwareInputStream progressIn = new PositionAwareInputStream(fileIn);
 +        // need to have access to the bufferedInput, so we can reuse it for the possible
 +        // PGPObject chunks after the first one, e.g. files with several consecutive ASCII
 +        // armour blocks
 +        BufferedInputStream bufferedInput = new BufferedInputStream(progressIn);
          int newKeys = 0;
          int oldKeys = 0;
 -        for (int i = 0; i < objects.size(); ++i) {
 -            progress.setProgress(i * 100 / objects.size(), 100);
 -            obj = objects.get(i);
 -            PGPPublicKeyRing publicKeyRing;
 -            PGPSecretKeyRing secretKeyRing;
 -            int retValue;
 -
 -            if (type == Id.type.secret_key) {
 -                if (!(obj instanceof PGPSecretKeyRing)) {
 -                    continue;
 -                }
 -                secretKeyRing = (PGPSecretKeyRing) obj;
 -                retValue = saveKeyRing(context, secretKeyRing);
 -            } else {
 -                if (!(obj instanceof PGPPublicKeyRing)) {
 -                    continue;
 +        try {
 +            while (true) {
 +                InputStream in = PGPUtil.getDecoderStream(bufferedInput);
 +                PGPObjectFactory objectFactory = new PGPObjectFactory(in);
 +                Object obj = objectFactory.nextObject();
 +                // if the first is already a null object, then we can stop trying
 +                if (obj == null) {
 +                    break;
                  }
 -                publicKeyRing = (PGPPublicKeyRing) obj;
 -                retValue = saveKeyRing(context, publicKeyRing);
 -            }
 +                while (obj != null) {
 +                    PGPPublicKeyRing publicKeyRing;
 +                    PGPSecretKeyRing secretKeyRing;
 +                    // a return value that doesn't match any Id.return_value.* values, in case
 +                    // saveKeyRing is never called
 +                    int retValue = 2107;
 -            if (retValue == Id.return_value.error) {
 -                throw new GeneralException(context.getString(R.string.error_savingKeys));
 -            }
 +                    try {
 +                        if (type == Id.type.secret_key && obj instanceof PGPSecretKeyRing) {
 +                            secretKeyRing = (PGPSecretKeyRing) obj;
 +                            retValue = mDatabase.saveKeyRing(secretKeyRing);
 +                        } else if (type == Id.type.public_key && obj instanceof PGPPublicKeyRing) {
 +                            publicKeyRing = (PGPPublicKeyRing) obj;
 +                            retValue = mDatabase.saveKeyRing(publicKeyRing);
 +                        }
 +                    } catch (IOException e) {
 +                        retValue = Id.return_value.error;
 +                    } catch (Database.GeneralException e) {
 +                        retValue = Id.return_value.error;
 +                    }
 +
 +                    if (retValue == Id.return_value.error) {
 +                        throw new GeneralException(context.getString(R.string.error_savingKeys));
 +                    }
 -            if (retValue == Id.return_value.updated) {
 -                ++oldKeys;
 -            } else if (retValue == Id.return_value.ok) {
 -                ++newKeys;
 +                    if (retValue == Id.return_value.updated) {
 +                        ++oldKeys;
 +                    } else if (retValue == Id.return_value.ok) {
 +                        ++newKeys;
 +                    }
 +                    progress.setProgress((int)(100 * progressIn.position() / fileSize), 100);
 +                    obj = objectFactory.nextObject();
 +                }
              }
 +        } catch (EOFException e) {
 +            // nothing to do, we are done
          }
 -        progress.setProgress(R.string.progress_reloadingKeys, 100, 100);
 -        loadKeyRings(context, type);
 -
          returnData.putInt("added", newKeys);
          returnData.putInt("updated", oldKeys);
 @@ -759,12 +633,13 @@ public class Apg {          return returnData;
      }
 -    public static Bundle exportKeyRings(Activity context, Vector<Object> keys, String filename,
 +    public static Bundle exportKeyRings(Activity context, Vector<Integer> keyRingIds,
 +                                        String filename,
                                          ProgressDialogUpdater progress)
              throws GeneralException, FileNotFoundException, PGPException, IOException {
          Bundle returnData = new Bundle();
 -        if (keys.size() == 1) {
 +        if (keyRingIds.size() == 1) {
              progress.setProgress(R.string.progress_exportingKey, 0, 100);
          } else {
              progress.setProgress(R.string.progress_exportingKeys, 0, 100);
 @@ -777,9 +652,9 @@ public class Apg {          ArmoredOutputStream out = new ArmoredOutputStream(fileOut);
          int numKeys = 0;
 -        for (int i = 0; i < keys.size(); ++i) {
 -            progress.setProgress(i * 100 / keys.size(), 100);
 -            Object obj = keys.get(i);
 +        for (int i = 0; i < keyRingIds.size(); ++i) {
 +            progress.setProgress(i * 100 / keyRingIds.size(), 100);
 +            Object obj = mDatabase.getKeyRing(keyRingIds.get(i));
              PGPPublicKeyRing publicKeyRing;
              PGPSecretKeyRing secretKeyRing;
 @@ -803,61 +678,6 @@ public class Apg {          return returnData;
      }
 -    private static void loadKeyRings(Activity context, int type) {
 -        Cursor cursor;
 -        if (type == Id.type.secret_key) {
 -            mSecretKeyRings.clear();
 -            mSecretKeyIdToIdMap.clear();
 -            mSecretKeyIdToKeyRingMap.clear();
 -            cursor = context.managedQuery(SecretKeys.CONTENT_URI, SECRET_KEY_PROJECTION,
 -                                          null, null, null);
 -        } else {
 -            mPublicKeyRings.clear();
 -            mPublicKeyIdToIdMap.clear();
 -            mPublicKeyIdToKeyRingMap.clear();
 -            cursor = context.managedQuery(PublicKeys.CONTENT_URI, PUBLIC_KEY_PROJECTION,
 -                                          null, null, null);
 -        }
 -
 -        for (int i = 0; i < cursor.getCount(); ++i) {
 -            cursor.moveToPosition(i);
 -            String sharedIdColumn = PublicKeys._ID; // same in both
 -            String sharedKeyIdColumn = PublicKeys.KEY_ID; // same in both
 -            String sharedKeyDataColumn = PublicKeys.KEY_DATA; // same in both
 -            int idIndex = cursor.getColumnIndex(sharedIdColumn);
 -            int keyIdIndex = cursor.getColumnIndex(sharedKeyIdColumn);
 -            int keyDataIndex = cursor.getColumnIndex(sharedKeyDataColumn);
 -
 -            byte keyData[] = cursor.getBlob(keyDataIndex);
 -            int id = cursor.getInt(idIndex);
 -            long keyId = cursor.getLong(keyIdIndex);
 -
 -            try {
 -                if (type == Id.type.secret_key) {
 -                    PGPSecretKeyRing key = new PGPSecretKeyRing(keyData);
 -                    mSecretKeyRings.add(key);
 -                    mSecretKeyIdToIdMap.put(keyId, id);
 -                    mSecretKeyIdToKeyRingMap.put(keyId, key);
 -                } else {
 -                    PGPPublicKeyRing key = new PGPPublicKeyRing(keyData);
 -                    mPublicKeyRings.add(key);
 -                    mPublicKeyIdToIdMap.put(keyId, id);
 -                    mPublicKeyIdToKeyRingMap.put(keyId, key);
 -                }
 -            } catch (IOException e) {
 -                // TODO: some error handling
 -            } catch (PGPException e) {
 -                // TODO: some error handling
 -            }
 -        }
 -
 -        if (type == Id.type.secret_key) {
 -            Collections.sort(mSecretKeyRings, new SecretKeySorter());
 -        } else {
 -            Collections.sort(mPublicKeyRings, new PublicKeySorter());
 -        }
 -    }
 -
      public static Date getCreationDate(PGPPublicKey key) {
          return key.getCreationTime();
      }
 @@ -867,6 +687,9 @@ public class Apg {      }
      public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) {
 +        if (keyRing == null) {
 +            return null;
 +        }
          for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
              if (key.isMasterKey()) {
                  return key;
 @@ -877,6 +700,9 @@ public class Apg {      }
      public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) {
 +        if (keyRing == null) {
 +            return null;
 +        }
          for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
              if (key.isMasterKey()) {
                  return key;
 @@ -982,7 +808,7 @@ public class Apg {      }
      public static PGPPublicKey getEncryptPublicKey(long masterKeyId) {
 -        PGPPublicKeyRing keyRing = mPublicKeyIdToKeyRingMap.get(masterKeyId);
 +        PGPPublicKeyRing keyRing = getPublicKeyRing(masterKeyId);
          if (keyRing == null) {
              return null;
          }
 @@ -994,7 +820,7 @@ public class Apg {      }
      public static PGPSecretKey getSigningKey(long masterKeyId) {
 -        PGPSecretKeyRing keyRing = mSecretKeyIdToKeyRingMap.get(masterKeyId);
 +        PGPSecretKeyRing keyRing = getSecretKeyRing(masterKeyId);
          if (keyRing == null) {
              return null;
          }
 @@ -1035,14 +861,6 @@ public class Apg {          return userId;
      }
 -    public static PGPPublicKeyRing getPublicKeyRing(long keyId) {
 -        return mPublicKeyIdToKeyRingMap.get(keyId);
 -    }
 -
 -    public static PGPSecretKeyRing getSecretKeyRing(long keyId) {
 -        return mSecretKeyIdToKeyRingMap.get(keyId);
 -    }
 -
      public static boolean isEncryptionKey(PGPPublicKey key) {
          if (!key.isEncryptionKey()) {
              return false;
 @@ -1122,9 +940,17 @@ public class Apg {      }
      public static String getAlgorithmInfo(PGPPublicKey key) {
 +        return getAlgorithmInfo(key.getAlgorithm(), key.getBitStrength());
 +    }
 +
 +    public static String getAlgorithmInfo(PGPSecretKey key) {
 +        return getAlgorithmInfo(key.getPublicKey());
 +    }
 +
 +    public static String getAlgorithmInfo(int algorithm, int keySize) {
          String algorithmStr = null;
 -        switch (key.getAlgorithm()) {
 +        switch (algorithm) {
              case PGPPublicKey.RSA_ENCRYPT:
              case PGPPublicKey.RSA_GENERAL:
              case PGPPublicKey.RSA_SIGN: {
 @@ -1148,97 +974,121 @@ public class Apg {                  break;
              }
          }
 -        return algorithmStr + ", " + key.getBitStrength() + "bit";
 -    }
 -
 -    public static String getAlgorithmInfo(PGPSecretKey key) {
 -        return getAlgorithmInfo(key.getPublicKey());
 +        return algorithmStr + ", " + keySize + "bit";
      }
 -    public static void deleteKey(Activity context, PGPPublicKeyRing keyRing) {
 -        PGPPublicKey masterKey = getMasterKey(keyRing);
 -        Uri uri = Uri.withAppendedPath(PublicKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
 -        context.getContentResolver().delete(uri, null, null);
 -        loadKeyRings(context, Id.type.public_key);
 +    public static void deleteKey(int keyRingId) {
 +        mDatabase.deleteKeyRing(keyRingId);
      }
 -    public static void deleteKey(Activity context, PGPSecretKeyRing keyRing) {
 -        PGPSecretKey masterKey = getMasterKey(keyRing);
 -        Uri uri = Uri.withAppendedPath(SecretKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
 -        context.getContentResolver().delete(uri, null, null);
 -        loadKeyRings(context, Id.type.secret_key);
 +    public static Object getKeyRing(int keyRingId) {
 +        return mDatabase.getKeyRing(keyRingId);
      }
 -    public static PGPPublicKey findPublicKey(long keyId) {
 -        PGPPublicKey key = null;
 -        for (int i = 0; i < mPublicKeyRings.size(); ++i) {
 -            PGPPublicKeyRing keyRing = mPublicKeyRings.get(i);
 -            try {
 -                key = keyRing.getPublicKey(keyId);
 -                if (key != null) {
 -                    return key;
 -                }
 -            } catch (PGPException e) {
 -                // just not found, can ignore this
 -            }
 +    public static PGPSecretKeyRing getSecretKeyRing(long keyId) {
 +        byte[] data = mDatabase.getKeyRingDataFromKeyId(Id.database.type_secret, keyId);
 +        if (data == null) {
 +            return null;
 +        }
 +        try {
 +            return new PGPSecretKeyRing(data);
 +        } catch (IOException e) {
 +            // no good way to handle this, return null
 +            // TODO: some info?
 +        } catch (PGPException e) {
 +            // no good way to handle this, return null
 +            // TODO: some info?
          }
          return null;
      }
 -    public static PGPSecretKey findSecretKey(long keyId) {
 -        PGPSecretKey key = null;
 -        for (int i = 0; i < mSecretKeyRings.size(); ++i) {
 -            PGPSecretKeyRing keyRing = mSecretKeyRings.get(i);
 -            key = keyRing.getSecretKey(keyId);
 -            if (key != null) {
 -                return key;
 -            }
 +    public static PGPPublicKeyRing getPublicKeyRing(long keyId) {
 +        byte[] data = mDatabase.getKeyRingDataFromKeyId(Id.database.type_public, keyId);
 +        if (data == null) {
 +            return null;
 +        }
 +        try {
 +            return new PGPPublicKeyRing(data);
 +        } catch (IOException e) {
 +            // no good way to handle this, return null
 +            // TODO: some info?
          }
          return null;
      }
 -    public static PGPSecretKeyRing findSecretKeyRing(long keyId) {
 -        for (int i = 0; i < mSecretKeyRings.size(); ++i) {
 -            PGPSecretKeyRing keyRing = mSecretKeyRings.get(i);
 -            PGPSecretKey key = null;
 -            key = keyRing.getSecretKey(keyId);
 -            if (key != null) {
 -                return keyRing;
 -            }
 +    public static PGPSecretKey getSecretKey(long keyId) {
 +        PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
 +        if (keyRing == null) {
 +            return null;
          }
 -        return null;
 +        return keyRing.getSecretKey(keyId);
      }
 -    public static PGPPublicKeyRing findPublicKeyRing(long keyId) {
 -        for (int i = 0; i < mPublicKeyRings.size(); ++i) {
 -            PGPPublicKeyRing keyRing = mPublicKeyRings.get(i);
 -            PGPPublicKey key = null;
 -            try {
 -                key = keyRing.getPublicKey(keyId);
 -                if (key != null) {
 -                    return keyRing;
 -                }
 -            } catch (PGPException e) {
 -                // key not found
 -            }
 +    public static PGPPublicKey getPublicKey(long keyId) {
 +        PGPPublicKeyRing keyRing = getPublicKeyRing(keyId);
 +        if (keyRing == null) {
 +            return null;
 +        }
 +        try {
 +            return keyRing.getPublicKey(keyId);
 +        } catch (PGPException e) {
 +            return null;
          }
 -        return null;
      }
 -    public static PGPPublicKey getPublicMasterKey(long keyId) {
 -        PGPPublicKey key = null;
 -        for (int i = 0; i < mPublicKeyRings.size(); ++i) {
 -            PGPPublicKeyRing keyRing = mPublicKeyRings.get(i);
 -            try {
 -                key = keyRing.getPublicKey(keyId);
 -                if (key != null) {
 -                    return getMasterKey(keyRing);
 -                }
 -            } catch (PGPException e) {
 -                // just not found, can ignore this
 -            }
 +    public static Vector<Integer> getKeyRingIds(int type) {
 +        SQLiteDatabase db = mDatabase.db();
 +        Vector<Integer> keyIds = new Vector<Integer>();
 +        Cursor c = db.query(KeyRings.TABLE_NAME,
 +                            new String[] { KeyRings._ID },
 +                            KeyRings.TYPE + " = ?", new String[] { "" + type },
 +                            null, null, null);
 +        if (c != null && c.moveToFirst()) {
 +            do {
 +                keyIds.add(c.getInt(0));
 +            } while (c.moveToNext());
 +        }
 +
 +        if (c != null) {
 +            c.close();
 +        }
 +
 +        return keyIds;
 +    }
 +
 +    public static String getMainUserId(long keyId, int type) {
 +        SQLiteDatabase db = mDatabase.db();
 +        Cursor c = db.query(Keys.TABLE_NAME + " INNER JOIN " + KeyRings.TABLE_NAME + " ON (" +
 +                            KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
 +                            Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + ") " +
 +                            " INNER JOIN " + Keys.TABLE_NAME + " AS masterKey ON (" +
 +                            KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
 +                            "masterKey." + Keys.KEY_RING_ID + " AND " +
 +                            "masterKey." + Keys.IS_MASTER_KEY + " = '1') " +
 +                            " INNER JOIN " + UserIds.TABLE_NAME + " ON (" +
 +                            UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " = " +
 +                            "masterKey." + Keys._ID + " AND " +
 +                            UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0')",
 +                            new String[] { UserIds.USER_ID },
 +                             Keys.TABLE_NAME + "." + Keys.KEY_ID + " = ? AND " +
 +                             KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
 +                             new String[] {
 +                                 "" + keyId,
 +                                 "" + type,
 +                             },
 +                             null, null, null);
 +        String userId = "";
 +        if (c != null && c.moveToFirst()) {
 +            do {
 +                userId = c.getString(0);
 +            } while (c.moveToNext());
          }
 -        return null;
 +
 +        if (c != null) {
 +            c.close();
 +        }
 +
 +        return userId;
      }
      public static void encrypt(Context context,
 @@ -1277,7 +1127,7 @@ public class Apg {          }
          if (signatureKeyId != 0) {
 -            signingKeyRing = findSecretKeyRing(signatureKeyId);
 +            signingKeyRing = getSecretKeyRing(signatureKeyId);
              signingKey = getSigningKey(signatureKeyId);
              if (signingKey == null) {
                  throw new GeneralException(context.getString(R.string.error_signatureFailed));
 @@ -1329,7 +1179,7 @@ public class Apg {          if (compression == Id.choice.compression.none) {
              bcpgOut = new BCPGOutputStream(encryptOut);
          } else {
 -            compressGen = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZLIB);
 +            compressGen = new PGPCompressedDataGenerator(compression);
              bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut));
          }
          if (signatureKeyId != 0) {
 @@ -1393,7 +1243,7 @@ public class Apg {              throw new GeneralException(context.getString(R.string.error_noSignatureKey));
          }
 -        signingKeyRing = findSecretKeyRing(signatureKeyId);
 +        signingKeyRing = getSecretKeyRing(signatureKeyId);
          signingKey = getSigningKey(signatureKeyId);
          if (signingKey == null) {
              throw new GeneralException(context.getString(R.string.error_signatureFailed));
 @@ -1479,7 +1329,7 @@ public class Apg {              if (obj instanceof PGPPublicKeyEncryptedData) {
                  gotAsymmetricEncryption = true;
                  PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj;
 -                secretKey = findSecretKey(pbe.getKeyID());
 +                secretKey = getSecretKey(pbe.getKeyID());
                  if (secretKey != null) {
                      break;
                  }
 @@ -1532,6 +1382,9 @@ public class Apg {                                   String passPhrase, ProgressDialogUpdater progress,
                                   boolean assumeSymmetric)
              throws IOException, GeneralException, PGPException, SignatureException {
 +        if (passPhrase == null) {
 +            passPhrase = "";
 +        }
          Bundle returnData = new Bundle();
          InputStream in = PGPUtil.getDecoderStream(inStream);
          PGPObjectFactory pgpF = new PGPObjectFactory(in);
 @@ -1589,7 +1442,7 @@ public class Apg {                  Object obj = it.next();
                  if (obj instanceof PGPPublicKeyEncryptedData) {
                      PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
 -                    secretKey = findSecretKey(encData.getKeyID());
 +                    secretKey = getSecretKey(encData.getKeyID());
                      if (secretKey != null) {
                          pbe = encData;
                          break;
 @@ -1634,11 +1487,11 @@ public class Apg {          if (dataChunk instanceof PGPOnePassSignatureList) {
              progress.setProgress(R.string.progress_processingSignature, currentProgress, 100);
 -            returnData.putBoolean("signature", true);
 +            returnData.putBoolean(EXTRA_SIGNATURE, true);
              PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
              for (int i = 0; i < sigList.size(); ++i) {
                  signature = sigList.get(i);
 -                signatureKey = findPublicKey(signature.getKeyID());
 +                signatureKey = getPublicKey(signature.getKeyID());
                  if (signatureKeyId == 0) {
                      signatureKeyId = signature.getKeyID();
                  }
 @@ -1648,21 +1501,21 @@ public class Apg {                      signatureIndex = i;
                      signatureKeyId = signature.getKeyID();
                      String userId = null;
 -                    PGPPublicKeyRing sigKeyRing = findPublicKeyRing(signatureKeyId);
 +                    PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
                      if (sigKeyRing != null) {
                          userId = getMainUserId(getMasterKey(sigKeyRing));
                      }
 -                    returnData.putString("signatureUserId", userId);
 +                    returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
                      break;
                  }
              }
 -            returnData.putLong("signatureKeyId", signatureKeyId);
 +            returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
              if (signature != null) {
                  signature.initVerify(signatureKey, new BouncyCastleProvider());
              } else {
 -                returnData.putBoolean("signatureUnknown", true);
 +                returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
              }
              dataChunk = plainFact.nextObject();
 @@ -1694,7 +1547,7 @@ public class Apg {                      try {
                          signature.update(buffer, 0, n);
                      } catch (SignatureException e) {
 -                        returnData.putBoolean("signatureSuccess", false);
 +                        returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, false);
                          signature = null;
                      }
                  }
 @@ -1714,9 +1567,9 @@ public class Apg {                  PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
                  PGPSignature messageSignature = (PGPSignature) signatureList.get(signatureIndex);
                  if (signature.verify(messageSignature)) {
 -                    returnData.putBoolean("signatureSuccess", true);
 +                    returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, true);
                  } else {
 -                    returnData.putBoolean("signatureSuccess", false);
 +                    returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, false);
                  }
              }
          }
 @@ -1769,7 +1622,7 @@ public class Apg {          byte[] clearText = out.toByteArray();
          outStream.write(clearText);
 -        returnData.putBoolean("signature", true);
 +        returnData.putBoolean(EXTRA_SIGNATURE, true);
          progress.setProgress(R.string.progress_processingSignature, 60, 100);
          PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
 @@ -1783,7 +1636,7 @@ public class Apg {          PGPPublicKey signatureKey = null;
          for (int i = 0; i < sigList.size(); ++i) {
              signature = sigList.get(i);
 -            signatureKey = findPublicKey(signature.getKeyID());
 +            signatureKey = getPublicKey(signature.getKeyID());
              if (signatureKeyId == 0) {
                  signatureKeyId = signature.getKeyID();
              }
 @@ -1792,19 +1645,19 @@ public class Apg {              } else {
                  signatureKeyId = signature.getKeyID();
                  String userId = null;
 -                PGPPublicKeyRing sigKeyRing = findPublicKeyRing(signatureKeyId);
 +                PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
                  if (sigKeyRing != null) {
                      userId = getMainUserId(getMasterKey(sigKeyRing));
                  }
 -                returnData.putString("signatureUserId", userId);
 +                returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
                  break;
              }
          }
 -        returnData.putLong("signatureKeyId", signatureKeyId);
 +        returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
          if (signature == null) {
 -            returnData.putBoolean("signatureUnknown", true);
 +            returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
              progress.setProgress(R.string.progress_done, 100, 100);
              return returnData;
          }
 @@ -1829,21 +1682,12 @@ public class Apg {              while (lookAhead != -1);
          }
 -        returnData.putBoolean("signatureSuccess", signature.verify());
 +        returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, signature.verify());
          progress.setProgress(R.string.progress_done, 100, 100);
          return returnData;
      }
 -    public static Vector<PGPPublicKeyRing> getPublicKeyRings() {
 -        return mPublicKeyRings;
 -    }
 -
 -    public static Vector<PGPSecretKeyRing> getSecretKeyRings() {
 -        return mSecretKeyRings;
 -    }
 -
 -
      // taken from ClearSignedFileProcessor in BC
      private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
          throws IOException {
 diff --git a/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java b/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java index 67aad7529..01cd2de25 100644 --- a/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java +++ b/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java @@ -25,6 +25,7 @@ import android.app.AlertDialog;  import android.app.Dialog;
  import android.content.Context;
  import android.content.DialogInterface;
 +import android.content.DialogInterface.OnClickListener;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.widget.EditText;
 @@ -42,14 +43,24 @@ public class AskForSecretKeyPassPhrase {          alert.setTitle(R.string.title_authentification);
          final PGPSecretKey secretKey;
 +        final Activity activity = context;
          if (secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none) {
              secretKey = null;
              alert.setMessage(context.getString(R.string.passPhraseForSymmetricEncryption));
          } else {
 -            secretKey = Apg.getMasterKey(Apg.findSecretKeyRing(secretKeyId));
 +            secretKey = Apg.getMasterKey(Apg.getSecretKeyRing(secretKeyId));
              if (secretKey == null) {
 -                return null;
 +                alert.setTitle(R.string.title_keyNotFound);
 +                alert.setMessage(context.getString(R.string.keyNotFound, secretKeyId));
 +                alert.setPositiveButton(android.R.string.ok, new OnClickListener() {
 +                    @Override
 +                    public void onClick(DialogInterface dialog, int which) {
 +                        activity.removeDialog(Id.dialog.pass_phrase);
 +                    }
 +                });
 +                alert.setCancelable(false);
 +                return alert.create();
              }
              String userId = Apg.getMainUserIdSafe(context, secretKey);
              alert.setMessage(context.getString(R.string.passPhraseFor, userId));
 @@ -65,30 +76,29 @@ public class AskForSecretKeyPassPhrase {          alert.setView(view);
          final PassPhraseCallbackInterface cb = callback;
 -        final Activity activity = context;
          alert.setPositiveButton(android.R.string.ok,
 -                                new DialogInterface.OnClickListener() {
 -                                    public void onClick(DialogInterface dialog, int id) {
 -                                        activity.removeDialog(Id.dialog.pass_phrase);
 -                                        String passPhrase = "" + input.getText();
 -                                        long keyId;
 -                                        if (secretKey != null) {
 -                                            try {
 -                                                secretKey.extractPrivateKey(passPhrase.toCharArray(),
 -                                                                            new BouncyCastleProvider());
 -                                            } catch (PGPException e) {
 -                                                Toast.makeText(activity,
 -                                                               R.string.wrongPassPhrase,
 -                                                               Toast.LENGTH_SHORT).show();
 -                                                return;
 -                                            }
 -                                            keyId = secretKey.getKeyID();
 -                                        } else {
 -                                            keyId = Id.key.symmetric;
 -                                        }
 -                                        cb.passPhraseCallback(keyId, passPhrase);
 -                                    }
 -                                });
 +                new DialogInterface.OnClickListener() {
 +                    public void onClick(DialogInterface dialog, int id) {
 +                        activity.removeDialog(Id.dialog.pass_phrase);
 +                        String passPhrase = "" + input.getText();
 +                        long keyId;
 +                        if (secretKey != null) {
 +                            try {
 +                                secretKey.extractPrivateKey(passPhrase.toCharArray(),
 +                                                            new BouncyCastleProvider());
 +                            } catch (PGPException e) {
 +                                Toast.makeText(activity,
 +                                               R.string.wrongPassPhrase,
 +                                               Toast.LENGTH_SHORT).show();
 +                                return;
 +                            }
 +                            keyId = secretKey.getKeyID();
 +                        } else {
 +                            keyId = Id.key.symmetric;
 +                        }
 +                        cb.passPhraseCallback(keyId, passPhrase);
 +                    }
 +                });
          alert.setNegativeButton(android.R.string.cancel,
                                  new DialogInterface.OnClickListener() {
 diff --git a/src/org/thialfihar/android/apg/BaseActivity.java b/src/org/thialfihar/android/apg/BaseActivity.java index 64705ba1f..210e09409 100644 --- a/src/org/thialfihar/android/apg/BaseActivity.java +++ b/src/org/thialfihar/android/apg/BaseActivity.java @@ -17,10 +17,7 @@  package org.thialfihar.android.apg;
  import java.io.File;
 -import java.util.Timer;
 -import java.util.TimerTask;
 -import org.bouncycastle2.bcpg.CompressionAlgorithmTags;
  import org.bouncycastle2.bcpg.HashAlgorithmTags;
  import org.bouncycastle2.openpgp.PGPEncryptedData;
 @@ -33,6 +30,7 @@ import android.content.DialogInterface;  import android.content.Intent;
  import android.content.SharedPreferences;
  import android.os.Bundle;
 +import android.os.Environment;
  import android.os.Handler;
  import android.os.Message;
  import android.view.LayoutInflater;
 @@ -53,8 +51,6 @@ public class BaseActivity extends Activity      private String mDeleteFile = null;
      protected static SharedPreferences mPreferences = null;
 -    private static Timer mCacheTimer = new Timer();
 -
      private Handler mHandler = new Handler() {
          @Override
          public void handleMessage(Message msg) {
 @@ -66,33 +62,23 @@ public class BaseActivity extends Activity      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
 +        Apg.initialize(this);
 +
          if (mPreferences == null) {
              mPreferences = getPreferences(MODE_PRIVATE);
          }
 -        Apg.initialize(this);
 -        if (mCacheTimer == null) {
 -            setPassPhraseCacheTimer();
 -        }
 -    }
 -    private void setPassPhraseCacheTimer() {
 -        if (mCacheTimer != null) {
 -            mCacheTimer.cancel();
 -            mCacheTimer = null;
 -        }
 -        int ttl = getPassPhraseCacheTtl();
 -        if (ttl == 0) {
 -            // no timer needed
 -            return;
 -        }
 -        // check every ttl/2 seconds, which shouldn't be heavy on the device (even if ttl = 15),
 -        // and makes sure the longest a pass phrase survives int the cache is 1.5 * ttl
 -        mCacheTimer = new Timer();
 -        mCacheTimer.scheduleAtFixedRate(new TimerTask() {
 -            public void run() {
 -                Apg.cleanUpCache(BaseActivity.this.getPassPhraseCacheTtl());
 +        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
 +            File dir = new File(Constants.path.app_dir);
 +            if (!dir.exists() && !dir.mkdirs()) {
 +                // ignore this for now, it's not crucial
 +                // that the directory doesn't exist at this point
              }
 -        }, 0, ttl * 1000 / 2);
 +        }
 +
 +        Intent intent = new Intent(this, Service.class);
 +        intent.putExtra(Service.EXTRA_TTL, getPassPhraseCacheTtl());
 +        startService(intent);
      }
      @Override
 @@ -282,7 +268,7 @@ public class BaseActivity extends Activity              case Id.request.secret_keys: {
                  if (resultCode == RESULT_OK) {
                      Bundle bundle = data.getExtras();
 -                    setSecretKeyId(bundle.getLong("selectedKeyId"));
 +                    setSecretKeyId(bundle.getLong(Apg.EXTRA_KEY_ID));
                  } else {
                      setSecretKeyId(Id.key.none);
                  }
 @@ -304,9 +290,9 @@ public class BaseActivity extends Activity      public void setProgress(int progress, int max) {
          Message msg = new Message();
          Bundle data = new Bundle();
 -        data.putInt("type", Id.message.progress_update);
 -        data.putInt("progress", progress);
 -        data.putInt("max", max);
 +        data.putInt(Apg.EXTRA_STATUS, Id.message.progress_update);
 +        data.putInt(Apg.EXTRA_PROGRESS, progress);
 +        data.putInt(Apg.EXTRA_MAX, max);
          msg.setData(data);
          mHandler.sendMessage(msg);
      }
 @@ -314,10 +300,10 @@ public class BaseActivity extends Activity      public void setProgress(String message, int progress, int max) {
          Message msg = new Message();
          Bundle data = new Bundle();
 -        data.putInt("type", Id.message.progress_update);
 -        data.putString("message", message);
 -        data.putInt("progress", progress);
 -        data.putInt("max", max);
 +        data.putInt(Apg.EXTRA_STATUS, Id.message.progress_update);
 +        data.putString(Apg.EXTRA_MESSAGE, message);
 +        data.putInt(Apg.EXTRA_PROGRESS, progress);
 +        data.putInt(Apg.EXTRA_MAX, max);
          msg.setData(data);
          mHandler.sendMessage(msg);
      }
 @@ -328,16 +314,16 @@ public class BaseActivity extends Activity              return;
          }
 -        int type = data.getInt("type");
 +        int type = data.getInt(Apg.EXTRA_STATUS);
          switch (type) {
              case Id.message.progress_update: {
 -                String message = data.getString("message");
 +                String message = data.getString(Apg.EXTRA_MESSAGE);
                  if (mProgressDialog != null) {
                      if (message != null) {
                          mProgressDialog.setMessage(message);
                      }
 -                    mProgressDialog.setMax(data.getInt("max"));
 -                    mProgressDialog.setProgress(data.getInt("progress"));
 +                    mProgressDialog.setMax(data.getInt(Apg.EXTRA_MAX));
 +                    mProgressDialog.setProgress(data.getInt(Apg.EXTRA_PROGRESS));
                  }
                  break;
              }
 @@ -382,7 +368,14 @@ public class BaseActivity extends Activity      }
      public int getPassPhraseCacheTtl() {
 -        return mPreferences.getInt(Constants.pref.pass_phrase_cache_ttl, 300);
 +        int ttl = mPreferences.getInt(Constants.pref.pass_phrase_cache_ttl, 180);
 +        // fix the value if it was set to "never" in previous versions, which currently is not
 +        // supported
 +        if (ttl == 0) {
 +            ttl = 180;
 +            setPassPhraseCacheTtl(ttl);
 +        }
 +        return ttl;
      }
      public void setPassPhraseCacheTtl(int value) {
 @@ -390,7 +383,9 @@ public class BaseActivity extends Activity          editor.putInt(Constants.pref.pass_phrase_cache_ttl, value);
          editor.commit();
 -        setPassPhraseCacheTimer();
 +        Intent intent = new Intent(this, Service.class);
 +        intent.putExtra(Service.EXTRA_TTL, value);
 +        startService(intent);
      }
      public int getDefaultEncryptionAlgorithm() {
 @@ -417,7 +412,7 @@ public class BaseActivity extends Activity      public int getDefaultMessageCompression() {
          return mPreferences.getInt(Constants.pref.default_message_compression,
 -                                   CompressionAlgorithmTags.ZLIB);
 +                                   Id.choice.compression.zlib);
      }
      public void setDefaultMessageCompression(int value) {
 @@ -428,7 +423,7 @@ public class BaseActivity extends Activity      public int getDefaultFileCompression() {
          return mPreferences.getInt(Constants.pref.default_file_compression,
 -                                   CompressionAlgorithmTags.ZLIB);
 +                                   Id.choice.compression.none);
      }
      public void setDefaultFileCompression(int value) {
 diff --git a/src/org/thialfihar/android/apg/CachedPassPhrase.java b/src/org/thialfihar/android/apg/CachedPassPhrase.java index e7566220e..74248aee3 100644 --- a/src/org/thialfihar/android/apg/CachedPassPhrase.java +++ b/src/org/thialfihar/android/apg/CachedPassPhrase.java @@ -10,6 +10,12 @@ public class CachedPassPhrase {          this.passPhrase = passPhrase;
      }
 +    public int hashCode() {
 +        int hc1 = (int)(this.timestamp & 0xffffffff);
 +        int hc2 = (this.passPhrase == null ? 0 : this.passPhrase.hashCode());
 +        return (hc1 + hc2) * hc2 + hc1;
 +    }
 +
      public boolean equals(Object other) {
          if (!(other instanceof CachedPassPhrase)) {
              return false;
 diff --git a/src/org/thialfihar/android/apg/DecryptActivity.java b/src/org/thialfihar/android/apg/DecryptActivity.java index 80ad13d5e..de8dcb3ff 100644 --- a/src/org/thialfihar/android/apg/DecryptActivity.java +++ b/src/org/thialfihar/android/apg/DecryptActivity.java @@ -31,8 +31,6 @@ import java.util.regex.Matcher;  import org.bouncycastle2.jce.provider.BouncyCastleProvider;  import org.bouncycastle2.openpgp.PGPException; -import org.bouncycastle2.util.Strings; -import org.openintents.intents.FileManager;  import android.app.Dialog;  import android.content.ActivityNotFoundException; @@ -57,6 +55,9 @@ import android.widget.ViewFlipper;  public class DecryptActivity extends BaseActivity {      private long mSignatureKeyId = 0; +    private Intent mIntent; + +    private boolean mReturnResult = false;      private String mReplyTo = null;      private String mSubject = null;      private boolean mSignedOnly = false; @@ -158,9 +159,9 @@ public class DecryptActivity extends BaseActivity {              mSource.showNext();          } -        Intent intent = getIntent(); -        if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_VIEW)) { -            Uri uri = intent.getData(); +        mIntent = getIntent(); +        if (Intent.ACTION_VIEW.equals(mIntent.getAction())) { +            Uri uri = mIntent.getData();              try {                  InputStream attachment = getContentResolver().openInputStream(uri);                  ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); @@ -170,15 +171,15 @@ public class DecryptActivity extends BaseActivity {                      byteOut.write(bytes, 0, length);                  }                  byteOut.close(); -                String data = Strings.fromUTF8ByteArray(byteOut.toByteArray()); +                String data = new String(byteOut.toByteArray());                  mMessage.setText(data);              } catch (FileNotFoundException e) {                  // ignore, then              } catch (IOException e) {                  // ignore, then              } -        } else if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_SEND)) { -            Bundle extras = intent.getExtras(); +        } else if (Intent.ACTION_SEND.equals(mIntent.getAction())) { +            Bundle extras = mIntent.getExtras();              if (extras == null) {                  extras = new Bundle();              } @@ -187,15 +188,15 @@ public class DecryptActivity extends BaseActivity {                  mMessage.setText(data);              }              mSubject = extras.getString(Intent.EXTRA_SUBJECT); -            if (mSubject.startsWith("Fwd: ")) { +            if (mSubject != null && mSubject.startsWith("Fwd: ")) {                  mSubject = mSubject.substring(5);              } -        } else if (intent.getAction() != null && intent.getAction().equals(Apg.Intent.DECRYPT)) { -            Bundle extras = intent.getExtras(); +        } else if (Apg.Intent.DECRYPT.equals(mIntent.getAction())) { +            Bundle extras = mIntent.getExtras();              if (extras == null) {                  extras = new Bundle();              } -            String data = extras.getString("data"); +            String data = extras.getString(Apg.EXTRA_DATA);              if (data != null) {                  Matcher matcher = Apg.PGP_MESSAGE.matcher(data);                  if (matcher.matches()) { @@ -214,14 +215,39 @@ public class DecryptActivity extends BaseActivity {                      }                  }              } -            mReplyTo = extras.getString("replyTo"); -            mSubject = extras.getString("subject"); -        } else if (intent.getAction() != null && intent.getAction().equals(Apg.Intent.DECRYPT_FILE)) { +            mReplyTo = extras.getString(Apg.EXTRA_REPLY_TO); +            mSubject = extras.getString(Apg.EXTRA_SUBJECT); +        } else if (Apg.Intent.DECRYPT_FILE.equals(mIntent.getAction())) {              mSource.setInAnimation(null);              mSource.setOutAnimation(null);              while (mSource.getCurrentView().getId() != R.id.sourceFile) {                  mSource.showNext();              } +        } else if (Apg.Intent.DECRYPT_AND_RETURN.equals(mIntent.getAction())) { +            Bundle extras = mIntent.getExtras(); +            if (extras == null) { +                extras = new Bundle(); +            } +            String data = extras.getString(Apg.EXTRA_DATA); +            if (data != null) { +                Matcher matcher = Apg.PGP_MESSAGE.matcher(data); +                if (matcher.matches()) { +                    data = matcher.group(1); +                    // replace non breakable spaces +                    data = data.replaceAll("\\xa0", " "); +                    mMessage.setText(data); +                } else { +                    matcher = Apg.PGP_SIGNED_MESSAGE.matcher(data); +                    if (matcher.matches()) { +                        data = matcher.group(1); +                        // replace non breakable spaces +                        data = data.replaceAll("\\xa0", " "); +                        mMessage.setText(data); +                        mDecryptButton.setText(R.string.btn_verify); +                    } +                } +            } +            mReturnResult = true;          }          if (mSource.getCurrentView().getId() == R.id.sourceMessage && @@ -256,29 +282,41 @@ public class DecryptActivity extends BaseActivity {          });          mReplyButton.setVisibility(View.INVISIBLE); -        if (mSource.getCurrentView().getId() == R.id.sourceMessage && -            mMessage.getText().length() > 0) { -            mDecryptButton.performClick(); +        if (mReturnResult) { +            mSourcePrevious.setClickable(false); +            mSourcePrevious.setEnabled(false); +            mSourcePrevious.setVisibility(View.INVISIBLE); + +            mSourceNext.setClickable(false); +            mSourceNext.setEnabled(false); +            mSourceNext.setVisibility(View.INVISIBLE); + +            mSourceLabel.setClickable(false); +            mSourceLabel.setEnabled(false);          }          updateSource(); + +        if (mSource.getCurrentView().getId() == R.id.sourceMessage && +            mMessage.getText().length() > 0) { +             mDecryptButton.performClick(); +        }      }      private void openFile() {          String filename = mFilename.getText().toString(); -        Intent intent = new Intent(FileManager.ACTION_PICK_FILE); +        Intent intent = new Intent(Intent.ACTION_GET_CONTENT); +        intent.addCategory(Intent.CATEGORY_OPENABLE);          intent.setData(Uri.parse("file://" + filename)); - -        intent.putExtra(FileManager.EXTRA_TITLE, getString(R.string.filemanager_titleDecrypt)); -        intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, R.string.filemanager_btnOpen); +        intent.setType("*/*");          try {              startActivityForResult(intent, Id.request.filename);          } catch (ActivityNotFoundException e) {              // No compatible file manager was found. -            Toast.makeText(this, R.string.oiFilemanagerNotInstalled, Toast.LENGTH_SHORT).show(); +            Toast.makeText(this, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show();          }      } @@ -373,9 +411,9 @@ public class DecryptActivity extends BaseActivity {                  // look at the file/message again to check whether there's                  // symmetric encryption data in there                  if (mDecryptTarget == Id.target.file) { -                    ((FileInputStream) in).reset(); +                    in = new FileInputStream(mInputFilename);                  } else { -                    ((ByteArrayInputStream) in).reset(); +                    in = new ByteArrayInputStream(mMessage.getText().toString().getBytes());                  }                  if (!Apg.hasSymmetricEncryption(this, in)) {                      throw new Apg.GeneralException(getString(R.string.error_noKnownEncryptionFound)); @@ -396,9 +434,9 @@ public class DecryptActivity extends BaseActivity {          } catch (FileNotFoundException e) {              error = getString(R.string.error_fileNotFound);          } catch (IOException e) { -            error = e.getLocalizedMessage(); +            error = "" + e;          } catch (Apg.GeneralException e) { -            error = e.getLocalizedMessage(); +            error = "" + e;          }          if (error != null) {              Toast.makeText(this, getString(R.string.errorMessage, error), @@ -412,12 +450,11 @@ public class DecryptActivity extends BaseActivity {          String data = mMessage.getText().toString();          data = data.replaceAll("(?m)^", "> ");          data = "\n\n" + data; -        intent.putExtra("data", data); -        intent.putExtra("subject", "Re: " + mSubject); -        intent.putExtra("sendTo", mReplyTo); -        intent.putExtra("eyId", mSignatureKeyId); -        intent.putExtra("signatureKeyId", getSecretKeyId()); -        intent.putExtra("encryptionKeyIds", new long[] { mSignatureKeyId }); +        intent.putExtra(Apg.EXTRA_DATA, data); +        intent.putExtra(Apg.EXTRA_SUBJECT, "Re: " + mSubject); +        intent.putExtra(Apg.EXTRA_SEND_TO, mReplyTo); +        intent.putExtra(Apg.EXTRA_SIGNATURE_KEY_ID, getSecretKeyId()); +        intent.putExtra(Apg.EXTRA_ENCRYPTION_KEY_IDS, new long[] { mSignatureKeyId });          startActivity(intent);      } @@ -474,25 +511,23 @@ public class DecryptActivity extends BaseActivity {              out.close();              if (mDecryptTarget == Id.target.message) { -                data.putString("decryptedMessage", -                               Strings.fromUTF8ByteArray(((ByteArrayOutputStream) -                                                              out).toByteArray())); +                data.putByteArray(Apg.EXTRA_DECRYPTED_MESSAGE, +                                  ((ByteArrayOutputStream) out).toByteArray());              }          } catch (PGPException e) { -            error = e.getMessage(); +            error = "" + e;          } catch (IOException e) { -            error = e.getMessage(); +            error = "" + e;          } catch (SignatureException e) { -            error = e.getMessage(); -            e.printStackTrace(); +            error = "" + e;          } catch (Apg.GeneralException e) { -            error = e.getMessage(); +            error = "" + e;          } -        data.putInt("type", Id.message.done); +        data.putInt(Apg.EXTRA_STATUS, Id.message.done);          if (error != null) { -            data.putString("error", error); +            data.putString(Apg.EXTRA_ERROR, error);          }          msg.setData(data); @@ -509,20 +544,20 @@ public class DecryptActivity extends BaseActivity {          mSignatureLayout.setVisibility(View.GONE);          mReplyButton.setVisibility(View.INVISIBLE); -        String error = data.getString("error"); +        String error = data.getString(Apg.EXTRA_ERROR);          if (error != null) {              Toast.makeText(DecryptActivity.this, -                           getString(R.string.errorMessage, -                                     data.getString("error")), -                           Toast.LENGTH_SHORT).show(); +                           getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();              return;          }          Toast.makeText(this, R.string.decryptionSuccessful, Toast.LENGTH_SHORT).show();          switch (mDecryptTarget) {              case Id.target.message: { -                String decryptedMessage = data.getString("decryptedMessage"); +                String decryptedMessage = +                        new String(data.getByteArray(Apg.EXTRA_DECRYPTED_MESSAGE));                  mMessage.setText(decryptedMessage); +                mMessage.setHorizontallyScrolling(false);                  mReplyButton.setVisibility(View.VISIBLE);                  break;              } @@ -541,9 +576,9 @@ public class DecryptActivity extends BaseActivity {              }          } -        if (data.getBoolean("signature")) { -            String userId = data.getString("signatureUserId"); -            mSignatureKeyId = data.getLong("signatureKeyId"); +        if (data.getBoolean(Apg.EXTRA_SIGNATURE)) { +            String userId = data.getString(Apg.EXTRA_SIGNATURE_USER_ID); +            mSignatureKeyId = data.getLong(Apg.EXTRA_SIGNATURE_KEY_ID);              mUserIdRest.setText("id: " + Long.toHexString(mSignatureKeyId & 0xffffffffL));              if (userId == null) {                  userId = getResources().getString(R.string.unknownUserId); @@ -555,15 +590,22 @@ public class DecryptActivity extends BaseActivity {              }              mUserId.setText(userId); -            if (data.getBoolean("signatureSuccess")) { +            if (data.getBoolean(Apg.EXTRA_SIGNATURE_SUCCESS)) {                  mSignatureStatusImage.setImageResource(R.drawable.overlay_ok); -            } else if (data.getBoolean("signatureUnknown")) { +            } else if (data.getBoolean(Apg.EXTRA_SIGNATURE_UNKNOWN)) {                  mSignatureStatusImage.setImageResource(R.drawable.overlay_error);              } else {                  mSignatureStatusImage.setImageResource(R.drawable.overlay_error);              }              mSignatureLayout.setVisibility(View.VISIBLE);          } + +        if (mReturnResult) { +            Intent intent = new Intent(); +            intent.putExtras(data); +            setResult(RESULT_OK, intent); +            finish(); +        }      }      @Override diff --git a/src/org/thialfihar/android/apg/EditKeyActivity.java b/src/org/thialfihar/android/apg/EditKeyActivity.java index b1e37e8a1..e71fd8e8c 100644 --- a/src/org/thialfihar/android/apg/EditKeyActivity.java +++ b/src/org/thialfihar/android/apg/EditKeyActivity.java @@ -16,6 +16,7 @@  package org.thialfihar.android.apg; +import java.io.IOException;  import java.security.NoSuchAlgorithmException;  import java.security.NoSuchProviderException;  import java.security.SignatureException; @@ -24,6 +25,7 @@ import java.util.Vector;  import org.bouncycastle2.openpgp.PGPException;  import org.bouncycastle2.openpgp.PGPSecretKey;  import org.bouncycastle2.openpgp.PGPSecretKeyRing; +import org.thialfihar.android.apg.provider.Database;  import org.thialfihar.android.apg.ui.widget.KeyEditor;  import org.thialfihar.android.apg.ui.widget.SectionView;  import org.thialfihar.android.apg.utils.IterableIterator; @@ -69,7 +71,7 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {          Intent intent = getIntent();          long keyId = 0;          if (intent.getExtras() != null) { -            keyId = intent.getExtras().getLong("keyId"); +            keyId = intent.getExtras().getLong(Apg.EXTRA_KEY_ID);          }          if (keyId != 0) { @@ -115,7 +117,7 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {          Toast.makeText(this, "Warning: Key editing is still kind of beta.", Toast.LENGTH_LONG).show();      } -    public long getMasterKeyId() { +    private long getMasterKeyId() {          if (mKeys.getEditors().getChildCount() == 0) {              return 0;          } @@ -243,22 +245,27 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {                  newPassPhrase = oldPassPhrase;              }              Apg.buildSecretKey(this, mUserIds, mKeys, oldPassPhrase, newPassPhrase, this); +            Apg.setCachedPassPhrase(getMasterKeyId(), newPassPhrase);          } catch (NoSuchProviderException e) { -            error = e.getMessage(); +            error = "" + e;          } catch (NoSuchAlgorithmException e) { -            error = e.getMessage(); +            error = "" + e;          } catch (PGPException e) { -            error = e.getMessage(); +            error = "" + e;          } catch (SignatureException e) { -            error = e.getMessage(); +            error = "" + e;          } catch (Apg.GeneralException e) { -            error = e.getMessage(); +            error = "" + e; +        } catch (Database.GeneralException e) { +            error = "" + e; +        } catch (IOException e) { +            error = "" + e;          } -        data.putInt("type", Id.message.done); +        data.putInt(Apg.EXTRA_STATUS, Id.message.done);          if (error != null) { -            data.putString("error", error); +            data.putString(Apg.EXTRA_ERROR, error);          }          msg.setData(data); @@ -272,11 +279,10 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {          Bundle data = msg.getData();          removeDialog(Id.dialog.saving); -        String error = data.getString("error"); +        String error = data.getString(Apg.EXTRA_ERROR);          if (error != null) {              Toast.makeText(EditKeyActivity.this, -                           getString(R.string.errorMessage, data.getString("error")), -                           Toast.LENGTH_SHORT).show(); +                           getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();          } else {              Toast.makeText(EditKeyActivity.this, R.string.keySaved, Toast.LENGTH_SHORT).show();              setResult(RESULT_OK); diff --git a/src/org/thialfihar/android/apg/EncryptActivity.java b/src/org/thialfihar/android/apg/EncryptActivity.java index 073e9dc9c..926d79599 100644 --- a/src/org/thialfihar/android/apg/EncryptActivity.java +++ b/src/org/thialfihar/android/apg/EncryptActivity.java @@ -35,7 +35,6 @@ import org.bouncycastle2.openpgp.PGPPublicKeyRing;  import org.bouncycastle2.openpgp.PGPSecretKey;  import org.bouncycastle2.openpgp.PGPSecretKeyRing;  import org.bouncycastle2.util.Strings; -import org.openintents.intents.FileManager;  import org.thialfihar.android.apg.Apg.GeneralException;  import org.thialfihar.android.apg.utils.Choice; @@ -62,11 +61,13 @@ import android.widget.Toast;  import android.widget.ViewFlipper;  public class EncryptActivity extends BaseActivity { +    private Intent mIntent = null;      private String mSubject = null;      private String mSendTo = null;      private long mEncryptionKeyIds[] = null; +    private boolean mReturnResult = false;      private EditText mMessage = null;      private Button mSelectKeysButton = null;      private Button mEncryptButton = null; @@ -265,21 +266,26 @@ public class EncryptActivity extends BaseActivity {              }          }); -        Intent intent = getIntent(); -        if (intent.getAction() != null && -            (intent.getAction().equals(Apg.Intent.ENCRYPT) || -             intent.getAction().equals(Apg.Intent.ENCRYPT_FILE))) { -            Bundle extras = intent.getExtras(); +        mIntent = getIntent(); +        if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) || +            Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction()) || +            Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) { +            Bundle extras = mIntent.getExtras();              if (extras == null) {                  extras = new Bundle();              } -            String data = extras.getString("data"); -            mSendTo = extras.getString("sendTo"); -            mSubject = extras.getString("subject"); -            long signatureKeyId = extras.getLong("signatureKeyId"); -            long encryptionKeyIds[] = extras.getLongArray("encryptionKeyIds"); + +            if (Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) { +                mReturnResult = true; +            } + +            String data = extras.getString(Apg.EXTRA_DATA); +            mSendTo = extras.getString(Apg.EXTRA_SEND_TO); +            mSubject = extras.getString(Apg.EXTRA_SUBJECT); +            long signatureKeyId = extras.getLong(Apg.EXTRA_SIGNATURE_KEY_ID); +            long encryptionKeyIds[] = extras.getLongArray(Apg.EXTRA_ENCRYPTION_KEY_IDS);              if (signatureKeyId != 0) { -                PGPSecretKeyRing keyRing = Apg.findSecretKeyRing(signatureKeyId); +                PGPSecretKeyRing keyRing = Apg.getSecretKeyRing(signatureKeyId);                  PGPSecretKey masterKey = null;                  if (keyRing != null) {                      masterKey = Apg.getMasterKey(keyRing); @@ -295,7 +301,7 @@ public class EncryptActivity extends BaseActivity {              if (encryptionKeyIds != null) {                  Vector<Long> goodIds = new Vector<Long>();                  for (int i = 0; i < encryptionKeyIds.length; ++i) { -                    PGPPublicKeyRing keyRing = Apg.findPublicKeyRing(encryptionKeyIds[i]); +                    PGPPublicKeyRing keyRing = Apg.getPublicKeyRing(encryptionKeyIds[i]);                      PGPPublicKey masterKey = null;                      if (keyRing == null) {                          continue; @@ -318,7 +324,8 @@ public class EncryptActivity extends BaseActivity {                  }              } -            if (intent.getAction().equals(Apg.Intent.ENCRYPT)) { +            if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) || +                Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) {                  if (data != null) {                      mMessage.setText(data);                  } @@ -327,7 +334,7 @@ public class EncryptActivity extends BaseActivity {                  while (mSource.getCurrentView().getId() != R.id.sourceMessage) {                      mSource.showNext();                  } -            } else if (intent.getAction().equals(Apg.Intent.ENCRYPT_FILE)) { +            } else if (Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction())) {                  mSource.setInAnimation(null);                  mSource.setOutAnimation(null);                  while (mSource.getCurrentView().getId() != R.id.sourceFile) { @@ -339,23 +346,47 @@ public class EncryptActivity extends BaseActivity {          updateView();          updateSource();          updateMode(); + +        if (mReturnResult) { +            mSourcePrevious.setClickable(false); +            mSourcePrevious.setEnabled(false); +            mSourcePrevious.setVisibility(View.INVISIBLE); + +            mSourceNext.setClickable(false); +            mSourceNext.setEnabled(false); +            mSourceNext.setVisibility(View.INVISIBLE); + +            mSourceLabel.setClickable(false); +            mSourceLabel.setEnabled(false); + +            mEncryptToClipboardButton.setEnabled(false); +            mEncryptToClipboardButton.setVisibility(View.INVISIBLE); +            mEncryptButton.setText(R.string.btn_encrypt); +        } + +        if (mReturnResult && +            mMessage.getText().length() > 0 && +            ((mEncryptionKeyIds != null && +              mEncryptionKeyIds.length > 0) || +             getSecretKeyId() != 0)) { +            encryptClicked(); +        }      }      private void openFile() {          String filename = mFilename.getText().toString(); -        Intent intent = new Intent(FileManager.ACTION_PICK_FILE); +        Intent intent = new Intent(Intent.ACTION_GET_CONTENT); +        intent.addCategory(Intent.CATEGORY_OPENABLE);          intent.setData(Uri.parse("file://" + filename)); - -        intent.putExtra(FileManager.EXTRA_TITLE, R.string.filemanager_titleEncrypt); -        intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, R.string.filemanager_btnOpen); +        intent.setType("*/*");          try {              startActivityForResult(intent, Id.request.filename);          } catch (ActivityNotFoundException e) {              // No compatible file manager was found. -            Toast.makeText(this, R.string.oiFilemanagerNotInstalled, Toast.LENGTH_SHORT).show(); +            Toast.makeText(this, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show();          }      } @@ -457,7 +488,7 @@ public class EncryptActivity extends BaseActivity {                  return;              }          } else { -            boolean encryptIt = mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0; +            boolean encryptIt = (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0);              // for now require at least one form of encryption for files              if (!encryptIt && mEncryptTarget == Id.target.file) {                  Toast.makeText(this, R.string.selectEncryptionKey, Toast.LENGTH_SHORT).show(); @@ -527,7 +558,7 @@ public class EncryptActivity extends BaseActivity {              } else {                  encryptionKeyIds = mEncryptionKeyIds;                  signatureKeyId = getSecretKeyId(); -                signOnly = mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0; +                signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0);              }              if (mEncryptTarget == Id.target.file) { @@ -548,7 +579,7 @@ public class EncryptActivity extends BaseActivity {              } else {                  String message = mMessage.getText().toString(); -                if (signOnly) { +                if (signOnly && mReturnResult) {                      // fix the message a bit, trailing spaces and newlines break stuff,                      // because GMail sends as HTML and such things fuck up the signature,                      // TODO: things like "<" and ">" also fuck up the signature @@ -582,26 +613,27 @@ public class EncryptActivity extends BaseActivity {              out.close();              if (mEncryptTarget != Id.target.file) { -                data.putString("message", new String(((ByteArrayOutputStream)out).toByteArray())); +                data.putByteArray(Apg.EXTRA_ENCRYPTED_MESSAGE, +                                  ((ByteArrayOutputStream)out).toByteArray());              }          } catch (IOException e) { -            error = e.getMessage(); +            error = "" + e;          } catch (PGPException e) { -            error = e.getMessage(); +            error = "" + e;          } catch (NoSuchProviderException e) { -            error = e.getMessage(); +            error = "" + e;          } catch (NoSuchAlgorithmException e) { -            error = e.getMessage(); +            error = "" + e;          } catch (SignatureException e) { -            error = e.getMessage(); +            error = "" + e;          } catch (Apg.GeneralException e) { -            error = e.getMessage(); +            error = "" + e;          } -        data.putInt("type", Id.message.done); +        data.putInt(Apg.EXTRA_STATUS, Id.message.done);          if (error != null) { -            data.putString("error", error); +            data.putString(Apg.EXTRA_ERROR, error);          }          msg.setData(data); @@ -645,7 +677,7 @@ public class EncryptActivity extends BaseActivity {      private void selectPublicKeys() {          Intent intent = new Intent(this, SelectPublicKeyListActivity.class); -        intent.putExtra("selection", mEncryptionKeyIds); +        intent.putExtra(Apg.EXTRA_SELECTION, mEncryptionKeyIds);          startActivityForResult(intent, Id.request.public_keys);      } @@ -702,7 +734,7 @@ public class EncryptActivity extends BaseActivity {              case Id.request.public_keys: {                  if (resultCode == RESULT_OK) {                      Bundle bundle = data.getExtras(); -                    mEncryptionKeyIds = bundle.getLongArray("selection"); +                    mEncryptionKeyIds = bundle.getLongArray(Apg.EXTRA_SELECTION);                  }                  updateView();                  break; @@ -723,53 +755,61 @@ public class EncryptActivity extends BaseActivity {          removeDialog(Id.dialog.encrypting);          Bundle data = msg.getData(); -        String error = data.getString("error"); +        String error = data.getString(Apg.EXTRA_ERROR);          if (error != null) {              Toast.makeText(EncryptActivity.this, -                           getString(R.string.errorMessage, data.getString("error")), -                           Toast.LENGTH_SHORT).show(); +                           getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();              return; -        } else { -            String message = data.getString("message"); -            switch (mEncryptTarget) { -                case Id.target.clipboard: { -                    ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); -                    clip.setText(message); -                    Toast.makeText(this, R.string.encryptionToClipboardSuccessful, -                                   Toast.LENGTH_SHORT).show(); -                    break; -                } +        } +        switch (mEncryptTarget) { +            case Id.target.clipboard: { +                String message = new String(data.getByteArray(Apg.EXTRA_ENCRYPTED_MESSAGE)); +                ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); +                clip.setText(message); +                Toast.makeText(this, R.string.encryptionToClipboardSuccessful, +                               Toast.LENGTH_SHORT).show(); +                break; +            } -                case Id.target.email: { -                    Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND); -                    emailIntent.setType("text/plain; charset=utf-8"); -                    emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message); -                    if (mSubject != null) { -                        emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, -                                             mSubject); -                    } -                    if (mSendTo != null) { -                        emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, -                                             new String[] { mSendTo }); -                    } -                    EncryptActivity.this. -                            startActivity(Intent.createChooser(emailIntent, -                                                               getString(R.string.title_sendEmail))); +            case Id.target.email: { +                if (mReturnResult) { +                    Intent intent = new Intent(); +                    intent.putExtras(data); +                    setResult(RESULT_OK, intent); +                    finish(); +                    return;                  } -                case Id.target.file: { -                    Toast.makeText(this, R.string.encryptionSuccessful, Toast.LENGTH_SHORT).show(); -                    if (mDeleteAfter.isChecked()) { -                        setDeleteFile(mInputFilename); -                        showDialog(Id.dialog.delete_file); -                    } -                    break; +                String message = new String(data.getByteArray(Apg.EXTRA_ENCRYPTED_MESSAGE)); +                Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND); +                emailIntent.setType("text/plain; charset=utf-8"); +                emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message); +                if (mSubject != null) { +                    emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, +                                         mSubject); +                } +                if (mSendTo != null) { +                    emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, +                                         new String[] { mSendTo });                  } +                EncryptActivity.this. +                        startActivity(Intent.createChooser(emailIntent, +                                                           getString(R.string.title_sendEmail))); +                break; +            } -                default: { -                    // shouldn't happen -                    break; +            case Id.target.file: { +                Toast.makeText(this, R.string.encryptionSuccessful, Toast.LENGTH_SHORT).show(); +                if (mDeleteAfter.isChecked()) { +                    setDeleteFile(mInputFilename); +                    showDialog(Id.dialog.delete_file);                  } +                break; +            } + +            default: { +                // shouldn't happen +                break;              }          }      } diff --git a/src/org/thialfihar/android/apg/FileDialog.java b/src/org/thialfihar/android/apg/FileDialog.java index 22d64fc84..b6bbbf3f1 100644 --- a/src/org/thialfihar/android/apg/FileDialog.java +++ b/src/org/thialfihar/android/apg/FileDialog.java @@ -16,8 +16,6 @@  package org.thialfihar.android.apg;
 -import org.openintents.intents.FileManager;
 -
  import android.app.Activity;
  import android.app.AlertDialog;
  import android.content.ActivityNotFoundException;
 @@ -48,6 +46,8 @@ public class FileDialog {                                      String defaultFile, OnClickListener onClickListener,
                                      String fileManagerTitle, String fileManagerButton,
                                      int requestCode) {
 +        // TODO: fileManagerTitle and fileManagerButton are deprecated, no use for them right now,
 +        // but maybe the Intent now used will someday support them again, so leaving them in
          LayoutInflater inflater =
              (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
          AlertDialog.Builder alert = new AlertDialog.Builder(activity);
 @@ -102,18 +102,17 @@ public class FileDialog {      private static void openFile() {
          String filename = mFilename.getText().toString();
 -        Intent intent = new Intent(FileManager.ACTION_PICK_FILE);
 +        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
 +        intent.addCategory(Intent.CATEGORY_OPENABLE);
          intent.setData(Uri.parse("file://" + filename));
 -
 -        intent.putExtra(FileManager.EXTRA_TITLE, mFileManagerTitle);
 -        intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, mFileManagerButton);
 +        intent.setType("*/*");
          try {
              mActivity.startActivityForResult(intent, mRequestCode);
          } catch (ActivityNotFoundException e) {
              // No compatible file manager was found.
 -            Toast.makeText(mActivity, R.string.oiFilemanagerNotInstalled, Toast.LENGTH_SHORT).show();
 +            Toast.makeText(mActivity, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show();
          }
      }
  }
 diff --git a/src/org/thialfihar/android/apg/Id.java b/src/org/thialfihar/android/apg/Id.java index 47cd0a890..4567f937d 100644 --- a/src/org/thialfihar/android/apg/Id.java +++ b/src/org/thialfihar/android/apg/Id.java @@ -80,6 +80,11 @@ public final class Id {          public static final int export_keys = 0x21070002;
      }
 +    public static final class database {
 +        public static final int type_public = 0;
 +        public static final int type_secret = 1;
 +    }
 +
      public static final class type {
          public static final int public_key = 0x21070001;
          public static final int secret_key = 0x21070002;
 diff --git a/src/org/thialfihar/android/apg/KeyListActivity.java b/src/org/thialfihar/android/apg/KeyListActivity.java new file mode 100644 index 000000000..7e86504b3 --- /dev/null +++ b/src/org/thialfihar/android/apg/KeyListActivity.java @@ -0,0 +1,642 @@ +/*
 + * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at
 + *
 + *      http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +package org.thialfihar.android.apg;
 +
 +import java.io.FileNotFoundException;
 +import java.io.IOException;
 +import java.util.Vector;
 +
 +import org.bouncycastle2.openpgp.PGPException;
 +import org.bouncycastle2.openpgp.PGPPublicKeyRing;
 +import org.bouncycastle2.openpgp.PGPSecretKeyRing;
 +import org.thialfihar.android.apg.provider.KeyRings;
 +import org.thialfihar.android.apg.provider.Keys;
 +import org.thialfihar.android.apg.provider.UserIds;
 +
 +import android.app.AlertDialog;
 +import android.app.Dialog;
 +import android.content.Context;
 +import android.content.DialogInterface;
 +import android.content.Intent;
 +import android.database.Cursor;
 +import android.database.sqlite.SQLiteDatabase;
 +import android.net.Uri;
 +import android.os.Bundle;
 +import android.os.Message;
 +import android.view.LayoutInflater;
 +import android.view.MenuItem;
 +import android.view.View;
 +import android.view.ViewGroup;
 +import android.widget.BaseExpandableListAdapter;
 +import android.widget.ExpandableListView;
 +import android.widget.ImageView;
 +import android.widget.TextView;
 +import android.widget.Toast;
 +import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
 +
 +public class KeyListActivity extends BaseActivity {
 +    protected ExpandableListView mList;
 +    protected KeyListAdapter mListAdapter;
 +
 +    protected int mSelectedItem = -1;
 +    protected int mTask = 0;
 +
 +    protected String mImportFilename = Constants.path.app_dir + "/";
 +    protected String mExportFilename = Constants.path.app_dir + "/";
 +
 +    protected int mKeyType = Id.type.public_key;
 +
 +    @Override
 +    public void onCreate(Bundle savedInstanceState) {
 +        super.onCreate(savedInstanceState);
 +        setContentView(R.layout.key_list);
 +
 +        mList = (ExpandableListView) findViewById(R.id.list);
 +        mListAdapter = new KeyListAdapter(this);
 +        mList.setAdapter(mListAdapter);
 +        registerForContextMenu(mList);
 +    }
 +
 +    @Override
 +    public boolean onOptionsItemSelected(MenuItem item) {
 +        switch (item.getItemId()) {
 +            case Id.menu.option.import_keys: {
 +                showDialog(Id.dialog.import_keys);
 +                return true;
 +            }
 +
 +            case Id.menu.option.export_keys: {
 +                showDialog(Id.dialog.export_keys);
 +                return true;
 +            }
 +
 +            default: {
 +                return super.onOptionsItemSelected(item);
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public boolean onContextItemSelected(MenuItem menuItem) {
 +        ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
 +        int type = ExpandableListView.getPackedPositionType(info.packedPosition);
 +        int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
 +
 +        if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
 +            return super.onContextItemSelected(menuItem);
 +        }
 +
 +        switch (menuItem.getItemId()) {
 +            case Id.menu.export: {
 +                mSelectedItem = groupPosition;
 +                showDialog(Id.dialog.export_key);
 +                return true;
 +            }
 +
 +            case Id.menu.delete: {
 +                mSelectedItem = groupPosition;
 +                showDialog(Id.dialog.delete_key);
 +                return true;
 +            }
 +
 +            default: {
 +                return super.onContextItemSelected(menuItem);
 +            }
 +        }
 +    }
 +
 +    @Override
 +    protected Dialog onCreateDialog(int id) {
 +        boolean singleKeyExport = false;
 +
 +        switch (id) {
 +            case Id.dialog.delete_key: {
 +                final int keyRingId = mListAdapter.getKeyRingId(mSelectedItem);
 +                mSelectedItem = -1;
 +                // TODO: better way to do this?
 +                String userId = "<unknown>";
 +                Object keyRing = Apg.getKeyRing(keyRingId);
 +                if (keyRing != null) {
 +                    if (keyRing instanceof PGPPublicKeyRing) {
 +                        userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey((PGPPublicKeyRing) keyRing));
 +                    } else {
 +                        userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey((PGPSecretKeyRing) keyRing));
 +                    }
 +                }
 +
 +                AlertDialog.Builder builder = new AlertDialog.Builder(this);
 +                builder.setTitle(R.string.warning);
 +                builder.setMessage(getString(mKeyType == Id.type.public_key ?
 +                                                 R.string.keyDeletionConfirmation :
 +                                                 R.string.secretKeyDeletionConfirmation, userId));
 +                builder.setIcon(android.R.drawable.ic_dialog_alert);
 +                builder.setPositiveButton(R.string.btn_delete,
 +                                          new DialogInterface.OnClickListener() {
 +                                              public void onClick(DialogInterface dialog, int id) {
 +                                                  deleteKey(keyRingId);
 +                                                  removeDialog(Id.dialog.delete_key);
 +                                              }
 +                                          });
 +                builder.setNegativeButton(android.R.string.cancel,
 +                                          new DialogInterface.OnClickListener() {
 +                                              public void onClick(DialogInterface dialog, int id) {
 +                                                  removeDialog(Id.dialog.delete_key);
 +                                              }
 +                                          });
 +                return builder.create();
 +            }
 +
 +            case Id.dialog.import_keys: {
 +                return FileDialog.build(this, getString(R.string.title_importKeys),
 +                                        getString(R.string.specifyFileToImportFrom),
 +                                        mImportFilename,
 +                                        new FileDialog.OnClickListener() {
 +
 +                                            @Override
 +                                            public void onOkClick(String filename) {
 +                                                removeDialog(Id.dialog.import_keys);
 +                                                mImportFilename = filename;
 +                                                importKeys();
 +                                            }
 +
 +                                            @Override
 +                                            public void onCancelClick() {
 +                                                removeDialog(Id.dialog.import_keys);
 +                                            }
 +                                        },
 +                                        getString(R.string.filemanager_titleOpen),
 +                                        getString(R.string.filemanager_btnOpen),
 +                                        Id.request.filename);
 +            }
 +
 +            case Id.dialog.export_key: {
 +                singleKeyExport = true;
 +                // break intentionally omitted, to use the Id.dialog.export_keys dialog
 +            }
 +
 +            case Id.dialog.export_keys: {
 +                String title = (singleKeyExport ?
 +                                    getString(R.string.title_exportKey) :
 +                                    getString(R.string.title_exportKeys));
 +
 +                final int thisDialogId = (singleKeyExport ? Id.dialog.export_key : Id.dialog.export_keys);
 +
 +                return FileDialog.build(this, title,
 +                                        getString(mKeyType == Id.type.public_key ?
 +                                                      R.string.specifyFileToExportTo :
 +                                                      R.string.specifyFileToExportSecretKeysTo),
 +                                        mExportFilename,
 +                                        new FileDialog.OnClickListener() {
 +                                            @Override
 +                                            public void onOkClick(String filename) {
 +                                                removeDialog(thisDialogId);
 +                                                mExportFilename = filename;
 +                                                exportKeys();
 +                                            }
 +
 +                                            @Override
 +                                            public void onCancelClick() {
 +                                                removeDialog(thisDialogId);
 +                                            }
 +                                        },
 +                                        getString(R.string.filemanager_titleSave),
 +                                        getString(R.string.filemanager_btnSave),
 +                                        Id.request.filename);
 +            }
 +
 +            default: {
 +                return super.onCreateDialog(id);
 +            }
 +        }
 +    }
 +
 +    public void importKeys() {
 +        showDialog(Id.dialog.importing);
 +        mTask = Id.task.import_keys;
 +        startThread();
 +    }
 +
 +    public void exportKeys() {
 +        showDialog(Id.dialog.exporting);
 +        mTask = Id.task.export_keys;
 +        startThread();
 +    }
 +
 +    @Override
 +    public void run() {
 +        String error = null;
 +        Bundle data = new Bundle();
 +        Message msg = new Message();
 +
 +        String filename = null;
 +        if (mTask == Id.task.import_keys) {
 +            filename = mImportFilename;
 +        } else {
 +            filename = mExportFilename;
 +        }
 +
 +        try {
 +            if (mTask == Id.task.import_keys) {
 +                data = Apg.importKeyRings(this, mKeyType, filename, this);
 +            } else {
 +                Vector<Integer> keyRingIds = new Vector<Integer>();
 +                if (mSelectedItem == -1) {
 +                    keyRingIds = Apg.getKeyRingIds(mKeyType == Id.type.public_key ?
 +                                                       Id.database.type_public :
 +                                                       Id.database.type_secret);
 +                } else {
 +                    int keyRingId = mListAdapter.getKeyRingId(mSelectedItem);
 +                    keyRingIds.add(keyRingId);
 +                    mSelectedItem = -1;
 +                }
 +                data = Apg.exportKeyRings(this, keyRingIds, filename, this);
 +            }
 +        } catch (FileNotFoundException e) {
 +            error = getString(R.string.error_fileNotFound);
 +        } catch (IOException e) {
 +            error = "" + e;
 +        } catch (PGPException e) {
 +            error = "" + e;
 +        } catch (Apg.GeneralException e) {
 +            error = "" + e;
 +        }
 +
 +        if (mTask == Id.task.import_keys) {
 +            data.putInt(Apg.EXTRA_STATUS, Id.message.import_done);
 +        } else {
 +            data.putInt(Apg.EXTRA_STATUS, Id.message.export_done);
 +        }
 +
 +        if (error != null) {
 +            data.putString(Apg.EXTRA_ERROR, error);
 +        }
 +
 +        msg.setData(data);
 +        sendMessage(msg);
 +    }
 +
 +    protected void deleteKey(int keyRingId) {
 +        Apg.deleteKey(keyRingId);
 +        refreshList();
 +    }
 +
 +    protected void refreshList() {
 +        mListAdapter.rebuild(true);
 +        mListAdapter.notifyDataSetChanged();
 +    }
 +
 +    @Override
 +    public void doneCallback(Message msg) {
 +        super.doneCallback(msg);
 +
 +        Bundle data = msg.getData();
 +        if (data != null) {
 +            int type = data.getInt(Apg.EXTRA_STATUS);
 +            switch (type) {
 +                case Id.message.import_done: {
 +                    removeDialog(Id.dialog.importing);
 +
 +                    String error = data.getString(Apg.EXTRA_ERROR);
 +                    if (error != null) {
 +                        Toast.makeText(KeyListActivity.this,
 +                                       getString(R.string.errorMessage, error),
 +                                       Toast.LENGTH_SHORT).show();
 +                    } else {
 +                        int added = data.getInt("added");
 +                        int updated = data.getInt("updated");
 +                        String message;
 +                        if (added > 0 && updated > 0) {
 +                            message = getString(R.string.keysAddedAndUpdated, added, updated);
 +                        } else if (added > 0) {
 +                            message = getString(R.string.keysAdded, added);
 +                        } else if (updated > 0) {
 +                            message = getString(R.string.keysUpdated, updated);
 +                        } else {
 +                            message = getString(R.string.noKeysAddedOrUpdated);
 +                        }
 +                        Toast.makeText(KeyListActivity.this, message,
 +                                       Toast.LENGTH_SHORT).show();
 +                    }
 +                    refreshList();
 +                    break;
 +                }
 +
 +                case Id.message.export_done: {
 +                    removeDialog(Id.dialog.exporting);
 +
 +                    String error = data.getString(Apg.EXTRA_ERROR);
 +                    if (error != null) {
 +                        Toast.makeText(KeyListActivity.this,
 +                                       getString(R.string.errorMessage, error),
 +                                       Toast.LENGTH_SHORT).show();
 +                    } else {
 +                        int exported = data.getInt("exported");
 +                        String message;
 +                        if (exported == 1) {
 +                            message = getString(R.string.keyExported);
 +                        } else if (exported > 0) {
 +                            message = getString(R.string.keysExported, exported);
 +                        } else{
 +                            message = getString(R.string.noKeysExported);
 +                        }
 +                        Toast.makeText(KeyListActivity.this, message,
 +                                       Toast.LENGTH_SHORT).show();
 +                    }
 +                    break;
 +                }
 +
 +                default: {
 +                    break;
 +                }
 +            }
 +        }
 +    }
 +
 +    protected class KeyListAdapter extends BaseExpandableListAdapter {
 +        private LayoutInflater mInflater;
 +        private Vector<Vector<KeyChild>> mChildren;
 +        private SQLiteDatabase mDatabase;
 +        private Cursor mCursor;
 +
 +        private class KeyChild {
 +            public static final int KEY = 0;
 +            public static final int USER_ID = 1;
 +
 +            public int type;
 +            public String userId;
 +            public long keyId;
 +            public boolean isMasterKey;
 +            public int algorithm;
 +            public int keySize;
 +            public boolean canSign;
 +            public boolean canEncrypt;
 +
 +            public KeyChild(long keyId, boolean isMasterKey, int algorithm, int keySize,
 +                            boolean canSign, boolean canEncrypt) {
 +                this.keyId = keyId;
 +                this.isMasterKey = isMasterKey;
 +                this.algorithm = algorithm;
 +                this.keySize = keySize;
 +                this.canSign = canSign;
 +                this.canEncrypt = canEncrypt;
 +            }
 +
 +            public KeyChild(String userId) {
 +                type = USER_ID;
 +                this.userId = userId;
 +            }
 +        }
 +
 +        public KeyListAdapter(Context context) {
 +            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 +            mDatabase = Apg.getDatabase().db();
 +            mCursor = mDatabase.query(
 +                    KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
 +                                          "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
 +                                          Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
 +                                          Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
 +                                          ") " +
 +                                          " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
 +                                          "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
 +                                          UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
 +                                          UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ",
 +                    new String[] {
 +                        KeyRings.TABLE_NAME + "." + KeyRings._ID,           // 0
 +                        KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
 +                        UserIds.TABLE_NAME + "." + UserIds.USER_ID,         // 2
 +                    },
 +                    KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
 +                    new String[] { "" + (mKeyType == Id.type.public_key ?
 +                                             Id.database.type_public : Id.database.type_secret) },
 +                    null, null, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC");
 +
 +            startManagingCursor(mCursor);
 +            rebuild(false);
 +        }
 +
 +        public void rebuild(boolean requery) {
 +            if (requery) {
 +                mCursor.requery();
 +            }
 +            mChildren = new Vector<Vector<KeyChild>>();
 +            for (int i = 0; i < mCursor.getCount(); ++i) {
 +                mChildren.add(null);
 +            }
 +        }
 +
 +        protected Vector<KeyChild> getChildrenOfGroup(int groupPosition) {
 +            Vector<KeyChild> children = mChildren.get(groupPosition);
 +            if (children != null) {
 +                return children;
 +            }
 +
 +            mCursor.moveToPosition(groupPosition);
 +            children = new Vector<KeyChild>();
 +            Cursor c = mDatabase.query(Keys.TABLE_NAME,
 +                    new String[] {
 +                        Keys._ID,           // 0
 +                        Keys.KEY_ID,        // 1
 +                        Keys.IS_MASTER_KEY, // 2
 +                        Keys.ALGORITHM,     // 3
 +                        Keys.KEY_SIZE,      // 4
 +                        Keys.CAN_SIGN,      // 5
 +                        Keys.CAN_ENCRYPT,   // 6
 +                    },
 +                    Keys.KEY_RING_ID + " = ?",
 +                    new String[] { mCursor.getString(0) },
 +                    null, null, Keys.RANK + " ASC");
 +
 +            long masterKeyId = -1;
 +            for (int i = 0; i < c.getCount(); ++i) {
 +                c.moveToPosition(i);
 +                children.add(new KeyChild(c.getLong(1), c.getInt(2) == 1, c.getInt(3), c.getInt(4),
 +                                          c.getInt(5) == 1, c.getInt(6) == 1));
 +                if (i == 0) {
 +                    masterKeyId = c.getInt(0);
 +                }
 +            }
 +            c.close();
 +
 +            if (masterKeyId != -1) {
 +                c = mDatabase.query(UserIds.TABLE_NAME,
 +                         new String[] {
 +                             UserIds.USER_ID, // 0
 +                         },
 +                         UserIds.KEY_ID + " = ? AND " + UserIds.RANK + " > 0",
 +                         new String[] { "" + masterKeyId },
 +                         null, null, UserIds.RANK + " ASC");
 +
 +                 for (int i = 0; i < c.getCount(); ++i) {
 +                     c.moveToPosition(i);
 +                     children.add(new KeyChild(c.getString(0)));
 +                 }
 +                 c.close();
 +            }
 +
 +            mChildren.set(groupPosition, children);
 +            return children;
 +        }
 +
 +        @Override
 +        public boolean hasStableIds() {
 +            return true;
 +        }
 +
 +        @Override
 +        public boolean isChildSelectable(int groupPosition, int childPosition) {
 +            return true;
 +        }
 +
 +        public int getGroupCount() {
 +            return mCursor.getCount();
 +        }
 +
 +        public Object getChild(int groupPosition, int childPosition) {
 +            return null;
 +        }
 +
 +        public long getChildId(int groupPosition, int childPosition) {
 +            return childPosition;
 +        }
 +
 +        public int getChildrenCount(int groupPosition) {
 +            return getChildrenOfGroup(groupPosition).size();
 +        }
 +
 +        public Object getGroup(int position) {
 +            return position;
 +        }
 +
 +        public long getGroupId(int position) {
 +            mCursor.moveToPosition(position);
 +            return mCursor.getLong(1); // MASTER_KEY_ID
 +        }
 +
 +        public int getKeyRingId(int position) {
 +            mCursor.moveToPosition(position);
 +            return mCursor.getInt(0); // _ID
 +        }
 +
 +        public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
 +                                 ViewGroup parent) {
 +            mCursor.moveToPosition(groupPosition);
 +
 +            View view = mInflater.inflate(R.layout.key_list_group_item, null);
 +            view.setBackgroundResource(android.R.drawable.list_selector_background);
 +
 +            TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
 +            mainUserId.setText("");
 +            TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
 +            mainUserIdRest.setText("");
 +
 +            String userId = mCursor.getString(2); // USER_ID
 +            if (userId != null) {
 +                String chunks[] = userId.split(" <", 2);
 +                userId = chunks[0];
 +                if (chunks.length > 1) {
 +                    mainUserIdRest.setText("<" + chunks[1]);
 +                }
 +                mainUserId.setText(userId);
 +            }
 +
 +            if (mainUserId.getText().length() == 0) {
 +                mainUserId.setText(R.string.unknownUserId);
 +            }
 +
 +            if (mainUserIdRest.getText().length() == 0) {
 +                mainUserIdRest.setVisibility(View.GONE);
 +            }
 +            return view;
 +        }
 +
 +        public View getChildView(int groupPosition, int childPosition,
 +                                 boolean isLastChild, View convertView,
 +                                 ViewGroup parent) {
 +            mCursor.moveToPosition(groupPosition);
 +
 +            Vector<KeyChild> children = getChildrenOfGroup(groupPosition);
 +
 +            KeyChild child = children.get(childPosition);
 +            View view = null;
 +            switch (child.type) {
 +                case KeyChild.KEY: {
 +                    if (child.isMasterKey) {
 +                        view = mInflater.inflate(R.layout.key_list_child_item_master_key, null);
 +                    } else {
 +                        view = mInflater.inflate(R.layout.key_list_child_item_sub_key, null);
 +                    }
 +
 +                    TextView keyId = (TextView) view.findViewById(R.id.keyId);
 +                    String keyIdStr = Long.toHexString(child.keyId & 0xffffffffL);
 +                    while (keyIdStr.length() < 8) {
 +                        keyIdStr = "0" + keyIdStr;
 +                    }
 +                    keyId.setText(keyIdStr);
 +                    TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
 +                    String algorithmStr = Apg.getAlgorithmInfo(child.algorithm, child.keySize);
 +                    keyDetails.setText("(" + algorithmStr + ")");
 +
 +                    ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
 +                    if (!child.canEncrypt) {
 +                        encryptIcon.setVisibility(View.GONE);
 +                    }
 +
 +                    ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
 +                    if (!child.canSign) {
 +                        signIcon.setVisibility(View.GONE);
 +                    }
 +                    break;
 +                }
 +
 +                case KeyChild.USER_ID: {
 +                    view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
 +                    TextView userId = (TextView) view.findViewById(R.id.userId);
 +                    userId.setText(child.userId);
 +                    break;
 +                }
 +            }
 +            return view;
 +        }
 +    }
 +
 +    @Override
 +    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 +        switch (requestCode) {
 +            case Id.request.filename: {
 +                if (resultCode == RESULT_OK && data != null) {
 +                    String filename = data.getDataString();
 +                    if (filename != null) {
 +                        // Get rid of URI prefix:
 +                        if (filename.startsWith("file://")) {
 +                            filename = filename.substring(7);
 +                        }
 +                        // replace %20 and so on
 +                        filename = Uri.decode(filename);
 +
 +                        FileDialog.setFilename(filename);
 +                    }
 +                }
 +                return;
 +            }
 +
 +            default: {
 +                break;
 +            }
 +        }
 +        super.onActivityResult(requestCode, resultCode, data);
 +    }
 +}
 diff --git a/src/org/thialfihar/android/apg/MailListActivity.java b/src/org/thialfihar/android/apg/MailListActivity.java index f0abe8f45..d166feede 100644 --- a/src/org/thialfihar/android/apg/MailListActivity.java +++ b/src/org/thialfihar/android/apg/MailListActivity.java @@ -87,7 +87,7 @@ public class MailListActivity extends ListActivity {          mconversations = new Vector<Conversation>();
          mmessages = new Vector<Message>();
 -        String account = getIntent().getExtras().getString("account");
 +        String account = getIntent().getExtras().getString(Apg.EXTRA_ACCOUNT);
          // TODO: what if account is null?
          Uri uri = Uri.parse("content://gmail-ls/conversations/" + account);
          Cursor cursor =
 @@ -153,9 +153,9 @@ public class MailListActivity extends ListActivity {                  Intent intent = new Intent(MailListActivity.this, DecryptActivity.class);
                  intent.setAction(Apg.Intent.DECRYPT);
                  Message message = (Message) ((MailboxAdapter) getListAdapter()).getItem(position);
 -                intent.putExtra("data", message.data);
 -                intent.putExtra("subject", message.subject);
 -                intent.putExtra("replyTo", message.replyTo);
 +                intent.putExtra(Apg.EXTRA_DATA, message.data);
 +                intent.putExtra(Apg.EXTRA_SUBJECT, message.subject);
 +                intent.putExtra(Apg.EXTRA_REPLY_TO, message.replyTo);
                  startActivity(intent);
              }
          });
 diff --git a/src/org/thialfihar/android/apg/MainActivity.java b/src/org/thialfihar/android/apg/MainActivity.java index a4d584304..65dd20d95 100644 --- a/src/org/thialfihar/android/apg/MainActivity.java +++ b/src/org/thialfihar/android/apg/MainActivity.java @@ -47,6 +47,8 @@ import android.widget.AdapterView.OnItemClickListener;  public class MainActivity extends BaseActivity {      private ListView mAccounts = null; +    private AccountListAdapter mListAdapter = null; +    private Cursor mAccountCursor;      @Override      public void onCreate(Bundle savedInstanceState) { @@ -95,22 +97,22 @@ public class MainActivity extends BaseActivity {              }          }); -        Cursor accountCursor = managedQuery(Accounts.CONTENT_URI, null, null, null, null); +        mAccountCursor = +                Apg.getDatabase().db().query(Accounts.TABLE_NAME, +                                             new String[] { +                                                 Accounts._ID, +                                                 Accounts.NAME, +                                             }, null, null, null, null, Accounts.NAME + " ASC"); +        startManagingCursor(mAccountCursor); -        mAccounts.setAdapter(new AccountListAdapter(this, accountCursor)); +        mListAdapter = new AccountListAdapter(this, mAccountCursor); +        mAccounts.setAdapter(mListAdapter);          mAccounts.setOnItemClickListener(new OnItemClickListener() {              @Override              public void onItemClick(AdapterView<?> arg0, View view, int index, long id) { -                Cursor cursor = -                        managedQuery(Uri.withAppendedPath(Accounts.CONTENT_URI, "" + id), null, -                                     null, null, null); -                if (cursor != null && cursor.getCount() > 0) { -                    cursor.moveToFirst(); -                    int nameIndex = cursor.getColumnIndex(Accounts.NAME); -                    String accountName = cursor.getString(nameIndex); -                    startActivity(new Intent(MainActivity.this, MailListActivity.class) -                                        .putExtra("account", accountName)); -                } +                String accountName = (String) mAccounts.getItemAtPosition(index); +                startActivity(new Intent(MainActivity.this, MailListActivity.class) +                                        .putExtra(Apg.EXTRA_ACCOUNT, accountName));              }          });          registerForContextMenu(mAccounts); @@ -154,9 +156,10 @@ public class MainActivity extends BaseActivity {                                  ContentValues values = new ContentValues();                                  values.put(Accounts.NAME, accountName);                                  try { -                                    MainActivity.this.getContentResolver() -                                                     .insert(Accounts.CONTENT_URI, -                                                             values); +                                    Apg.getDatabase().db().insert(Accounts.TABLE_NAME, +                                                                  Accounts.NAME, values); +                                    mAccountCursor.requery(); +                                    mListAdapter.notifyDataSetChanged();                                  } catch (SQLException e) {                                      Toast.makeText(MainActivity.this,                                                     getString(R.string.errorMessage, @@ -188,6 +191,12 @@ public class MainActivity extends BaseActivity {                  message.setText("Read the warnings!\n\n" +                                  "Changes:\n" + +                                "* k9mail integration, k9mail beta build is available on the k9mail website\n" + +                                "* support of other file managers (e.g. ASTRO)\n" + +                                "* Slovenian translation (thanks, 359)\n" + +                                "* new database, much faster, less memory usage\n" + +                                "* defined Intents and content provider for other apps\n" + +                                "* bugfixes\n" +                                  "\n" +                                  "WARNING: be careful editing your existing keys, as they " +                                  "WILL be stripped of certificates right now.\n" + @@ -277,8 +286,11 @@ public class MainActivity extends BaseActivity {          switch (menuItem.getItemId()) {              case Id.menu.delete: { -                Uri uri = Uri.withAppendedPath(Accounts.CONTENT_URI, "" + info.id); -                this.getContentResolver().delete(uri, null, null); +                Apg.getDatabase().db().delete(Accounts.TABLE_NAME, +                                              Accounts._ID + " = ?", +                                              new String[] { "" + info.id }); +                mAccountCursor.requery(); +                mListAdapter.notifyDataSetChanged();                  return true;              } @@ -298,6 +310,13 @@ public class MainActivity extends BaseActivity {          }          @Override +        public Object getItem(int position) { +            Cursor c = getCursor(); +            c.moveToPosition(position); +            return c.getString(c.getColumnIndex(Accounts.NAME)); +        } + +        @Override          public int getCount() {              return super.getCount();          } diff --git a/src/org/thialfihar/android/apg/PreferencesActivity.java b/src/org/thialfihar/android/apg/PreferencesActivity.java index fae63d63b..e80e1ad5f 100644 --- a/src/org/thialfihar/android/apg/PreferencesActivity.java +++ b/src/org/thialfihar/android/apg/PreferencesActivity.java @@ -50,7 +50,6 @@ public class PreferencesActivity extends BaseActivity {                  new Choice(180, getString(R.string.choice_3mins)),
                  new Choice(300, getString(R.string.choice_5mins)),
                  new Choice(600, getString(R.string.choice_10mins)),
 -                new Choice(0, getString(R.string.choice_untilQuit)),
          };
          ArrayAdapter<Choice> adapter =
                  new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices);
 diff --git a/src/org/thialfihar/android/apg/PublicKeyListActivity.java b/src/org/thialfihar/android/apg/PublicKeyListActivity.java index 67bc608ad..4997f60b7 100644 --- a/src/org/thialfihar/android/apg/PublicKeyListActivity.java +++ b/src/org/thialfihar/android/apg/PublicKeyListActivity.java @@ -16,54 +16,19 @@  package org.thialfihar.android.apg;
 -import java.io.FileNotFoundException;
 -import java.io.IOException;
 -import java.util.Vector;
 -
 -import org.bouncycastle2.openpgp.PGPException;
 -import org.bouncycastle2.openpgp.PGPPublicKey;
 -import org.bouncycastle2.openpgp.PGPPublicKeyRing;
 -import org.thialfihar.android.apg.utils.IterableIterator;
 -
 -import android.app.AlertDialog;
 -import android.app.Dialog;
 -import android.content.Context;
 -import android.content.DialogInterface;
 -import android.content.Intent;
 -import android.net.Uri;
  import android.os.Bundle;
 -import android.os.Message;
  import android.view.ContextMenu;
 -import android.view.LayoutInflater;
  import android.view.Menu;
 -import android.view.MenuItem;
  import android.view.View;
 -import android.view.ViewGroup;
  import android.view.ContextMenu.ContextMenuInfo;
 -import android.widget.BaseExpandableListAdapter;
  import android.widget.ExpandableListView;
 -import android.widget.ImageView;
 -import android.widget.TextView;
 -import android.widget.Toast;
 -import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
 -
 -public class PublicKeyListActivity extends BaseActivity {
 -    ExpandableListView mList;
 -
 -    protected int mSelectedItem = -1;
 -    protected int mTask = 0;
 -
 -    private String mImportFilename = Constants.path.app_dir + "/pubring.gpg";
 -    private String mExportFilename = Constants.path.app_dir + "/pubexport.asc";
 +public class PublicKeyListActivity extends KeyListActivity {
      @Override
 -    protected void onCreate(Bundle savedInstanceState) {
 +    public void onCreate(Bundle savedInstanceState) {
 +        mExportFilename = Constants.path.app_dir + "/pubexport.asc";
 +        mKeyType = Id.type.public_key;
          super.onCreate(savedInstanceState);
 -        setContentView(R.layout.key_list);
 -
 -        mList = (ExpandableListView) findViewById(R.id.list);
 -        mList.setAdapter(new PublicKeyListAdapter(this));
 -        registerForContextMenu(mList);
      }
      @Override
 @@ -80,506 +45,16 @@ public class PublicKeyListActivity extends BaseActivity {      }
      @Override
 -    public boolean onOptionsItemSelected(MenuItem item) {
 -        switch (item.getItemId()) {
 -            case Id.menu.option.import_keys: {
 -                showDialog(Id.dialog.import_keys);
 -                return true;
 -            }
 -
 -            case Id.menu.option.export_keys: {
 -                showDialog(Id.dialog.export_keys);
 -                return true;
 -            }
 -
 -            default: {
 -                return super.onOptionsItemSelected(item);
 -            }
 -        }
 -    }
 -
 -    @Override
      public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
          super.onCreateContextMenu(menu, v, menuInfo);
          ExpandableListView.ExpandableListContextMenuInfo info =
                  (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
          int type = ExpandableListView.getPackedPositionType(info.packedPosition);
 -        int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
          if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
 -            PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
 -            String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
 -            menu.setHeaderTitle(userId);
 +            // TODO: user id? menu.setHeaderTitle("Key");
              menu.add(0, Id.menu.export, 0, R.string.menu_exportKey);
              menu.add(0, Id.menu.delete, 1, R.string.menu_deleteKey);
          }
      }
 -
 -    @Override
 -    public boolean onContextItemSelected(MenuItem menuItem) {
 -        ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
 -        int type = ExpandableListView.getPackedPositionType(info.packedPosition);
 -        int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
 -
 -        if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
 -            return super.onContextItemSelected(menuItem);
 -        }
 -
 -        switch (menuItem.getItemId()) {
 -            case Id.menu.export: {
 -                mSelectedItem = groupPosition;
 -                showDialog(Id.dialog.export_key);
 -                return true;
 -            }
 -
 -            case Id.menu.delete: {
 -                mSelectedItem = groupPosition;
 -                showDialog(Id.dialog.delete_key);
 -                return true;
 -            }
 -
 -            default: {
 -                return super.onContextItemSelected(menuItem);
 -            }
 -        }
 -    }
 -
 -    @Override
 -    protected Dialog onCreateDialog(int id) {
 -        boolean singleKeyExport = false;
 -
 -        switch (id) {
 -            case Id.dialog.delete_key: {
 -                PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(mSelectedItem);
 -                String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
 -
 -                AlertDialog.Builder builder = new AlertDialog.Builder(this);
 -                builder.setTitle(R.string.warning);
 -                builder.setMessage(getString(R.string.keyDeletionConfirmation, userId));
 -                builder.setIcon(android.R.drawable.ic_dialog_alert);
 -                builder.setPositiveButton(R.string.btn_delete,
 -                                          new DialogInterface.OnClickListener() {
 -                                              public void onClick(DialogInterface dialog, int id) {
 -                                                  deleteKey(mSelectedItem);
 -                                                  mSelectedItem = -1;
 -                                                  removeDialog(Id.dialog.delete_key);
 -                                              }
 -                                          });
 -                builder.setNegativeButton(android.R.string.cancel,
 -                                          new DialogInterface.OnClickListener() {
 -                                              public void onClick(DialogInterface dialog, int id) {
 -                                                  mSelectedItem = -1;
 -                                                  removeDialog(Id.dialog.delete_key);
 -                                              }
 -                                          });
 -                return builder.create();
 -            }
 -
 -            case Id.dialog.import_keys: {
 -                return FileDialog.build(this, getString(R.string.title_importKeys),
 -                                        getString(R.string.specifyFileToImportFrom),
 -                                        mImportFilename,
 -                                        new FileDialog.OnClickListener() {
 -
 -                                            @Override
 -                                            public void onOkClick(String filename) {
 -                                                removeDialog(Id.dialog.import_keys);
 -                                                mImportFilename = filename;
 -                                                importKeys();
 -                                            }
 -
 -                                            @Override
 -                                            public void onCancelClick() {
 -                                                removeDialog(Id.dialog.import_keys);
 -                                            }
 -                                        },
 -                                        getString(R.string.filemanager_titleOpen),
 -                                        getString(R.string.filemanager_btnOpen),
 -                                        Id.request.filename);
 -            }
 -
 -            case Id.dialog.export_key: {
 -                singleKeyExport = true;
 -                // break intentionally omitted, to use the Id.dialog.export_keys dialog
 -            }
 -
 -            case Id.dialog.export_keys: {
 -                String title = (singleKeyExport ?
 -                                    getString(R.string.title_exportKey) :
 -                                    getString(R.string.title_exportKeys));
 -
 -                final int thisDialogId = (singleKeyExport ? Id.dialog.export_key : Id.dialog.export_keys);
 -
 -                return FileDialog.build(this, title,
 -                                        getString(R.string.specifyFileToExportTo),
 -                                        mExportFilename,
 -                                        new FileDialog.OnClickListener() {
 -
 -                                            @Override
 -                                            public void onOkClick(String filename) {
 -                                                removeDialog(thisDialogId);
 -                                                mExportFilename = filename;
 -                                                exportKeys();
 -                                            }
 -
 -                                            @Override
 -                                            public void onCancelClick() {
 -                                                removeDialog(thisDialogId);
 -                                            }
 -                                        },
 -                                        getString(R.string.filemanager_titleSave),
 -                                        getString(R.string.filemanager_btnSave),
 -                                        Id.request.filename);
 -            }
 -
 -            default: {
 -                return super.onCreateDialog(id);
 -            }
 -        }
 -    }
 -
 -    public void importKeys() {
 -        showDialog(Id.dialog.importing);
 -        mTask = Id.task.import_keys;
 -        startThread();
 -    }
 -
 -    public void exportKeys() {
 -        showDialog(Id.dialog.exporting);
 -        mTask = Id.task.export_keys;
 -        startThread();
 -    }
 -
 -    @Override
 -    public void run() {
 -        String error = null;
 -        Bundle data = new Bundle();
 -        Message msg = new Message();
 -
 -        String filename = null;
 -        if (mTask == Id.task.import_keys) {
 -            filename = mImportFilename;
 -        } else {
 -            filename = mExportFilename;
 -        }
 -
 -        try {
 -            if (mTask == Id.task.import_keys) {
 -                data = Apg.importKeyRings(this, Id.type.public_key, filename, this);
 -            } else {
 -                Vector<Object> keys = new Vector<Object>();
 -                if (mSelectedItem == -1) {
 -                    for (PGPPublicKeyRing key : Apg.getPublicKeyRings()) {
 -                        keys.add(key);
 -                    }
 -                } else {
 -                    keys.add(Apg.getPublicKeyRings().get(mSelectedItem));
 -                }
 -                data = Apg.exportKeyRings(this, keys, filename, this);
 -            }
 -        } catch (FileNotFoundException e) {
 -            error = getString(R.string.error_fileNotFound);
 -        } catch (IOException e) {
 -            error = e.getMessage();
 -        } catch (PGPException e) {
 -            error = e.getMessage();
 -        } catch (Apg.GeneralException e) {
 -            error = e.getMessage();
 -        }
 -
 -        if (mTask == Id.task.import_keys) {
 -            data.putInt("type", Id.message.import_done);
 -        } else {
 -            data.putInt("type", Id.message.export_done);
 -        }
 -
 -        if (error != null) {
 -            data.putString("error", error);
 -        }
 -
 -        msg.setData(data);
 -        sendMessage(msg);
 -    }
 -
 -    private void deleteKey(int index) {
 -        PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(index);
 -        Apg.deleteKey(this, keyRing);
 -        refreshList();
 -    }
 -
 -    private void refreshList() {
 -        ((PublicKeyListAdapter) mList.getExpandableListAdapter()).notifyDataSetChanged();
 -    }
 -
 -    @Override
 -    public void doneCallback(Message msg) {
 -        super.doneCallback(msg);
 -
 -        Bundle data = msg.getData();
 -        if (data != null) {
 -            int type = data.getInt("type");
 -            switch (type) {
 -                case Id.message.import_done: {
 -                    removeDialog(Id.dialog.importing);
 -
 -                    String error = data.getString("error");
 -                    if (error != null) {
 -                        Toast.makeText(PublicKeyListActivity.this,
 -                                       getString(R.string.errorMessage, data.getString("error")),
 -                                       Toast.LENGTH_SHORT).show();
 -                    } else {
 -                        int added = data.getInt("added");
 -                        int updated = data.getInt("updated");
 -                        String message;
 -                        if (added > 0 && updated > 0) {
 -                            message = getString(R.string.keysAddedAndUpdated, added, updated);
 -                        } else if (added > 0) {
 -                            message = getString(R.string.keysAdded, added);
 -                        } else if (updated > 0) {
 -                            message = getString(R.string.keysUpdated, updated);
 -                        } else {
 -                            message = getString(R.string.noKeysAddedOrUpdated);
 -                        }
 -                        Toast.makeText(PublicKeyListActivity.this, message,
 -                                       Toast.LENGTH_SHORT).show();
 -                    }
 -                    refreshList();
 -                    break;
 -                }
 -
 -                case Id.message.export_done: {
 -                    removeDialog(Id.dialog.exporting);
 -
 -                    String error = data.getString("error");
 -                    if (error != null) {
 -                        Toast.makeText(PublicKeyListActivity.this,
 -                                       getString(R.string.errorMessage, data.getString("error")),
 -                                       Toast.LENGTH_SHORT).show();
 -                    } else {
 -                        int exported = data.getInt("exported");
 -                        String message;
 -                        if (exported == 1) {
 -                            message = getString(R.string.keyExported);
 -                        } else if (exported > 0) {
 -                            message = getString(R.string.keysExported);
 -                        } else{
 -                            message = getString(R.string.noKeysExported);
 -                        }
 -                        Toast.makeText(PublicKeyListActivity.this, message,
 -                                       Toast.LENGTH_SHORT).show();
 -                    }
 -                    break;
 -                }
 -
 -                default: {
 -                    break;
 -                }
 -            }
 -        }
 -    }
 -
 -    private static class PublicKeyListAdapter extends BaseExpandableListAdapter {
 -        private LayoutInflater mInflater;
 -
 -        private class KeyChild {
 -            public static final int KEY = 0;
 -            public static final int USER_ID = 1;
 -
 -            public int type;
 -            public PGPPublicKey key;
 -            public String userId;
 -
 -            public KeyChild(PGPPublicKey key) {
 -                type = KEY;
 -                this.key = key;
 -            }
 -
 -            public KeyChild(String userId) {
 -                type = USER_ID;
 -                this.userId = userId;
 -            }
 -        }
 -
 -        public PublicKeyListAdapter(Context context) {
 -            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 -        }
 -
 -        protected Vector<KeyChild> getChildrenOfKeyRing(PGPPublicKeyRing keyRing) {
 -            Vector<KeyChild> children = new Vector<KeyChild>();
 -            PGPPublicKey masterKey = null;
 -            for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
 -                children.add(new KeyChild(key));
 -                if (key.isMasterKey()) {
 -                    masterKey = key;
 -                }
 -            }
 -
 -            if (masterKey != null) {
 -                boolean isFirst = true;
 -                for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
 -                    if (isFirst) {
 -                        // ignore first, it's in the group already
 -                        isFirst = false;
 -                        continue;
 -                    }
 -                    children.add(new KeyChild(userId));
 -                }
 -            }
 -
 -            return children;
 -        }
 -
 -        @Override
 -        public boolean hasStableIds() {
 -            return true;
 -        }
 -
 -        @Override
 -        public boolean isChildSelectable(int groupPosition, int childPosition) {
 -            return true;
 -        }
 -
 -        public int getGroupCount() {
 -            return Apg.getPublicKeyRings().size();
 -        }
 -
 -        public Object getChild(int groupPosition, int childPosition) {
 -            PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
 -            Vector<KeyChild> children = getChildrenOfKeyRing(keyRing);
 -
 -            KeyChild child = children.get(childPosition);
 -            return child;
 -        }
 -
 -        public long getChildId(int groupPosition, int childPosition) {
 -            return childPosition;
 -        }
 -
 -        public int getChildrenCount(int groupPosition) {
 -            return getChildrenOfKeyRing(Apg.getPublicKeyRings().get(groupPosition)).size();
 -        }
 -
 -        public Object getGroup(int position) {
 -            return position;
 -        }
 -
 -        public long getGroupId(int position) {
 -            return position;
 -        }
 -
 -        public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
 -                                 ViewGroup parent) {
 -            PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
 -            for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
 -                View view;
 -                if (!key.isMasterKey()) {
 -                    continue;
 -                }
 -                view = mInflater.inflate(R.layout.key_list_group_item, null);
 -                view.setBackgroundResource(android.R.drawable.list_selector_background);
 -
 -                TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
 -                mainUserId.setText("");
 -                TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
 -                mainUserIdRest.setText("");
 -
 -                String userId = Apg.getMainUserId(key);
 -                if (userId != null) {
 -                    String chunks[] = userId.split(" <", 2);
 -                    userId = chunks[0];
 -                    if (chunks.length > 1) {
 -                        mainUserIdRest.setText("<" + chunks[1]);
 -                    }
 -                    mainUserId.setText(userId);
 -                }
 -
 -                if (mainUserId.getText().length() == 0) {
 -                    mainUserId.setText(R.string.unknownUserId);
 -                }
 -
 -                if (mainUserIdRest.getText().length() == 0) {
 -                    mainUserIdRest.setVisibility(View.GONE);
 -                }
 -                return view;
 -            }
 -            return null;
 -        }
 -
 -        public View getChildView(int groupPosition, int childPosition,
 -                                 boolean isLastChild, View convertView,
 -                                 ViewGroup parent) {
 -            PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
 -            Vector<KeyChild> children = getChildrenOfKeyRing(keyRing);
 -
 -            KeyChild child = children.get(childPosition);
 -            View view = null;
 -            switch (child.type) {
 -                case KeyChild.KEY: {
 -                    PGPPublicKey key = child.key;
 -                    if (key.isMasterKey()) {
 -                        view = mInflater.inflate(R.layout.key_list_child_item_master_key, null);
 -                    } else {
 -                        view = mInflater.inflate(R.layout.key_list_child_item_sub_key, null);
 -                    }
 -
 -                    TextView keyId = (TextView) view.findViewById(R.id.keyId);
 -                    String keyIdStr = Long.toHexString(key.getKeyID() & 0xffffffffL);
 -                    while (keyIdStr.length() < 8) {
 -                        keyIdStr = "0" + keyIdStr;
 -                    }
 -                    keyId.setText(keyIdStr);
 -                    TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
 -                    String algorithmStr = Apg.getAlgorithmInfo(key);
 -                    keyDetails.setText("(" + algorithmStr + ")");
 -
 -                    ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
 -                    if (!Apg.isEncryptionKey(key)) {
 -                        encryptIcon.setVisibility(View.GONE);
 -                    }
 -
 -                    ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
 -                    if (!Apg.isSigningKey(key)) {
 -                        signIcon.setVisibility(View.GONE);
 -                    }
 -                    break;
 -                }
 -
 -                case KeyChild.USER_ID: {
 -                    view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
 -                    TextView userId = (TextView) view.findViewById(R.id.userId);
 -                    userId.setText(child.userId);
 -                    break;
 -                }
 -            }
 -            return view;
 -        }
 -    }
 -
 -    @Override
 -    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 -        switch (requestCode) {
 -            case Id.request.filename: {
 -                if (resultCode == RESULT_OK && data != null) {
 -                    String filename = data.getDataString();
 -                    if (filename != null) {
 -                        // Get rid of URI prefix:
 -                        if (filename.startsWith("file://")) {
 -                            filename = filename.substring(7);
 -                        }
 -                        // replace %20 and so on
 -                        filename = Uri.decode(filename);
 -
 -                        FileDialog.setFilename(filename);
 -                    }
 -
 -                }
 -                return;
 -            }
 -
 -            default: {
 -                break;
 -            }
 -        }
 -        super.onActivityResult(requestCode, resultCode, data);
 -    }
  }
 diff --git a/src/org/thialfihar/android/apg/SecretKeyListActivity.java b/src/org/thialfihar/android/apg/SecretKeyListActivity.java index a69fc5b9c..0252c46d2 100644 --- a/src/org/thialfihar/android/apg/SecretKeyListActivity.java +++ b/src/org/thialfihar/android/apg/SecretKeyListActivity.java @@ -16,55 +16,24 @@  package org.thialfihar.android.apg;
 -import java.io.FileNotFoundException;
 -import java.io.IOException;
 -import java.util.Vector;
 -
 -import org.bouncycastle2.openpgp.PGPException;
 -import org.bouncycastle2.openpgp.PGPSecretKey;
 -import org.bouncycastle2.openpgp.PGPSecretKeyRing;
 -import org.thialfihar.android.apg.utils.IterableIterator;
 -
 -import android.app.AlertDialog;
  import android.app.Dialog;
 -import android.content.Context;
 -import android.content.DialogInterface;
  import android.content.Intent;
 -import android.net.Uri;
  import android.os.Bundle;
 -import android.os.Message;
  import android.view.ContextMenu;
 -import android.view.LayoutInflater;
  import android.view.Menu;
  import android.view.MenuItem;
  import android.view.View;
 -import android.view.ViewGroup;
  import android.view.ContextMenu.ContextMenuInfo;
 -import android.widget.BaseExpandableListAdapter;
  import android.widget.ExpandableListView;
 -import android.widget.ImageView;
 -import android.widget.TextView;
 -import android.widget.Toast;
  import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
  import android.widget.ExpandableListView.OnChildClickListener;
 -public class SecretKeyListActivity extends BaseActivity implements OnChildClickListener {
 -    ExpandableListView mList;
 -
 -    protected int mSelectedItem = -1;
 -    protected int mTask = 0;
 -
 -    private String mImportFilename = Constants.path.app_dir + "/secring.gpg";
 -    private String mExportFilename = Constants.path.app_dir + "/secexport.asc";
 -
 +public class SecretKeyListActivity extends KeyListActivity implements OnChildClickListener {
      @Override
 -    protected void onCreate(Bundle savedInstanceState) {
 +    public void onCreate(Bundle savedInstanceState) {
 +        mExportFilename = Constants.path.app_dir + "/secexport.asc";
 +        mKeyType = Id.type.secret_key;
          super.onCreate(savedInstanceState);
 -        setContentView(R.layout.key_list);
 -
 -        mList = (ExpandableListView) findViewById(R.id.list);
 -        mList.setAdapter(new SecretKeyListAdapter(this));
 -        registerForContextMenu(mList);
          mList.setOnChildClickListener(this);
      }
 @@ -86,16 +55,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL      @Override
      public boolean onOptionsItemSelected(MenuItem item) {
          switch (item.getItemId()) {
 -            case Id.menu.option.import_keys: {
 -                showDialog(Id.dialog.import_keys);
 -                return true;
 -            }
 -
 -            case Id.menu.option.export_keys: {
 -                showDialog(Id.dialog.export_keys);
 -                return true;
 -            }
 -
              case Id.menu.option.create: {
                  createKey();
                  return true;
 @@ -113,12 +72,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL          ExpandableListView.ExpandableListContextMenuInfo info =
                  (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
          int type = ExpandableListView.getPackedPositionType(info.packedPosition);
 -        int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
          if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
 -            PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
 -            String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
 -            menu.setHeaderTitle(userId);
 +            // TODO: user id? menu.setHeaderTitle("Key");
              menu.add(0, Id.menu.edit, 0, R.string.menu_editKey);
              menu.add(0, Id.menu.export, 1, R.string.menu_exportKey);
              menu.add(0, Id.menu.delete, 2, R.string.menu_deleteKey);
 @@ -142,18 +98,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL                  return true;
              }
 -            case Id.menu.export: {
 -                mSelectedItem = groupPosition;
 -                showDialog(Id.dialog.export_key);
 -                return true;
 -            }
 -
 -            case Id.menu.delete: {
 -                mSelectedItem = groupPosition;
 -                showDialog(Id.dialog.delete_key);
 -                return true;
 -            }
 -
              default: {
                  return super.onContextItemSelected(menuItem);
              }
 @@ -170,96 +114,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL      @Override
      protected Dialog onCreateDialog(int id) {
 -        boolean singleKeyExport = false;
 -
          switch (id) {
 -            case Id.dialog.delete_key: {
 -                PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
 -
 -                String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
 -
 -                AlertDialog.Builder builder = new AlertDialog.Builder(this);
 -                builder.setTitle(R.string.warning);
 -                builder.setMessage(getString(R.string.secretKeyDeletionConfirmation, userId));
 -                builder.setIcon(android.R.drawable.ic_dialog_alert);
 -                builder.setPositiveButton(R.string.btn_delete,
 -                                          new DialogInterface.OnClickListener() {
 -                                              public void onClick(DialogInterface dialog, int id) {
 -                                                  deleteKey(mSelectedItem);
 -                                                  mSelectedItem = -1;
 -                                                  removeDialog(Id.dialog.delete_key);
 -                                              }
 -                                          });
 -                builder.setNegativeButton(android.R.string.ok,
 -                                          new DialogInterface.OnClickListener() {
 -                                              public void onClick(DialogInterface dialog, int id) {
 -                                                  mSelectedItem = -1;
 -                                                  removeDialog(Id.dialog.delete_key);
 -                                              }
 -                                          });
 -                return builder.create();
 -            }
 -
 -            case Id.dialog.import_keys: {
 -                return FileDialog.build(this, getString(R.string.title_importKeys),
 -                                        getString(R.string.specifyFileToImportFrom),
 -                                        mImportFilename,
 -                                        new FileDialog.OnClickListener() {
 -
 -                                            @Override
 -                                            public void onOkClick(String filename) {
 -                                                removeDialog(Id.dialog.import_keys);
 -                                                mImportFilename = filename;
 -                                                importKeys();
 -                                            }
 -
 -                                            @Override
 -                                            public void onCancelClick() {
 -                                                removeDialog(Id.dialog.import_keys);
 -                                            }
 -                                        },
 -                                        getString(R.string.filemanager_titleOpen),
 -                                        getString(R.string.filemanager_btnOpen),
 -                                        Id.request.filename);
 -            }
 -
 -            case Id.dialog.export_key: {
 -                singleKeyExport = true;
 -                // break intentionally omitted, to use the Id.dialog.export_keys dialog
 -            }
 -
 -            case Id.dialog.export_keys: {
 -                String title = (singleKeyExport ?
 -                                     getString(R.string.title_exportKey) :
 -                                     getString(R.string.title_exportKeys));
 -
 -                final int thisDialogId = (singleKeyExport ? Id.dialog.export_key : Id.dialog.export_keys);
 -
 -                return FileDialog.build(this, title,
 -                                        getString(R.string.specifyFileToExportSecretKeysTo),
 -                                        mExportFilename,
 -                                        new FileDialog.OnClickListener() {
 -
 -                                            @Override
 -                                            public void onOkClick(String filename) {
 -                                                removeDialog(thisDialogId);
 -                                                mExportFilename = filename;
 -                                                exportKeys();
 -                                            }
 -
 -                                            @Override
 -                                            public void onCancelClick() {
 -                                                removeDialog(thisDialogId);
 -                                            }
 -                                        },
 -                                        getString(R.string.filemanager_titleSave),
 -                                        getString(R.string.filemanager_btnSave),
 -                                        Id.request.filename);
 -            }
 -
              case Id.dialog.pass_phrase: {
 -                PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
 -                long keyId = keyRing.getSecretKey().getKeyID();
 +                long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
                  return AskForSecretKeyPassPhrase.createDialog(this, keyId, this);
              }
 @@ -270,8 +127,7 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL      }
      public void checkPassPhraseAndEdit() {
 -        PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
 -        long keyId = keyRing.getSecretKey().getKeyID();
 +        long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
          String passPhrase = Apg.getCachedPassPhrase(keyId);
          if (passPhrase == null) {
              showDialog(Id.dialog.pass_phrase);
 @@ -295,10 +151,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL      }
      private void editKey() {
 -        PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
 -        long keyId = keyRing.getSecretKey().getKeyID();
 +        long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
          Intent intent = new Intent(this, EditKeyActivity.class);
 -        intent.putExtra("keyId", keyId);
 +        intent.putExtra(Apg.EXTRA_KEY_ID, keyId);
          startActivityForResult(intent, Id.message.edit_key);
      }
 @@ -313,24 +168,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL                  break;
              }
 -            case Id.request.filename: {
 -                if (resultCode == RESULT_OK && data != null) {
 -                    String filename = data.getDataString();
 -                    if (filename != null) {
 -                        // Get rid of URI prefix:
 -                        if (filename.startsWith("file://")) {
 -                            filename = filename.substring(7);
 -                        }
 -                        // replace %20 and so on
 -                        filename = Uri.decode(filename);
 -
 -                        FileDialog.setFilename(filename);
 -                    }
 -
 -                }
 -                return;
 -            }
 -
              default: {
                  break;
              }
 @@ -338,320 +175,4 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL          super.onActivityResult(requestCode, resultCode, data);
      }
 -
 -    public void importKeys() {
 -        showDialog(Id.dialog.importing);
 -        mTask = Id.task.import_keys;
 -        startThread();
 -    }
 -
 -    public void exportKeys() {
 -        showDialog(Id.dialog.exporting);
 -        mTask = Id.task.export_keys;
 -        startThread();
 -    }
 -
 -    @Override
 -    public void run() {
 -        String error = null;
 -        Bundle data = new Bundle();
 -        Message msg = new Message();
 -
 -        String filename = null;
 -        if (mTask == Id.task.import_keys) {
 -            filename = mImportFilename;
 -        } else {
 -            filename = mExportFilename;
 -        }
 -
 -        try {
 -            if (mTask == Id.task.import_keys) {
 -                data = Apg.importKeyRings(this, Id.type.secret_key, filename, this);
 -            } else {
 -                Vector<Object> keys = new Vector<Object>();
 -                if (mSelectedItem == -1) {
 -                    for (PGPSecretKeyRing key : Apg.getSecretKeyRings()) {
 -                        keys.add(key);
 -                    }
 -                } else {
 -                    keys.add(Apg.getSecretKeyRings().get(mSelectedItem));
 -                }
 -                data = Apg.exportKeyRings(this, keys, filename, this);
 -            }
 -        } catch (FileNotFoundException e) {
 -            error = getString(R.string.error_fileNotFound);
 -        } catch (IOException e) {
 -            error = e.getMessage();
 -        } catch (PGPException e) {
 -            error = e.getMessage();
 -        } catch (Apg.GeneralException e) {
 -            error = e.getMessage();
 -        }
 -
 -        if (mTask == Id.task.import_keys) {
 -            data.putInt("type", Id.message.import_done);
 -        } else {
 -            data.putInt("type", Id.message.export_done);
 -        }
 -
 -        if (error != null) {
 -            data.putString("error", error);
 -        }
 -
 -        msg.setData(data);
 -        sendMessage(msg);
 -    }
 -
 -    private void deleteKey(int index) {
 -        PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(index);
 -        Apg.deleteKey(this, keyRing);
 -        refreshList();
 -    }
 -
 -    private void refreshList() {
 -        ((SecretKeyListAdapter) mList.getExpandableListAdapter()).notifyDataSetChanged();
 -    }
 -
 -    @Override
 -    public void doneCallback(Message msg) {
 -        super.doneCallback(msg);
 -
 -        Bundle data = msg.getData();
 -        if (data != null) {
 -            int type = data.getInt("type");
 -            switch (type) {
 -                case Id.message.import_done: {
 -                    removeDialog(Id.dialog.importing);
 -
 -                    String error = data.getString("error");
 -                    if (error != null) {
 -                        Toast.makeText(SecretKeyListActivity.this,
 -                                       getString(R.string.errorMessage, data.getString("error")),
 -                                       Toast.LENGTH_SHORT).show();
 -                    } else {
 -                        int added = data.getInt("added");
 -                        int updated = data.getInt("updated");
 -                        String message;
 -                        if (added > 0 && updated > 0) {
 -                            message = getString(R.string.keysAddedAndUpdated, added, updated);
 -                        } else if (added > 0) {
 -                            message = getString(R.string.keysAdded, added);
 -                        } else if (updated > 0) {
 -                            message = getString(R.string.keysUpdated, updated);
 -                        } else {
 -                            message = getString(R.string.noKeysAddedOrUpdated);
 -                        }
 -                        Toast.makeText(SecretKeyListActivity.this, message,
 -                                       Toast.LENGTH_SHORT).show();
 -                    }
 -                    refreshList();
 -                    break;
 -                }
 -
 -                case Id.message.export_done: {
 -                    removeDialog(Id.dialog.exporting);
 -
 -                    String error = data.getString("error");
 -                    if (error != null) {
 -                        Toast.makeText(SecretKeyListActivity.this,
 -                                       getString(R.string.errorMessage, data.getString("error")),
 -                                       Toast.LENGTH_SHORT).show();
 -                    } else {
 -                        int exported = data.getInt("exported");
 -                        String message;
 -                        if (exported == 1) {
 -                            message = getString(R.string.keyExported);
 -                        } else if (exported > 0) {
 -                            message = getString(R.string.keysExported);
 -                        } else{
 -                            message = getString(R.string.noKeysExported);
 -                        }
 -                        Toast.makeText(SecretKeyListActivity.this, message,
 -                                       Toast.LENGTH_SHORT).show();
 -                    }
 -                    break;
 -                }
 -
 -                default: {
 -                    break;
 -                }
 -            }
 -        }
 -    }
 -
 -    private static class SecretKeyListAdapter extends BaseExpandableListAdapter {
 -        private LayoutInflater mInflater;
 -
 -        private class KeyChild {
 -            static final int KEY = 0;
 -            static final int USER_ID = 1;
 -
 -            public int type;
 -            public PGPSecretKey key;
 -            public String userId;
 -
 -            public KeyChild(PGPSecretKey key) {
 -                type = KEY;
 -                this.key = key;
 -            }
 -
 -            public KeyChild(String userId) {
 -                type = USER_ID;
 -                this.userId = userId;
 -            }
 -        }
 -
 -        public SecretKeyListAdapter(Context context) {
 -            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 -        }
 -
 -        protected Vector<KeyChild> getChildrenOfKeyRing(PGPSecretKeyRing keyRing) {
 -            Vector<KeyChild> children = new Vector<KeyChild>();
 -            PGPSecretKey masterKey = null;
 -            for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
 -                children.add(new KeyChild(key));
 -                if (key.isMasterKey()) {
 -                    masterKey = key;
 -                }
 -            }
 -
 -            if (masterKey != null) {
 -                boolean isFirst = true;
 -                for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
 -                    if (isFirst) {
 -                        // ignore first, it's in the group already
 -                        isFirst = false;
 -                        continue;
 -                    }
 -                    children.add(new KeyChild(userId));
 -                }
 -            }
 -
 -            return children;
 -        }
 -
 -        @Override
 -        public boolean hasStableIds() {
 -            return true;
 -        }
 -
 -        @Override
 -        public boolean isChildSelectable(int groupPosition, int childPosition) {
 -            return true;
 -        }
 -
 -        public int getGroupCount() {
 -            return Apg.getSecretKeyRings().size();
 -        }
 -
 -        public Object getChild(int groupPosition, int childPosition) {
 -            PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
 -            Vector<KeyChild> children = getChildrenOfKeyRing(keyRing);
 -            KeyChild child = children.get(childPosition);
 -            return child;
 -        }
 -
 -        public long getChildId(int groupPosition, int childPosition) {
 -            return childPosition;
 -        }
 -
 -        public int getChildrenCount(int groupPosition) {
 -            return getChildrenOfKeyRing(Apg.getSecretKeyRings().get(groupPosition)).size();
 -        }
 -
 -        public Object getGroup(int position) {
 -            return position;
 -        }
 -
 -        public long getGroupId(int position) {
 -            return position;
 -        }
 -
 -        public View getGroupView(int groupPosition, boolean isExpanded,
 -                                 View convertView, ViewGroup parent) {
 -            PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
 -            for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
 -                View view;
 -                if (!key.isMasterKey()) {
 -                    continue;
 -                }
 -                view = mInflater.inflate(R.layout.key_list_group_item, null);
 -                view.setBackgroundResource(android.R.drawable.list_selector_background);
 -
 -                TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
 -                mainUserId.setText("");
 -                TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
 -                mainUserIdRest.setText("");
 -
 -                String userId = Apg.getMainUserId(key);
 -                if (userId != null) {
 -                    String chunks[] = userId.split(" <", 2);
 -                    userId = chunks[0];
 -                    if (chunks.length > 1) {
 -                        mainUserIdRest.setText("<" + chunks[1]);
 -                    }
 -                    mainUserId.setText(userId);
 -                }
 -
 -                if (mainUserId.getText().length() == 0) {
 -                    mainUserId.setText(R.string.unknownUserId);
 -                }
 -
 -                if (mainUserIdRest.getText().length() == 0) {
 -                    mainUserIdRest.setVisibility(View.GONE);
 -                }
 -                return view;
 -            }
 -            return null;
 -        }
 -
 -        public View getChildView(int groupPosition, int childPosition,
 -                                 boolean isLastChild, View convertView,
 -                                 ViewGroup parent) {
 -            PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
 -            Vector<KeyChild> children = getChildrenOfKeyRing(keyRing);
 -
 -            KeyChild child = children.get(childPosition);
 -            View view = null;
 -            switch (child.type) {
 -                case KeyChild.KEY: {
 -                    PGPSecretKey key = child.key;
 -                    if (key.isMasterKey()) {
 -                        view = mInflater.inflate(R.layout.key_list_child_item_master_key, null);
 -                    } else {
 -                        view = mInflater.inflate(R.layout.key_list_child_item_sub_key, null);
 -                    }
 -
 -                    TextView keyId = (TextView) view.findViewById(R.id.keyId);
 -                    String keyIdStr = Long.toHexString(key.getKeyID() & 0xffffffffL);
 -                    while (keyIdStr.length() < 8) {
 -                        keyIdStr = "0" + keyIdStr;
 -                    }
 -                    keyId.setText(keyIdStr);
 -                    TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
 -                    String algorithmStr = Apg.getAlgorithmInfo(key);
 -                    keyDetails.setText("(" + algorithmStr + ")");
 -
 -                    ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
 -                    if (!Apg.isEncryptionKey(key)) {
 -                        encryptIcon.setVisibility(View.GONE);
 -                    }
 -
 -                    ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
 -                    if (!Apg.isSigningKey(key)) {
 -                        signIcon.setVisibility(View.GONE);
 -                    }
 -                    break;
 -                }
 -
 -                case KeyChild.USER_ID: {
 -                    view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
 -                    TextView userId = (TextView) view.findViewById(R.id.userId);
 -                    userId.setText(child.userId);
 -                    break;
 -                }
 -            }
 -            return view;
 -        }
 -    }
  }
 diff --git a/src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java b/src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java index fbb0b6fe0..aeb6d59a3 100644 --- a/src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java +++ b/src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java @@ -16,12 +16,8 @@  package org.thialfihar.android.apg; -import java.util.Collections;  import java.util.Vector; -import org.bouncycastle2.openpgp.PGPPublicKey; -import org.bouncycastle2.openpgp.PGPPublicKeyRing; -  import android.content.Intent;  import android.os.Bundle;  import android.view.View; @@ -42,27 +38,21 @@ public class SelectPublicKeyListActivity extends BaseActivity {          mIntent = getIntent();          long selectedKeyIds[] = null;          if (mIntent.getExtras() != null) { -            selectedKeyIds = mIntent.getExtras().getLongArray("selection"); +            selectedKeyIds = mIntent.getExtras().getLongArray(Apg.EXTRA_SELECTION);          }          mList = (ListView) findViewById(R.id.list);          // needed in Android 1.5, where the XML attribute gets ignored          mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); -        Vector<PGPPublicKeyRing> keyRings = -                (Vector<PGPPublicKeyRing>) Apg.getPublicKeyRings().clone(); -        Collections.sort(keyRings, new Apg.PublicKeySorter()); -        mList.setAdapter(new SelectPublicKeyListAdapter(mList, keyRings)); +        SelectPublicKeyListAdapter adapter = new SelectPublicKeyListAdapter(this, mList); +        mList.setAdapter(adapter);          if (selectedKeyIds != null) { -            for (int i = 0; i < keyRings.size(); ++i) { -                PGPPublicKeyRing keyRing = keyRings.get(i); -                PGPPublicKey key = Apg.getMasterKey(keyRing); -                if (key == null) { -                    continue; -                } +            for (int i = 0; i < adapter.getCount(); ++i) { +                long keyId = adapter.getItemId(i);                  for (int j = 0; j < selectedKeyIds.length; ++j) { -                    if (key.getKeyID() == selectedKeyIds[j]) { +                    if (keyId == selectedKeyIds[j]) {                          mList.setItemChecked(i, true);                          break;                      } @@ -106,8 +96,8 @@ public class SelectPublicKeyListActivity extends BaseActivity {          for (int i = 0; i < vector.size(); ++i) {              selectedKeyIds[i] = vector.get(i);          } -        data.putExtra("selection", selectedKeyIds); +        data.putExtra(Apg.EXTRA_SELECTION, selectedKeyIds);          setResult(RESULT_OK, data);          finish();      } -}
\ No newline at end of file +} diff --git a/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java b/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java index 1b0b82fd8..ffc344ead 100644 --- a/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java +++ b/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java @@ -16,15 +16,16 @@  package org.thialfihar.android.apg;
 -import java.text.DateFormat;
  import java.util.Date;
 -import java.util.Vector;
 -import org.bouncycastle2.openpgp.PGPPublicKey;
 -import org.bouncycastle2.openpgp.PGPPublicKeyRing;
 -import org.thialfihar.android.apg.utils.IterableIterator;
 +import org.thialfihar.android.apg.provider.KeyRings;
 +import org.thialfihar.android.apg.provider.Keys;
 +import org.thialfihar.android.apg.provider.UserIds;
 +import android.app.Activity;
  import android.content.Context;
 +import android.database.Cursor;
 +import android.database.sqlite.SQLiteDatabase;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.ViewGroup;
 @@ -34,40 +35,55 @@ import android.widget.ListView;  import android.widget.TextView;
  public class SelectPublicKeyListAdapter extends BaseAdapter {
 -    protected Vector<PGPPublicKeyRing> mKeyRings;
      protected LayoutInflater mInflater;
      protected ListView mParent;
 +    protected SQLiteDatabase mDatabase;
 +    protected Cursor mCursor;
 -    public SelectPublicKeyListAdapter(ListView parent,
 -                                Vector<PGPPublicKeyRing> keyRings) {
 -        setKeyRings(keyRings);
 +    public SelectPublicKeyListAdapter(Activity activity, ListView parent) {
          mParent = parent;
 +        mDatabase =  Apg.getDatabase().db();
          mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 -    }
 -
 -    public void setKeyRings(Vector<PGPPublicKeyRing> keyRings) {
 -        mKeyRings = keyRings;
 -        notifyDataSetChanged();
 -    }
 -
 -    public Vector<PGPPublicKeyRing> getKeyRings() {
 -        return mKeyRings;
 +        long now = new Date().getTime() / 1000;
 +        mCursor = mDatabase.query(
 +              KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
 +                                    "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
 +                                    Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
 +                                    Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
 +                                    ") " +
 +                                    " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
 +                                    "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
 +                                    UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
 +                                    UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ",
 +              new String[] {
 +                  KeyRings.TABLE_NAME + "." + KeyRings._ID,           // 0
 +                  KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
 +                  UserIds.TABLE_NAME + "." + UserIds.USER_ID,         // 2
 +                  "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
 +                      "tmp." + Keys.KEY_RING_ID + " = " +
 +                      KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
 +                      "tmp." + Keys.IS_REVOKED + " = '0' AND " +
 +                      "tmp." + Keys.CAN_ENCRYPT + " = '1')",          // 3
 +                  "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
 +                      "tmp." + Keys.KEY_RING_ID + " = " +
 +                      KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
 +                      "tmp." + Keys.IS_REVOKED + " = '0' AND " +
 +                      "tmp." + Keys.CAN_ENCRYPT + " = '1' AND " +
 +                      "tmp." + Keys.CREATION + " <= '" + now + "' AND " +
 +                      "(tmp." + Keys.EXPIRY + " IS NULL OR " +
 +                       "tmp." + Keys.EXPIRY + " >= '" + now + "'))",  // 4
 +              },
 +              KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
 +              new String[] { "" + Id.database.type_public },
 +              null, null, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC");
 +
 +        activity.startManagingCursor(mCursor);
      }
      @Override
      public boolean isEnabled(int position) {
 -        PGPPublicKeyRing keyRing = mKeyRings.get(position);
 -
 -        if (Apg.getMasterKey(keyRing) == null) {
 -            return false;
 -        }
 -
 -        Vector<PGPPublicKey> encryptKeys = Apg.getUsableEncryptKeys(keyRing);
 -        if (encryptKeys.size() == 0) {
 -            return false;
 -        }
 -
 -        return true;
 +        mCursor.moveToPosition(position);
 +        return mCursor.getInt(4) > 0; // valid CAN_ENCRYPT
      }
      @Override
 @@ -77,93 +93,62 @@ public class SelectPublicKeyListAdapter extends BaseAdapter {      @Override
      public int getCount() {
 -        return mKeyRings.size();
 +        return mCursor.getCount();
      }
      @Override
      public Object getItem(int position) {
 -        return mKeyRings.get(position);
 +        return position;
      }
      @Override
      public long getItemId(int position) {
 -        PGPPublicKeyRing keyRing = mKeyRings.get(position);
 -        PGPPublicKey key = Apg.getMasterKey(keyRing);
 -        if (key != null) {
 -            return key.getKeyID();
 -        }
 -
 -        return 0;
 +        mCursor.moveToPosition(position);
 +        return mCursor.getLong(1); // MASTER_KEY_ID
      }
      @Override
      public View getView(int position, View convertView, ViewGroup parent) {
 +        mCursor.moveToPosition(position);
 +
          View view = mInflater.inflate(R.layout.select_public_key_item, null);
          boolean enabled = isEnabled(position);
 -        PGPPublicKeyRing keyRing = mKeyRings.get(position);
 -        PGPPublicKey key = null;
 -        for (PGPPublicKey tKey : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
 -            if (tKey.isMasterKey()) {
 -                key = tKey;
 -                break;
 -            }
 -        }
 -
 -        Vector<PGPPublicKey> encryptKeys = Apg.getEncryptKeys(keyRing);
 -        Vector<PGPPublicKey> usableKeys = Apg.getUsableEncryptKeys(keyRing);
 -
          TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
          mainUserId.setText(R.string.unknownUserId);
          TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
          mainUserIdRest.setText("");
          TextView keyId = (TextView) view.findViewById(R.id.keyId);
          keyId.setText(R.string.noKey);
 -        TextView creation = (TextView) view.findViewById(R.id.creation);
 -        creation.setText(R.string.noDate);
 -        TextView expiry = (TextView) view.findViewById(R.id.expiry);
 -        expiry.setText(R.string.noExpiry);
          TextView status = (TextView) view.findViewById(R.id.status);
          status.setText(R.string.unknownStatus);
 -        if (key != null) {
 -            String userId = Apg.getMainUserId(key);
 -            if (userId != null) {
 -                String chunks[] = userId.split(" <", 2);
 -                userId = chunks[0];
 -                if (chunks.length > 1) {
 -                    mainUserIdRest.setText("<" + chunks[1]);
 -                }
 -                mainUserId.setText(userId);
 +        String userId = mCursor.getString(2); // USER_ID
 +        if (userId != null) {
 +            String chunks[] = userId.split(" <", 2);
 +            userId = chunks[0];
 +            if (chunks.length > 1) {
 +                mainUserIdRest.setText("<" + chunks[1]);
              }
 -
 -            keyId.setText("" + Long.toHexString(key.getKeyID() & 0xffffffffL));
 +            mainUserId.setText(userId);
          }
 +        long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
 +        keyId.setText("" + Long.toHexString(masterKeyId & 0xffffffffL));
 +
          if (mainUserIdRest.getText().length() == 0) {
              mainUserIdRest.setVisibility(View.GONE);
          }
 -        PGPPublicKey timespanKey = key;
 -        if (usableKeys.size() > 0) {
 -            timespanKey = usableKeys.get(0);
 +        if (enabled) {
              status.setText(R.string.canEncrypt);
 -        } else if (encryptKeys.size() > 0) {
 -            timespanKey = encryptKeys.get(0);
 -            Date now = new Date();
 -            if (now.compareTo(Apg.getCreationDate(timespanKey)) > 0) {
 -                status.setText(R.string.notValid);
 -            } else {
 +        } else {
 +            if (mCursor.getInt(3) > 0) {
 +                // has some CAN_ENCRYPT keys, but col(4) = 0, so must be revoked or expired
                  status.setText(R.string.expired);
 +            } else {
 +                status.setText(R.string.noKey);
              }
 -        } else {
 -            status.setText(R.string.noKey);
 -        }
 -
 -        creation.setText(DateFormat.getDateInstance().format(Apg.getCreationDate(timespanKey)));
 -        Date expiryDate = Apg.getExpiryDate(timespanKey);
 -        if (expiryDate != null) {
 -            expiry.setText(DateFormat.getDateInstance().format(expiryDate));
          }
          status.setText(status.getText() + " ");
 @@ -176,8 +161,6 @@ public class SelectPublicKeyListAdapter extends BaseAdapter {          mainUserId.setEnabled(enabled);
          mainUserIdRest.setEnabled(enabled);
          keyId.setEnabled(enabled);
 -        creation.setEnabled(enabled);
 -        expiry.setEnabled(enabled);
          selected.setEnabled(enabled);
          status.setEnabled(enabled);
 diff --git a/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java b/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java index b6811d6e3..cd87a94b6 100644 --- a/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java +++ b/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java @@ -16,32 +16,16 @@  package org.thialfihar.android.apg; -import java.text.DateFormat; -import java.util.Collections; -import java.util.Date; -import java.util.Vector; - -import org.bouncycastle2.openpgp.PGPSecretKey; -import org.bouncycastle2.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.utils.IterableIterator; - -import android.content.Context;  import android.content.Intent;  import android.os.Bundle; -import android.view.LayoutInflater;  import android.view.View; -import android.view.ViewGroup;  import android.widget.AdapterView; -import android.widget.BaseAdapter;  import android.widget.ListView; -import android.widget.TextView;  import android.widget.AdapterView.OnItemClickListener;  public class SelectSecretKeyListActivity extends BaseActivity { -    protected Vector<PGPSecretKeyRing> mKeyRings; -    protected LayoutInflater mInflater; -    protected Intent mIntent;      protected ListView mList; +    protected SelectSecretKeyListAdapter mListAdapter;      protected long mSelectedKeyId = 0; @@ -49,158 +33,20 @@ public class SelectSecretKeyListActivity extends BaseActivity {      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState); -        mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); - -        // fill things -        mIntent = getIntent(); - -        mKeyRings = (Vector<PGPSecretKeyRing>) Apg.getSecretKeyRings().clone(); -        Collections.sort(mKeyRings, new Apg.SecretKeySorter()); -          setContentView(R.layout.select_secret_key);          mList = (ListView) findViewById(R.id.list); -        mList.setAdapter(new SecretKeyListAdapter(this)); +        mListAdapter = new SelectSecretKeyListAdapter(this, mList); +        mList.setAdapter(mListAdapter);          mList.setOnItemClickListener(new OnItemClickListener() {              @Override              public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {                  Intent data = new Intent(); -                data.putExtra("selectedKeyId", id); +                data.putExtra(Apg.EXTRA_KEY_ID, id);                  setResult(RESULT_OK, data);                  finish();              }          });      } - -    private class SecretKeyListAdapter extends BaseAdapter { - -        public SecretKeyListAdapter(Context context) { -        } - -        @Override -        public boolean isEnabled(int position) { -            PGPSecretKeyRing keyRing = mKeyRings.get(position); - -            if (Apg.getMasterKey(keyRing) == null) { -                return false; -            } - -            Vector<PGPSecretKey> usableKeys = Apg.getUsableSigningKeys(keyRing); -            if (usableKeys.size() == 0) { -                return false; -            } - -            return true; -        } - -        @Override -        public boolean hasStableIds() { -            return true; -        } - -        @Override -        public int getCount() { -            return mKeyRings.size(); -        } - -        @Override -        public Object getItem(int position) { -            return mKeyRings.get(position); -        } - -        @Override -        public long getItemId(int position) { -            PGPSecretKeyRing keyRing = mKeyRings.get(position); -            PGPSecretKey key = Apg.getMasterKey(keyRing); -            if (key != null) { -                return key.getKeyID(); -            } - -            return 0; -        } - -        @Override -        public View getView(int position, View convertView, ViewGroup parent) { -            View view = mInflater.inflate(R.layout.select_secret_key_item, null); -            boolean enabled = isEnabled(position); - -            PGPSecretKeyRing keyRing = mKeyRings.get(position); -            PGPSecretKey key = null; -            for (PGPSecretKey tKey : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) { -                if (tKey.isMasterKey()) { -                    key = tKey; -                    break; -                } -            } - -            TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); -            mainUserId.setText(R.string.unknownUserId); -            TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); -            mainUserIdRest.setText(""); -            TextView keyId = (TextView) view.findViewById(R.id.keyId); -            keyId.setText(R.string.noKey); -            TextView creation = (TextView) view.findViewById(R.id.creation); -            creation.setText(R.string.noDate); -            TextView expiry = (TextView) view.findViewById(R.id.expiry); -            expiry.setText(R.string.noExpiry); -            TextView status = (TextView) view.findViewById(R.id.status); -            status.setText(R.string.unknownStatus); - -            if (key != null) { -                String userId = Apg.getMainUserId(key); -                if (userId != null) { -                    String chunks[] = userId.split(" <", 2); -                    userId = chunks[0]; -                    if (chunks.length > 1) { -                        mainUserIdRest.setText("<" + chunks[1]); -                    } -                    mainUserId.setText(userId); -                } - -                keyId.setText("" + Long.toHexString(key.getKeyID() & 0xffffffffL)); -            } - -            if (mainUserIdRest.getText().length() == 0) { -                mainUserIdRest.setVisibility(View.GONE); -            } - -            Vector<PGPSecretKey> signingKeys = Apg.getSigningKeys(keyRing); -            Vector<PGPSecretKey> usableKeys = Apg.getUsableSigningKeys(keyRing); - -            PGPSecretKey timespanKey = key; -            if (usableKeys.size() > 0) { -                timespanKey = usableKeys.get(0); -                status.setText(R.string.canSign); -            } else if (signingKeys.size() > 0) { -                timespanKey = signingKeys.get(0); -                Date now = new Date(); -                if (now.compareTo(Apg.getCreationDate(timespanKey)) > 0) { -                    status.setText(R.string.notValid); -                } else { -                    status.setText(R.string.expired); -                } -            } else { -                status.setText(R.string.noKey); -            } - -            creation.setText(DateFormat.getDateInstance().format(Apg.getCreationDate(timespanKey))); -            Date expiryDate = Apg.getExpiryDate(timespanKey); -            if (expiryDate != null) { -                expiry.setText(DateFormat.getDateInstance().format(expiryDate)); -            } - -            status.setText(status.getText() + " "); - -            view.setEnabled(enabled); -            mainUserId.setEnabled(enabled); -            mainUserIdRest.setEnabled(enabled); -            keyId.setEnabled(enabled); -            creation.setEnabled(enabled); -            expiry.setEnabled(enabled); -            status.setEnabled(enabled); - -            return view; -        } -    } -}
\ No newline at end of file +} diff --git a/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java b/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java new file mode 100644 index 000000000..33cd15b40 --- /dev/null +++ b/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java @@ -0,0 +1,147 @@ +package org.thialfihar.android.apg;
 +
 +import java.util.Date;
 +
 +import org.thialfihar.android.apg.provider.KeyRings;
 +import org.thialfihar.android.apg.provider.Keys;
 +import org.thialfihar.android.apg.provider.UserIds;
 +
 +import android.app.Activity;
 +import android.content.Context;
 +import android.database.Cursor;
 +import android.database.sqlite.SQLiteDatabase;
 +import android.view.LayoutInflater;
 +import android.view.View;
 +import android.view.ViewGroup;
 +import android.widget.BaseAdapter;
 +import android.widget.ListView;
 +import android.widget.TextView;
 +
 +public class SelectSecretKeyListAdapter extends BaseAdapter {
 +    protected LayoutInflater mInflater;
 +    protected ListView mParent;
 +    protected SQLiteDatabase mDatabase;
 +    protected Cursor mCursor;
 +
 +    public SelectSecretKeyListAdapter(Activity activity, ListView parent) {
 +        mParent = parent;
 +        mDatabase =  Apg.getDatabase().db();
 +        mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 +        long now = new Date().getTime() / 1000;
 +        mCursor = mDatabase.query(
 +              KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
 +                                    "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
 +                                    Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
 +                                    Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
 +                                    ") " +
 +                                    " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
 +                                    "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
 +                                    UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
 +                                    UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ",
 +              new String[] {
 +                  KeyRings.TABLE_NAME + "." + KeyRings._ID,           // 0
 +                  KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
 +                  UserIds.TABLE_NAME + "." + UserIds.USER_ID,         // 2
 +                  "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
 +                      "tmp." + Keys.KEY_RING_ID + " = " +
 +                      KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
 +                      "tmp." + Keys.IS_REVOKED + " = '0' AND " +
 +                      "tmp." + Keys.CAN_SIGN + " = '1')",             // 3,
 +                  "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
 +                      "tmp." + Keys.KEY_RING_ID + " = " +
 +                      KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
 +                      "tmp." + Keys.IS_REVOKED + " = '0' AND " +
 +                      "tmp." + Keys.CAN_SIGN + " = '1' AND " +
 +                      "tmp." + Keys.CREATION + " <= '" + now + "' AND " +
 +                      "(tmp." + Keys.EXPIRY + " IS NULL OR " +
 +                       "tmp." + Keys.EXPIRY + " >= '" + now + "'))",  // 4
 +              },
 +              KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
 +              new String[] { "" + Id.database.type_secret },
 +              null, null, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC");
 +
 +        activity.startManagingCursor(mCursor);
 +    }
 +
 +    @Override
 +    public boolean isEnabled(int position) {
 +        mCursor.moveToPosition(position);
 +        return mCursor.getInt(4) > 0; // valid CAN_SIGN
 +    }
 +
 +    @Override
 +    public boolean hasStableIds() {
 +        return true;
 +    }
 +
 +    @Override
 +    public int getCount() {
 +        return mCursor.getCount();
 +    }
 +
 +    @Override
 +    public Object getItem(int position) {
 +        return position;
 +    }
 +
 +    @Override
 +    public long getItemId(int position) {
 +        mCursor.moveToPosition(position);
 +        return mCursor.getLong(1); // MASTER_KEY_ID
 +    }
 +
 +    @Override
 +    public View getView(int position, View convertView, ViewGroup parent) {
 +        mCursor.moveToPosition(position);
 +
 +        View view = mInflater.inflate(R.layout.select_secret_key_item, null);
 +        boolean enabled = isEnabled(position);
 +
 +        TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
 +        mainUserId.setText(R.string.unknownUserId);
 +        TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
 +        mainUserIdRest.setText("");
 +        TextView keyId = (TextView) view.findViewById(R.id.keyId);
 +        keyId.setText(R.string.noKey);
 +        TextView status = (TextView) view.findViewById(R.id.status);
 +        status.setText(R.string.unknownStatus);
 +
 +        String userId = mCursor.getString(2); // USER_ID
 +        if (userId != null) {
 +            String chunks[] = userId.split(" <", 2);
 +            userId = chunks[0];
 +            if (chunks.length > 1) {
 +                mainUserIdRest.setText("<" + chunks[1]);
 +            }
 +            mainUserId.setText(userId);
 +        }
 +
 +        long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
 +        keyId.setText("" + Long.toHexString(masterKeyId & 0xffffffffL));
 +
 +        if (mainUserIdRest.getText().length() == 0) {
 +            mainUserIdRest.setVisibility(View.GONE);
 +        }
 +
 +        if (enabled) {
 +            status.setText(R.string.canSign);
 +        } else {
 +            if (mCursor.getInt(3) > 0) {
 +                // has some CAN_SIGN keys, but col(4) = 0, so must be revoked or expired
 +                status.setText(R.string.expired);
 +            } else {
 +                status.setText(R.string.noKey);
 +            }
 +        }
 +
 +        status.setText(status.getText() + " ");
 +
 +        view.setEnabled(enabled);
 +        mainUserId.setEnabled(enabled);
 +        mainUserIdRest.setEnabled(enabled);
 +        keyId.setEnabled(enabled);
 +        status.setEnabled(enabled);
 +
 +        return view;
 +    }
 +}
\ No newline at end of file diff --git a/src/org/thialfihar/android/apg/Service.java b/src/org/thialfihar/android/apg/Service.java new file mode 100644 index 000000000..4457274ff --- /dev/null +++ b/src/org/thialfihar/android/apg/Service.java @@ -0,0 +1,81 @@ +package org.thialfihar.android.apg;
 +
 +import android.content.Intent;
 +import android.os.Binder;
 +import android.os.Handler;
 +import android.os.IBinder;
 +
 +public class Service extends android.app.Service {
 +    private final IBinder mBinder = new LocalBinder();
 +
 +    public static final String EXTRA_TTL = "ttl";
 +
 +    private int mPassPhraseCacheTtl = 15;
 +    private Handler mCacheHandler = new Handler();
 +    private Runnable mCacheTask = new Runnable() {
 +        public void run() {
 +            // TODO: I suppose we could read out the time left until the first cache entry
 +            // expiration, then use that for the timer...
 +
 +            // check every ttl/2 seconds, which shouldn't be heavy on the device (even if ttl = 15),
 +            // and makes sure the longest a pass phrase survives in the cache is 1.5 * ttl
 +            int delay = mPassPhraseCacheTtl * 1000 / 2;
 +            // also make sure the delay is not longer than one minute
 +            if (delay > 60000) {
 +                delay = 60000;
 +            }
 +
 +            delay = Apg.cleanUpCache(mPassPhraseCacheTtl, delay);
 +            // don't check too often, even if we were close
 +            if (delay < 5000) {
 +                delay = 5000;
 +            }
 +
 +            mCacheHandler.postDelayed(this, delay);
 +        }
 +    };
 +
 +    static private boolean mIsRunning = false;
 +
 +    @Override
 +    public void onCreate() {
 +        super.onCreate();
 +
 +        mIsRunning = true;
 +    }
 +
 +    @Override
 +    public void onDestroy() {
 +        super.onDestroy();
 +        mIsRunning = false;
 +    }
 +
 +    @Override
 +    public void onStart(Intent intent, int startId) {
 +        super.onStart(intent, startId);
 +
 +        if (intent != null) {
 +            mPassPhraseCacheTtl = intent.getIntExtra(EXTRA_TTL, 15);
 +        }
 +        if (mPassPhraseCacheTtl < 15) {
 +            mPassPhraseCacheTtl = 15;
 +        }
 +        mCacheHandler.removeCallbacks(mCacheTask);
 +        mCacheHandler.postDelayed(mCacheTask, 1000);
 +    }
 +
 +    static public boolean isRunning() {
 +        return mIsRunning;
 +    }
 +
 +    public class LocalBinder extends Binder {
 +        Service getService() {
 +            return Service.this;
 +        }
 +    }
 +
 +    @Override
 +    public IBinder onBind(Intent intent) {
 +        return mBinder;
 +    }
 +}
 diff --git a/src/org/thialfihar/android/apg/provider/Accounts.java b/src/org/thialfihar/android/apg/provider/Accounts.java index 4fce2b607..8162472ec 100644 --- a/src/org/thialfihar/android/apg/provider/Accounts.java +++ b/src/org/thialfihar/android/apg/provider/Accounts.java @@ -16,7 +16,12 @@  package org.thialfihar.android.apg.provider;
 -public class Accounts extends Accounts1 {
 -    private Accounts() {
 -    }
 -}
\ No newline at end of file +import android.provider.BaseColumns;
 +
 +public class Accounts implements BaseColumns {
 +    public static final String TABLE_NAME = "accounts";
 +
 +    public static final String _ID_type = "INTEGER PRIMARY KEY";
 +    public static final String NAME = "c_name";
 +    public static final String NAME_type = "TEXT";
 +}
 diff --git a/src/org/thialfihar/android/apg/provider/DataProvider.java b/src/org/thialfihar/android/apg/provider/DataProvider.java index fbc1be047..8a3fefdff 100644 --- a/src/org/thialfihar/android/apg/provider/DataProvider.java +++ b/src/org/thialfihar/android/apg/provider/DataProvider.java @@ -18,15 +18,12 @@ package org.thialfihar.android.apg.provider;  import java.util.HashMap; +import org.thialfihar.android.apg.Id; +  import android.content.ContentProvider; -import android.content.ContentUris;  import android.content.ContentValues; -import android.content.Context;  import android.content.UriMatcher;  import android.database.Cursor; -import android.database.SQLException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper;  import android.database.sqlite.SQLiteQueryBuilder;  import android.net.Uri;  import android.text.TextUtils; @@ -34,153 +31,190 @@ import android.text.TextUtils;  public class DataProvider extends ContentProvider {      public static final String AUTHORITY = "org.thialfihar.android.apg.provider"; -    private static final String DATABASE_NAME = "apg"; -    private static final int DATABASE_VERSION = 1; - -    private static final int PUBLIC_KEYS = 101; -    private static final int PUBLIC_KEY_ID = 102; -    private static final int PUBLIC_KEY_BY_KEY_ID = 103; - -    private static final int SECRET_KEYS = 201; -    private static final int SECRET_KEY_ID = 202; -    private static final int SECRET_KEY_BY_KEY_ID = 203; - -    private static final int ACCOUNTS = 301; -    private static final int ACCOUNT_ID = 302; +    private static final int PUBLIC_KEY_RINGS = 101; +    private static final int PUBLIC_KEY_RING_ID = 102; +    private static final int PUBLIC_KEY_RING_BY_KEY_ID = 103; +    private static final int PUBLIC_KEY_RING_KEYS = 111; +    private static final int PUBLIC_KEY_RING_KEY_RANK = 112; +    private static final int PUBLIC_KEY_RING_USER_IDS = 121; +    private static final int PUBLIC_KEY_RING_USER_ID_RANK = 122; + +    private static final int SECRET_KEY_RINGS = 201; +    private static final int SECRET_KEY_RING_ID = 202; +    private static final int SECRET_KEY_RING_BY_KEY_ID = 203; +    private static final int SECRET_KEY_RING_KEYS = 211; +    private static final int SECRET_KEY_RING_KEY_RANK = 212; +    private static final int SECRET_KEY_RING_USER_IDS = 221; +    private static final int SECRET_KEY_RING_USER_ID_RANK = 222; + +    private static final String PUBLIC_KEY_RING_CONTENT_DIR_TYPE = +            "vnd.android.cursor.dir/vnd.thialfihar.apg.public.key_ring"; +    private static final String PUBLIC_KEY_RING_CONTENT_ITEM_TYPE = +            "vnd.android.cursor.item/vnd.thialfihar.apg.public.key_ring"; + +    private static final String PUBLIC_KEY_CONTENT_DIR_TYPE = +            "vnd.android.cursor.dir/vnd.thialfihar.apg.public.key"; +    private static final String PUBLIC_KEY_CONTENT_ITEM_TYPE = +            "vnd.android.cursor.item/vnd.thialfihar.apg.public.key"; + +    private static final String SECRET_KEY_RING_CONTENT_DIR_TYPE = +            "vnd.android.cursor.dir/vnd.thialfihar.apg.secret.key_ring"; +    private static final String SECRET_KEY_RING_CONTENT_ITEM_TYPE = +            "vnd.android.cursor.item/vnd.thialfihar.apg.secret.key_ring"; + +    private static final String SECRET_KEY_CONTENT_DIR_TYPE = +            "vnd.android.cursor.dir/vnd.thialfihar.apg.secret.key"; +    private static final String SECRET_KEY_CONTENT_ITEM_TYPE = +            "vnd.android.cursor.item/vnd.thialfihar.apg.secret.key"; + +    private static final String USER_ID_CONTENT_DIR_TYPE = +            "vnd.android.cursor.dir/vnd.thialfihar.apg.user_id"; +    private static final String USER_ID_CONTENT_ITEM_TYPE = +            "vnd.android.cursor.item/vnd.thialfihar.apg.user_id"; + +    public static final String MASTER_KEY_ID = "master_key_id"; +    public static final String KEY_ID = "key_id"; +    public static final String USER_ID = "user_id";      private static final UriMatcher mUriMatcher; -    private static final HashMap<String, String> mPublicKeysProjectionMap; -    private static final HashMap<String, String> mSecretKeysProjectionMap; -    private static final HashMap<String, String> mAccountsProjectionMap; -    private DatabaseHelper mdbHelper; +    private Database mDb;      static {          mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); -        mUriMatcher.addURI(DataProvider.AUTHORITY, "public_keys", PUBLIC_KEYS); -        mUriMatcher.addURI(DataProvider.AUTHORITY, "public_keys/#", PUBLIC_KEY_ID); -        mUriMatcher.addURI(DataProvider.AUTHORITY, "public_keys/key_id/*", PUBLIC_KEY_BY_KEY_ID); - -        mUriMatcher.addURI(DataProvider.AUTHORITY, "secret_keys", SECRET_KEYS); -        mUriMatcher.addURI(DataProvider.AUTHORITY, "secret_keys/#", SECRET_KEY_ID); -        mUriMatcher.addURI(DataProvider.AUTHORITY, "secret_keys/key_id/*", SECRET_KEY_BY_KEY_ID); - -        mUriMatcher.addURI(DataProvider.AUTHORITY, "accounts", ACCOUNTS); -        mUriMatcher.addURI(DataProvider.AUTHORITY, "accounts/#", ACCOUNT_ID); - -        mPublicKeysProjectionMap = new HashMap<String, String>(); -        mPublicKeysProjectionMap.put(PublicKeys._ID, PublicKeys._ID); -        mPublicKeysProjectionMap.put(PublicKeys.KEY_ID, PublicKeys.KEY_ID); -        mPublicKeysProjectionMap.put(PublicKeys.KEY_DATA, PublicKeys.KEY_DATA); -        mPublicKeysProjectionMap.put(PublicKeys.WHO_ID, PublicKeys.WHO_ID); - -        mSecretKeysProjectionMap = new HashMap<String, String>(); -        mSecretKeysProjectionMap.put(PublicKeys._ID, PublicKeys._ID); -        mSecretKeysProjectionMap.put(PublicKeys.KEY_ID, PublicKeys.KEY_ID); -        mSecretKeysProjectionMap.put(PublicKeys.KEY_DATA, PublicKeys.KEY_DATA); -        mSecretKeysProjectionMap.put(PublicKeys.WHO_ID, PublicKeys.WHO_ID); - -        mAccountsProjectionMap = new HashMap<String, String>(); -        mAccountsProjectionMap.put(Accounts._ID, Accounts._ID); -        mAccountsProjectionMap.put(Accounts.NAME, Accounts.NAME); -    } +        mUriMatcher.addURI(AUTHORITY, "key_rings/public/key_id/*", PUBLIC_KEY_RING_BY_KEY_ID); -    /** -     * This class helps open, create, and upgrade the database file. -     */ -    private static class DatabaseHelper extends SQLiteOpenHelper { +        mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys", PUBLIC_KEY_RING_KEYS); +        mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys/#", PUBLIC_KEY_RING_KEY_RANK); -        DatabaseHelper(Context context) { -            super(context, DATABASE_NAME, null, DATABASE_VERSION); -        } +        mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/user_ids", PUBLIC_KEY_RING_USER_IDS); +        mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/user_ids/#", PUBLIC_KEY_RING_USER_ID_RANK); -        @Override -        public void onCreate(SQLiteDatabase db) { -            db.execSQL("CREATE TABLE " + PublicKeys.TABLE_NAME + " (" + -                       PublicKeys._ID + " " + PublicKeys._ID_type + "," + -                       PublicKeys.KEY_ID + " " + PublicKeys.KEY_ID_type + ", " + -                       PublicKeys.KEY_DATA + " " + PublicKeys.KEY_DATA_type + ", " + -                       PublicKeys.WHO_ID + " " + PublicKeys.WHO_ID_type + ");"); - -            db.execSQL("CREATE TABLE " + SecretKeys.TABLE_NAME + " (" + -                       SecretKeys._ID + " " + SecretKeys._ID_type + "," + -                       SecretKeys.KEY_ID + " " + SecretKeys.KEY_ID_type + ", " + -                       SecretKeys.KEY_DATA + " " + SecretKeys.KEY_DATA_type + ", " + -                       SecretKeys.WHO_ID + " " + SecretKeys.WHO_ID_type + ");"); - -            db.execSQL("CREATE TABLE " + Accounts.TABLE_NAME + " (" + -                       Accounts._ID + " " + Accounts._ID_type + "," + -                       Accounts.NAME + " " + Accounts.NAME_type + ");"); -        } +        mUriMatcher.addURI(AUTHORITY, "key_rings/public", PUBLIC_KEY_RINGS); +        mUriMatcher.addURI(AUTHORITY, "key_rings/public/*", PUBLIC_KEY_RING_ID); -        @Override -        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { -            // TODO: upgrade db if necessary, and do that in a clever way -        } +        mUriMatcher.addURI(AUTHORITY, "key_rings/secret/key_id/*", SECRET_KEY_RING_BY_KEY_ID); + +        mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys", SECRET_KEY_RING_KEYS); +        mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys/#", SECRET_KEY_RING_KEY_RANK); + +        mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/user_ids", SECRET_KEY_RING_USER_IDS); +        mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/user_ids/#", SECRET_KEY_RING_USER_ID_RANK); + +        mUriMatcher.addURI(AUTHORITY, "key_rings/secret", SECRET_KEY_RINGS); +        mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*", SECRET_KEY_RING_ID);      }      @Override      public boolean onCreate() { -        mdbHelper = new DatabaseHelper(getContext()); +        mDb = new Database(getContext());          return true;      }      @Override      public Cursor query(Uri uri, String[] projection, String selection,                          String[] selectionArgs, String sortOrder) { +        // TODO: implement the others, then use them for the lists          SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - -        switch (mUriMatcher.match(uri)) { -            case PUBLIC_KEYS: { -                qb.setTables(PublicKeys.TABLE_NAME); -                qb.setProjectionMap(mPublicKeysProjectionMap); +        HashMap<String, String> projectionMap = new HashMap<String, String>(); +        int match = mUriMatcher.match(uri); +        int type; +        switch (match) { +            case PUBLIC_KEY_RINGS: +            case PUBLIC_KEY_RING_ID: +            case PUBLIC_KEY_RING_BY_KEY_ID: +            case PUBLIC_KEY_RING_KEYS: +            case PUBLIC_KEY_RING_KEY_RANK: +            case PUBLIC_KEY_RING_USER_IDS: +            case PUBLIC_KEY_RING_USER_ID_RANK: +                type = Id.database.type_public;                  break; -            } -            case PUBLIC_KEY_ID: { -                qb.setTables(PublicKeys.TABLE_NAME); -                qb.setProjectionMap(mPublicKeysProjectionMap); -                qb.appendWhere(PublicKeys._ID + "=" + uri.getPathSegments().get(1)); +            case SECRET_KEY_RINGS: +            case SECRET_KEY_RING_ID: +            case SECRET_KEY_RING_BY_KEY_ID: +            case SECRET_KEY_RING_KEYS: +            case SECRET_KEY_RING_KEY_RANK: +            case SECRET_KEY_RING_USER_IDS: +            case SECRET_KEY_RING_USER_ID_RANK: +                type = Id.database.type_secret;                  break; -            } -            case PUBLIC_KEY_BY_KEY_ID: { -                qb.setTables(PublicKeys.TABLE_NAME); -                qb.setProjectionMap(mPublicKeysProjectionMap); -                qb.appendWhere(PublicKeys.KEY_ID + "=" + uri.getPathSegments().get(2)); -                break; +            default: { +                throw new IllegalArgumentException("Unknown URI " + uri);              } +        } -            case SECRET_KEYS: { -                qb.setTables(SecretKeys.TABLE_NAME); -                qb.setProjectionMap(mSecretKeysProjectionMap); -                break; -            } +        qb.appendWhere(KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = " + type); + +        switch (match) { +            case PUBLIC_KEY_RINGS: +            case SECRET_KEY_RINGS: { +                qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + +                             "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + +                             Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " + +                             Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" + +                             ") " + +                             " INNER JOIN " + UserIds.TABLE_NAME + " ON " + +                             "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " + +                             UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " + +                             UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') "); + +                projectionMap.put(MASTER_KEY_ID, +                                  KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID); +                projectionMap.put(USER_ID, +                                  UserIds.TABLE_NAME + "." + UserIds.USER_ID); -            case SECRET_KEY_ID: { -                qb.setTables(SecretKeys.TABLE_NAME); -                qb.setProjectionMap(mSecretKeysProjectionMap); -                qb.appendWhere(SecretKeys._ID + "=" + uri.getPathSegments().get(1));                  break;              } -            case SECRET_KEY_BY_KEY_ID: { -                qb.setTables(SecretKeys.TABLE_NAME); -                qb.setProjectionMap(mSecretKeysProjectionMap); -                qb.appendWhere(SecretKeys.KEY_ID + "=" + uri.getPathSegments().get(2)); +            case PUBLIC_KEY_RING_ID: +            case SECRET_KEY_RING_ID: { +                qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + +                             "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + +                             Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " + +                             Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" + +                             ") " + +                             " INNER JOIN " + UserIds.TABLE_NAME + " ON " + +                             "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " + +                             UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " + +                             UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') "); + +                projectionMap.put(MASTER_KEY_ID, +                                  KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID); +                projectionMap.put(USER_ID, +                                  UserIds.TABLE_NAME + "." + UserIds.USER_ID); + +                qb.appendWhere(" AND " + +                               KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID + " = "); +                qb.appendWhereEscapeString(uri.getPathSegments().get(2));                  break;              } -            case ACCOUNTS: { -                qb.setTables(Accounts.TABLE_NAME); -                qb.setProjectionMap(mAccountsProjectionMap); -                break; -            } +            case SECRET_KEY_RING_BY_KEY_ID: +            case PUBLIC_KEY_RING_BY_KEY_ID: { +                qb.setTables(Keys.TABLE_NAME + " AS tmp INNER JOIN " + +                             KeyRings.TABLE_NAME + " ON (" + +                             KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + +                             "tmp." + Keys.KEY_RING_ID + ")" + +                             " INNER JOIN " + Keys.TABLE_NAME + " ON " + +                             "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + +                             Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " + +                             Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" + +                             ") " + +                             " INNER JOIN " + UserIds.TABLE_NAME + " ON " + +                             "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " + +                             UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " + +                             UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') "); + +                projectionMap.put(MASTER_KEY_ID, +                                  KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID); +                projectionMap.put(USER_ID, +                                  UserIds.TABLE_NAME + "." + UserIds.USER_ID); + +                qb.appendWhere(" AND tmp." + Keys.KEY_ID + " = "); +                qb.appendWhereEscapeString(uri.getPathSegments().get(3)); -            case ACCOUNT_ID: { -                qb.setTables(Accounts.TABLE_NAME); -                qb.setProjectionMap(mAccountsProjectionMap); -                qb.appendWhere(Accounts._ID + "=" + uri.getPathSegments().get(1));                  break;              } @@ -189,20 +223,20 @@ public class DataProvider extends ContentProvider {              }          } +        qb.setProjectionMap(projectionMap); +          // If no sort order is specified use the default          String orderBy;          if (TextUtils.isEmpty(sortOrder)) { -            orderBy = PublicKeys.DEFAULT_SORT_ORDER; +            orderBy = null;          } else {              orderBy = sortOrder;          } -        // Get the database and run the query -        SQLiteDatabase db = mdbHelper.getReadableDatabase(); -        Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); +        //System.out.println(qb.buildQuery(projection, selection, selectionArgs, null, null, sortOrder, null).replace("WHERE", "WHERE\n")); +        Cursor c = qb.query(mDb.db(), projection, selection, selectionArgs, null, null, orderBy); -        // Tell the cursor what uri to watch, so it knows when its source data -        // changes +        // Tell the cursor what uri to watch, so it knows when its source data changes          c.setNotificationUri(getContext().getContentResolver(), uri);          return c;      } @@ -210,278 +244,68 @@ public class DataProvider extends ContentProvider {      @Override      public String getType(Uri uri) {          switch (mUriMatcher.match(uri)) { -            case PUBLIC_KEYS: { -                return PublicKeys.CONTENT_TYPE; -            } +            case PUBLIC_KEY_RINGS: +                return PUBLIC_KEY_RING_CONTENT_DIR_TYPE; -            case PUBLIC_KEY_ID: { -                return PublicKeys.CONTENT_ITEM_TYPE; -            } +            case PUBLIC_KEY_RING_ID: +                return PUBLIC_KEY_RING_CONTENT_ITEM_TYPE; -            case PUBLIC_KEY_BY_KEY_ID: { -                return PublicKeys.CONTENT_ITEM_TYPE; -            } +            case PUBLIC_KEY_RING_BY_KEY_ID: +                return PUBLIC_KEY_RING_CONTENT_ITEM_TYPE; -            case SECRET_KEYS: { -                return SecretKeys.CONTENT_TYPE; -            } +            case PUBLIC_KEY_RING_KEYS: +                return PUBLIC_KEY_CONTENT_DIR_TYPE; -            case SECRET_KEY_ID: { -                return SecretKeys.CONTENT_ITEM_TYPE; -            } +            case PUBLIC_KEY_RING_KEY_RANK: +                return PUBLIC_KEY_CONTENT_ITEM_TYPE; -            case SECRET_KEY_BY_KEY_ID: { -                return SecretKeys.CONTENT_ITEM_TYPE; -            } +            case PUBLIC_KEY_RING_USER_IDS: +                return USER_ID_CONTENT_DIR_TYPE; -            case ACCOUNTS: { -                return Accounts.CONTENT_TYPE; -            } +            case PUBLIC_KEY_RING_USER_ID_RANK: +                return USER_ID_CONTENT_ITEM_TYPE; -            case ACCOUNT_ID: { -                return Accounts.CONTENT_ITEM_TYPE; -            } +            case SECRET_KEY_RINGS: +                return SECRET_KEY_RING_CONTENT_DIR_TYPE; -            default: { -                throw new IllegalArgumentException("Unknown URI " + uri); -            } -        } -    } +            case SECRET_KEY_RING_ID: +                return SECRET_KEY_RING_CONTENT_ITEM_TYPE; -    @Override -    public Uri insert(Uri uri, ContentValues initialValues) { -        switch (mUriMatcher.match(uri)) { -            case PUBLIC_KEYS: { -                ContentValues values; -                if (initialValues != null) { -                    values = new ContentValues(initialValues); -                } else { -                    values = new ContentValues(); -                } - -                if (!values.containsKey(PublicKeys.WHO_ID)) { -                    values.put(PublicKeys.WHO_ID, ""); -                } - -                SQLiteDatabase db = mdbHelper.getWritableDatabase(); -                long rowId = db.insert(PublicKeys.TABLE_NAME, PublicKeys.WHO_ID, values); -                if (rowId > 0) { -                    Uri transferUri = ContentUris.withAppendedId(PublicKeys.CONTENT_URI, rowId); -                    getContext().getContentResolver().notifyChange(transferUri, null); -                    return transferUri; -                } - -                throw new SQLException("Failed to insert row into " + uri); -            } +            case SECRET_KEY_RING_BY_KEY_ID: +                return SECRET_KEY_RING_CONTENT_ITEM_TYPE; -            case SECRET_KEYS: { -                ContentValues values; -                if (initialValues != null) { -                    values = new ContentValues(initialValues); -                } else { -                    values = new ContentValues(); -                } - -                if (!values.containsKey(SecretKeys.WHO_ID)) { -                    values.put(SecretKeys.WHO_ID, ""); -                } - -                SQLiteDatabase db = mdbHelper.getWritableDatabase(); -                long rowId = db.insert(SecretKeys.TABLE_NAME, SecretKeys.WHO_ID, values); -                if (rowId > 0) { -                    Uri transferUri = ContentUris.withAppendedId(SecretKeys.CONTENT_URI, rowId); -                    getContext().getContentResolver().notifyChange(transferUri, null); -                    return transferUri; -                } - -                throw new SQLException("Failed to insert row into " + uri); -            } +            case SECRET_KEY_RING_KEYS: +                return SECRET_KEY_CONTENT_DIR_TYPE; -            case ACCOUNTS: { -                ContentValues values; -                if (initialValues != null) { -                    values = new ContentValues(initialValues); -                } else { -                    values = new ContentValues(); -                } - -                SQLiteDatabase db = mdbHelper.getWritableDatabase(); -                long rowId = db.insert(Accounts.TABLE_NAME, null, values); -                if (rowId > 0) { -                    Uri transferUri = ContentUris.withAppendedId(Accounts.CONTENT_URI, rowId); -                    getContext().getContentResolver().notifyChange(transferUri, null); -                    return transferUri; -                } - -                throw new SQLException("Failed to insert row into " + uri); -            } +            case SECRET_KEY_RING_KEY_RANK: +                return SECRET_KEY_CONTENT_ITEM_TYPE; -            default: { +            case SECRET_KEY_RING_USER_IDS: +                return USER_ID_CONTENT_DIR_TYPE; + +            case SECRET_KEY_RING_USER_ID_RANK: +                return USER_ID_CONTENT_ITEM_TYPE; + +            default:                  throw new IllegalArgumentException("Unknown URI " + uri); -            }          }      }      @Override -    public int delete(Uri uri, String where, String[] whereArgs) { -        SQLiteDatabase db = mdbHelper.getWritableDatabase(); -        int count; -        switch (mUriMatcher.match(uri)) { -            case PUBLIC_KEYS: { -                count = db.delete(PublicKeys.TABLE_NAME, where, whereArgs); -                break; -            } - -            case PUBLIC_KEY_ID: { -                String publicKeyId = uri.getPathSegments().get(1); -                count = db.delete(PublicKeys.TABLE_NAME, -                                  PublicKeys._ID + "=" + publicKeyId + -                                          (!TextUtils.isEmpty(where) ? -                                                   " AND (" + where + ')' : ""), -                                  whereArgs); -                break; -            } - -            case PUBLIC_KEY_BY_KEY_ID: { -                String publicKeyKeyId = uri.getPathSegments().get(2); -                count = db.delete(PublicKeys.TABLE_NAME, -                                  PublicKeys.KEY_ID + "=" + publicKeyKeyId + -                                          (!TextUtils.isEmpty(where) ? -                                                   " AND (" + where + ')' : ""), -                                  whereArgs); -                break; -            } - -            case SECRET_KEYS: { -                count = db.delete(SecretKeys.TABLE_NAME, where, whereArgs); -                break; -            } - -            case SECRET_KEY_ID: { -                String secretKeyId = uri.getPathSegments().get(1); -                count = db.delete(SecretKeys.TABLE_NAME, -                                  SecretKeys._ID + "=" + secretKeyId + -                                          (!TextUtils.isEmpty(where) ? -                                                   " AND (" + where + ')' : ""), -                                  whereArgs); -                break; -            } - -            case SECRET_KEY_BY_KEY_ID: { -                String secretKeyKeyId = uri.getPathSegments().get(2); -                count = db.delete(SecretKeys.TABLE_NAME, -                                  SecretKeys.KEY_ID + "=" + secretKeyKeyId + -                                          (!TextUtils.isEmpty(where) ? -                                                   " AND (" + where + ')' : ""), -                                  whereArgs); -                break; -            } - -            case ACCOUNTS: { -                count = db.delete(Accounts.TABLE_NAME, where, whereArgs); -                break; -            } - -            case ACCOUNT_ID: { -                String accountId = uri.getPathSegments().get(1); -                count = db.delete(Accounts.TABLE_NAME, -                                  Accounts._ID + "=" + accountId + -                                          (!TextUtils.isEmpty(where) ? -                                                   " AND (" + where + ')' : ""), -                                  whereArgs); -                break; -            } - -            default: { -                throw new IllegalArgumentException("Unknown URI " + uri); -            } -        } +    public Uri insert(Uri uri, ContentValues initialValues) { +        // not supported +        return null; +    } -        getContext().getContentResolver().notifyChange(uri, null); -        return count; +    @Override +    public int delete(Uri uri, String where, String[] whereArgs) { +        // not supported +        return 0;      }      @Override      public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { -        SQLiteDatabase db = mdbHelper.getWritableDatabase(); -        int count; -        switch (mUriMatcher.match(uri)) { -            case PUBLIC_KEYS: { -                count = db.update(PublicKeys.TABLE_NAME, values, where, whereArgs); -                break; -            } - -            case PUBLIC_KEY_ID: { -                String publicKeyId = uri.getPathSegments().get(1); - -                count = db.update(PublicKeys.TABLE_NAME, values, -                                  PublicKeys._ID + "=" + publicKeyId + -                                          (!TextUtils.isEmpty(where) ? -                                                   " AND (" + where + ')' : ""), -                                  whereArgs); -                break; -            } - -            case PUBLIC_KEY_BY_KEY_ID: { -                String publicKeyKeyId = uri.getPathSegments().get(2); - -                count = db.update(PublicKeys.TABLE_NAME, values, -                                  PublicKeys.KEY_ID + "=" + publicKeyKeyId + -                                          (!TextUtils.isEmpty(where) ? -                                                   " AND (" + where + ')' : ""), -                                  whereArgs); -                break; -            } - -            case SECRET_KEYS: { -                count = db.update(SecretKeys.TABLE_NAME, values, where, whereArgs); -                break; -            } - -            case SECRET_KEY_ID: { -                String secretKeyId = uri.getPathSegments().get(1); - -                count = db.update(SecretKeys.TABLE_NAME, values, -                                  SecretKeys._ID + "=" + secretKeyId + -                                          (!TextUtils.isEmpty(where) ? -                                                   " AND (" + where + ')' : ""), -                                  whereArgs); -                break; -            } - -            case SECRET_KEY_BY_KEY_ID: { -                String secretKeyKeyId = uri.getPathSegments().get(2); - -                count = db.update(SecretKeys.TABLE_NAME, values, -                                  SecretKeys.KEY_ID + "=" + secretKeyKeyId + -                                          (!TextUtils.isEmpty(where) ? -                                                   " AND (" + where + ')' : ""), -                                  whereArgs); -                break; -            } - -            case ACCOUNTS: { -                count = db.update(Accounts.TABLE_NAME, values, where, whereArgs); -                break; -            } - -            case ACCOUNT_ID: { -                String accountId = uri.getPathSegments().get(1); - -                count = db.update(Accounts.TABLE_NAME, values, -                                  Accounts._ID + "=" + accountId + -                                          (!TextUtils.isEmpty(where) ? -                                                   " AND (" + where + ')' : ""), -                                  whereArgs); -                break; -            } - -            default: { -                throw new IllegalArgumentException("Unknown URI " + uri); -            } -        } - -        getContext().getContentResolver().notifyChange(uri, null); -        return count; +        // not supported +        return 0;      }  } diff --git a/src/org/thialfihar/android/apg/provider/Database.java b/src/org/thialfihar/android/apg/provider/Database.java new file mode 100644 index 000000000..810ebebbf --- /dev/null +++ b/src/org/thialfihar/android/apg/provider/Database.java @@ -0,0 +1,605 @@ +package org.thialfihar.android.apg.provider;
 +
 +import java.io.IOException;
 +import java.util.Date;
 +import java.util.HashMap;
 +import java.util.Vector;
 +
 +import org.bouncycastle2.openpgp.PGPException;
 +import org.bouncycastle2.openpgp.PGPPublicKey;
 +import org.bouncycastle2.openpgp.PGPPublicKeyRing;
 +import org.bouncycastle2.openpgp.PGPSecretKey;
 +import org.bouncycastle2.openpgp.PGPSecretKeyRing;
 +import org.thialfihar.android.apg.Apg;
 +import org.thialfihar.android.apg.Id;
 +import org.thialfihar.android.apg.utils.IterableIterator;
 +
 +import android.content.ContentValues;
 +import android.content.Context;
 +import android.database.Cursor;
 +import android.database.sqlite.SQLiteDatabase;
 +import android.database.sqlite.SQLiteOpenHelper;
 +import android.util.Log;
 +
 +public class Database extends SQLiteOpenHelper {
 +    public static class GeneralException extends Exception {
 +        static final long serialVersionUID = 0xf812773343L;
 +
 +        public GeneralException(String message) {
 +            super(message);
 +        }
 +    }
 +
 +    private static final String DATABASE_NAME = "apg";
 +    private static final int DATABASE_VERSION = 2;
 +
 +    public static final String AUTHORITY = "org.thialfihar.android.apg.database";
 +
 +    public static HashMap<String, String> sKeyRingsProjection;
 +    public static HashMap<String, String> sKeysProjection;
 +    public static HashMap<String, String> sUserIdsProjection;
 +
 +    private SQLiteDatabase mDb = null;
 +    private int mStatus = 0;
 +
 +    static {
 +        sKeyRingsProjection = new HashMap<String, String>();
 +        sKeyRingsProjection.put(KeyRings._ID, KeyRings._ID);
 +        sKeyRingsProjection.put(KeyRings.MASTER_KEY_ID, KeyRings.MASTER_KEY_ID);
 +        sKeyRingsProjection.put(KeyRings.TYPE, KeyRings.TYPE);
 +        sKeyRingsProjection.put(KeyRings.WHO_ID, KeyRings.WHO_ID);
 +        sKeyRingsProjection.put(KeyRings.KEY_RING_DATA, KeyRings.KEY_RING_DATA);
 +
 +        sKeysProjection = new HashMap<String, String>();
 +        sKeysProjection.put(Keys._ID, Keys._ID);
 +        sKeysProjection.put(Keys.KEY_ID, Keys.KEY_ID);
 +        sKeysProjection.put(Keys.TYPE, Keys.TYPE);
 +        sKeysProjection.put(Keys.IS_MASTER_KEY, Keys.IS_MASTER_KEY);
 +        sKeysProjection.put(Keys.ALGORITHM, Keys.ALGORITHM);
 +        sKeysProjection.put(Keys.KEY_SIZE, Keys.KEY_SIZE);
 +        sKeysProjection.put(Keys.CAN_SIGN, Keys.CAN_SIGN);
 +        sKeysProjection.put(Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT);
 +        sKeysProjection.put(Keys.IS_REVOKED, Keys.IS_REVOKED);
 +        sKeysProjection.put(Keys.CREATION, Keys.CREATION);
 +        sKeysProjection.put(Keys.EXPIRY, Keys.EXPIRY);
 +        sKeysProjection.put(Keys.KEY_DATA, Keys.KEY_DATA);
 +        sKeysProjection.put(Keys.RANK, Keys.RANK);
 +
 +        sUserIdsProjection = new HashMap<String, String>();
 +        sUserIdsProjection.put(UserIds._ID, UserIds._ID);
 +        sUserIdsProjection.put(UserIds.KEY_ID, UserIds.KEY_ID);
 +        sUserIdsProjection.put(UserIds.USER_ID, UserIds.USER_ID);
 +        sUserIdsProjection.put(UserIds.RANK, UserIds.RANK);
 +    }
 +
 +    public Database(Context context) {
 +        super(context, DATABASE_NAME, null, DATABASE_VERSION);
 +        // force upgrade to test things
 +        //onUpgrade(getWritableDatabase(), 1, 2);
 +        mDb = getWritableDatabase();
 +    }
 +
 +    @Override
 +    protected void finalize() throws Throwable {
 +        mDb.close();
 +        super.finalize();
 +    }
 +
 +    @Override
 +    public void onCreate(SQLiteDatabase db) {
 +        db.execSQL("CREATE TABLE " + KeyRings.TABLE_NAME + " (" +
 +                   KeyRings._ID + " " + KeyRings._ID_type + "," +
 +                   KeyRings.MASTER_KEY_ID + " " + KeyRings.MASTER_KEY_ID_type + ", " +
 +                   KeyRings.TYPE + " " + KeyRings.TYPE_type + ", " +
 +                   KeyRings.WHO_ID + " " + KeyRings.WHO_ID_type + ", " +
 +                   KeyRings.KEY_RING_DATA + " " + KeyRings.KEY_RING_DATA_type + ");");
 +
 +        db.execSQL("CREATE TABLE " + Keys.TABLE_NAME + " (" +
 +                   Keys._ID + " " + Keys._ID_type + "," +
 +                   Keys.KEY_ID + " " + Keys.KEY_ID_type + ", " +
 +                   Keys.TYPE + " " + Keys.TYPE_type + ", " +
 +                   Keys.IS_MASTER_KEY + " " + Keys.IS_MASTER_KEY_type + ", " +
 +                   Keys.ALGORITHM + " " + Keys.ALGORITHM_type + ", " +
 +                   Keys.KEY_SIZE + " " + Keys.KEY_SIZE_type + ", " +
 +                   Keys.CAN_SIGN + " " + Keys.CAN_SIGN_type + ", " +
 +                   Keys.CAN_ENCRYPT + " " + Keys.CAN_ENCRYPT_type + ", " +
 +                   Keys.IS_REVOKED + " " + Keys.IS_REVOKED_type + ", " +
 +                   Keys.CREATION + " " + Keys.CREATION_type + ", " +
 +                   Keys.EXPIRY + " " + Keys.EXPIRY_type + ", " +
 +                   Keys.KEY_RING_ID + " " + Keys.KEY_RING_ID_type + ", " +
 +                   Keys.KEY_DATA + " " + Keys.KEY_DATA_type +
 +                   Keys.RANK + " " + Keys.RANK_type + ");");
 +
 +        db.execSQL("CREATE TABLE " + UserIds.TABLE_NAME + " (" +
 +                   UserIds._ID + " " + UserIds._ID_type + "," +
 +                   UserIds.KEY_ID + " " + UserIds.KEY_ID_type + "," +
 +                   UserIds.USER_ID + " " + UserIds.USER_ID_type + "," +
 +                   UserIds.RANK + " " + UserIds.RANK_type + ");");
 +
 +        db.execSQL("CREATE TABLE " + Accounts.TABLE_NAME + " (" +
 +                   Accounts._ID + " " + Accounts._ID_type + "," +
 +                   Accounts.NAME + " " + Accounts.NAME_type + ");");
 +    }
 +
 +    @Override
 +    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
 +        mDb = db;
 +        for (int version = oldVersion; version < newVersion; ++version) {
 +            switch (version) {
 +                case 1: { // upgrade 1 to 2
 +                    db.execSQL("DROP TABLE IF EXISTS " + KeyRings.TABLE_NAME + ";");
 +                    db.execSQL("DROP TABLE IF EXISTS " + Keys.TABLE_NAME + ";");
 +                    db.execSQL("DROP TABLE IF EXISTS " + UserIds.TABLE_NAME + ";");
 +
 +                    db.execSQL("CREATE TABLE " + KeyRings.TABLE_NAME + " (" +
 +                               KeyRings._ID + " " + KeyRings._ID_type + "," +
 +                               KeyRings.MASTER_KEY_ID + " " + KeyRings.MASTER_KEY_ID_type + ", " +
 +                               KeyRings.TYPE + " " + KeyRings.TYPE_type + ", " +
 +                               KeyRings.WHO_ID + " " + KeyRings.WHO_ID_type + ", " +
 +                               KeyRings.KEY_RING_DATA + " " + KeyRings.KEY_RING_DATA_type + ");");
 +
 +                    db.execSQL("CREATE TABLE " + Keys.TABLE_NAME + " (" +
 +                               Keys._ID + " " + Keys._ID_type + "," +
 +                               Keys.KEY_ID + " " + Keys.KEY_ID_type + ", " +
 +                               Keys.TYPE + " " + Keys.TYPE_type + ", " +
 +                               Keys.IS_MASTER_KEY + " " + Keys.IS_MASTER_KEY_type + ", " +
 +                               Keys.ALGORITHM + " " + Keys.ALGORITHM_type + ", " +
 +                               Keys.KEY_SIZE + " " + Keys.KEY_SIZE_type + ", " +
 +                               Keys.CAN_SIGN + " " + Keys.CAN_SIGN_type + ", " +
 +                               Keys.CAN_ENCRYPT + " " + Keys.CAN_ENCRYPT_type + ", " +
 +                               Keys.IS_REVOKED + " " + Keys.IS_REVOKED_type + ", " +
 +                               Keys.CREATION + " " + Keys.CREATION_type + ", " +
 +                               Keys.EXPIRY + " " + Keys.EXPIRY_type + ", " +
 +                               Keys.KEY_RING_ID + " " + Keys.KEY_RING_ID_type + ", " +
 +                               Keys.KEY_DATA + " " + Keys.KEY_DATA_type +
 +                               Keys.RANK + " " + Keys.RANK_type + ");");
 +
 +                    db.execSQL("CREATE TABLE " + UserIds.TABLE_NAME + " (" +
 +                               UserIds._ID + " " + UserIds._ID_type + "," +
 +                               UserIds.KEY_ID + " " + UserIds.KEY_ID_type + "," +
 +                               UserIds.USER_ID + " " + UserIds.USER_ID_type + "," +
 +                               UserIds.RANK + " " + UserIds.RANK_type + ");");
 +
 +                    Cursor cursor = db.query("public_keys", new String[] { "c_key_data" },
 +                                             null, null, null, null, null);
 +                    if (cursor != null && cursor.moveToFirst()) {
 +                        do {
 +                            byte[] data = cursor.getBlob(0);
 +                            try {
 +                                PGPPublicKeyRing keyRing = new PGPPublicKeyRing(data);
 +                                saveKeyRing(keyRing);
 +                            } catch (IOException e) {
 +                                Log.e("apg.db.upgrade", "key import failed: " + e);
 +                            } catch (GeneralException e) {
 +                                Log.e("apg.db.upgrade", "key import failed: " + e);
 +                            }
 +                        } while (cursor.moveToNext());
 +                    }
 +
 +                    if (cursor != null) {
 +                        cursor.close();
 +                    }
 +
 +                    cursor = db.query("secret_keys", new String[]{ "c_key_data" },
 +                                      null, null, null, null, null);
 +                    if (cursor != null && cursor.moveToFirst()) {
 +                        do {
 +                            byte[] data = cursor.getBlob(0);
 +                            try {
 +                                PGPSecretKeyRing keyRing = new PGPSecretKeyRing(data);
 +                                saveKeyRing(keyRing);
 +                            } catch (IOException e) {
 +                                Log.e("apg.db.upgrade", "key import failed: " + e);
 +                            } catch (PGPException e) {
 +                                Log.e("apg.db.upgrade", "key import failed: " + e);
 +                            } catch (GeneralException e) {
 +                                Log.e("apg.db.upgrade", "key import failed: " + e);
 +                            }
 +                        } while (cursor.moveToNext());
 +                    }
 +
 +                    if (cursor != null) {
 +                        cursor.close();
 +                    }
 +
 +                    db.execSQL("DROP TABLE IF EXISTS public_keys;");
 +                    db.execSQL("DROP TABLE IF EXISTS secret_keys;");
 +
 +                    break;
 +                }
 +
 +                default: {
 +                    break;
 +                }
 +            }
 +        }
 +        mDb = null;
 +    }
 +
 +    public int saveKeyRing(PGPPublicKeyRing keyRing) throws IOException, GeneralException {
 +        mDb.beginTransaction();
 +        ContentValues values = new ContentValues();
 +        PGPPublicKey masterKey = keyRing.getPublicKey();
 +        long masterKeyId = masterKey.getKeyID();
 +
 +        values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
 +        values.put(KeyRings.TYPE, Id.database.type_public);
 +        values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
 +
 +        long rowId = insertOrUpdateKeyRing(values);
 +        int returnValue = mStatus;
 +
 +        if (rowId == -1) {
 +            throw new GeneralException("saving public key ring " + masterKeyId + " failed");
 +        }
 +
 +        Vector<Integer> seenIds = new Vector<Integer>();
 +        int rank = 0;
 +        for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
 +            seenIds.add(saveKey(rowId, key, rank));
 +            ++rank;
 +        }
 +
 +        String seenIdsStr = "";
 +        for (Integer id : seenIds) {
 +            if (seenIdsStr.length() > 0) {
 +                seenIdsStr += ",";
 +            }
 +            seenIdsStr += id;
 +        }
 +        mDb.delete(Keys.TABLE_NAME,
 +                   Keys.KEY_RING_ID + " = ? AND " +
 +                   Keys._ID + " NOT IN (" + seenIdsStr + ")",
 +                   new String[] { "" + rowId });
 +
 +        mDb.setTransactionSuccessful();
 +        mDb.endTransaction();
 +        return returnValue;
 +    }
 +
 +    public int saveKeyRing(PGPSecretKeyRing keyRing) throws IOException, GeneralException {
 +        mDb.beginTransaction();
 +        ContentValues values = new ContentValues();
 +        PGPSecretKey masterKey = keyRing.getSecretKey();
 +        long masterKeyId = masterKey.getKeyID();
 +
 +        values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
 +        values.put(KeyRings.TYPE, Id.database.type_secret);
 +        values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
 +
 +        long rowId = insertOrUpdateKeyRing(values);
 +        int returnValue = mStatus;
 +
 +        if (rowId == -1) {
 +            throw new GeneralException("saving secret key ring " + masterKeyId + " failed");
 +        }
 +
 +        Vector<Integer> seenIds = new Vector<Integer>();
 +        int rank = 0;
 +        for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
 +            seenIds.add(saveKey(rowId, key, rank));
 +            ++rank;
 +        }
 +
 +        String seenIdsStr = "";
 +        for (Integer id : seenIds) {
 +            if (seenIdsStr.length() > 0) {
 +                seenIdsStr += ",";
 +            }
 +            seenIdsStr += id;
 +        }
 +        mDb.delete(Keys.TABLE_NAME,
 +                   Keys.KEY_RING_ID + " = ? AND " +
 +                   Keys._ID + " NOT IN (" + seenIdsStr + ")",
 +                   new String[] { "" + rowId });
 +
 +        mDb.setTransactionSuccessful();
 +        mDb.endTransaction();
 +        return returnValue;
 +    }
 +
 +    private int saveKey(long keyRingId, PGPPublicKey key, int rank)
 +            throws IOException, GeneralException {
 +        ContentValues values = new ContentValues();
 +
 +        values.put(Keys.KEY_ID, key.getKeyID());
 +        values.put(Keys.TYPE, Id.database.type_public);
 +        values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
 +        values.put(Keys.ALGORITHM, key.getAlgorithm());
 +        values.put(Keys.KEY_SIZE, key.getBitStrength());
 +        values.put(Keys.CAN_SIGN, Apg.isSigningKey(key));
 +        values.put(Keys.CAN_ENCRYPT, Apg.isEncryptionKey(key));
 +        values.put(Keys.IS_REVOKED, key.isRevoked());
 +        values.put(Keys.CREATION, Apg.getCreationDate(key).getTime() / 1000);
 +        Date expiryDate = Apg.getExpiryDate(key);
 +        if (expiryDate != null) {
 +            values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
 +        }
 +        values.put(Keys.KEY_RING_ID, keyRingId);
 +        values.put(Keys.KEY_DATA, key.getEncoded());
 +        values.put(Keys.RANK, rank);
 +
 +        long rowId = insertOrUpdateKey(values);
 +
 +        if (rowId == -1) {
 +            throw new GeneralException("saving public key " + key.getKeyID() + " failed");
 +        }
 +
 +        Vector<Integer> seenIds = new Vector<Integer>();
 +        int userIdRank = 0;
 +        for (String userId : new IterableIterator<String>(key.getUserIDs())) {
 +            seenIds.add(saveUserId(rowId, userId, userIdRank));
 +            ++userIdRank;
 +        }
 +
 +        String seenIdsStr = "";
 +        for (Integer id : seenIds) {
 +            if (seenIdsStr.length() > 0) {
 +                seenIdsStr += ",";
 +            }
 +            seenIdsStr += id;
 +        }
 +        mDb.delete(UserIds.TABLE_NAME,
 +                   UserIds.KEY_ID + " = ? AND " +
 +                   UserIds._ID + " NOT IN (" + seenIdsStr + ")",
 +                   new String[] { "" + rowId });
 +
 +        return (int)rowId;
 +    }
 +
 +    private int saveKey(long keyRingId, PGPSecretKey key, int rank)
 +            throws IOException, GeneralException {
 +        ContentValues values = new ContentValues();
 +
 +        values.put(Keys.KEY_ID, key.getPublicKey().getKeyID());
 +        values.put(Keys.TYPE, Id.database.type_secret);
 +        values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
 +        values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm());
 +        values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength());
 +        values.put(Keys.CAN_SIGN, Apg.isSigningKey(key));
 +        values.put(Keys.CAN_ENCRYPT, Apg.isEncryptionKey(key));
 +        values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked());
 +        values.put(Keys.CREATION, Apg.getCreationDate(key).getTime() / 1000);
 +        Date expiryDate = Apg.getExpiryDate(key);
 +        if (expiryDate != null) {
 +            values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
 +        }
 +        values.put(Keys.KEY_RING_ID, keyRingId);
 +        values.put(Keys.KEY_DATA, key.getEncoded());
 +        values.put(Keys.RANK, rank);
 +
 +        long rowId = insertOrUpdateKey(values);
 +
 +        if (rowId == -1) {
 +            throw new GeneralException("saving secret key " + key.getPublicKey().getKeyID() + " failed");
 +        }
 +
 +        Vector<Integer> seenIds = new Vector<Integer>();
 +        int userIdRank = 0;
 +        for (String userId : new IterableIterator<String>(key.getUserIDs())) {
 +            seenIds.add(saveUserId(rowId, userId, userIdRank));
 +            ++userIdRank;
 +        }
 +
 +        String seenIdsStr = "";
 +        for (Integer id : seenIds) {
 +            if (seenIdsStr.length() > 0) {
 +                seenIdsStr += ",";
 +            }
 +            seenIdsStr += id;
 +        }
 +        mDb.delete(UserIds.TABLE_NAME,
 +                   UserIds.KEY_ID + " = ? AND " +
 +                   UserIds._ID + " NOT IN (" + seenIdsStr + ")",
 +                   new String[] { "" + rowId });
 +
 +        return (int)rowId;
 +    }
 +
 +    private int saveUserId(long keyId, String userId, int rank) throws GeneralException {
 +        ContentValues values = new ContentValues();
 +
 +        values.put(UserIds.KEY_ID, keyId);
 +        values.put(UserIds.USER_ID, userId);
 +        values.put(UserIds.RANK, rank);
 +
 +        long rowId = insertOrUpdateUserId(values);
 +
 +        if (rowId == -1) {
 +            throw new GeneralException("saving user id " + userId + " failed");
 +        }
 +
 +        return (int)rowId;
 +    }
 +
 +    private long insertOrUpdateKeyRing(ContentValues values) {
 +        Cursor c = mDb.query(KeyRings.TABLE_NAME, new String[] { KeyRings._ID },
 +                             KeyRings.MASTER_KEY_ID + " = ? AND " + KeyRings.TYPE + " = ?",
 +                             new String[] {
 +                                 values.getAsString(KeyRings.MASTER_KEY_ID),
 +                                 values.getAsString(KeyRings.TYPE),
 +                             },
 +                             null, null, null);
 +        long rowId = -1;
 +        if (c != null && c.moveToFirst()) {
 +            rowId = c.getLong(0);
 +            mDb.update(KeyRings.TABLE_NAME, values,
 +                       KeyRings._ID + " = ?", new String[] { "" + rowId });
 +            mStatus = Id.return_value.updated;
 +        } else {
 +            rowId = mDb.insert(KeyRings.TABLE_NAME, KeyRings.WHO_ID, values);
 +            mStatus = Id.return_value.ok;
 +        }
 +
 +        if (c != null) {
 +            c.close();
 +        }
 +
 +        return rowId;
 +    }
 +
 +    private long insertOrUpdateKey(ContentValues values) {
 +        Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys._ID },
 +                             Keys.KEY_ID + " = ? AND " + Keys.TYPE + " = ?",
 +                             new String[] {
 +                                 values.getAsString(Keys.KEY_ID),
 +                                 values.getAsString(Keys.TYPE),
 +                             },
 +                             null, null, null);
 +        long rowId = -1;
 +        if (c != null && c.moveToFirst()) {
 +            rowId = c.getLong(0);
 +            mDb.update(Keys.TABLE_NAME, values,
 +                       Keys._ID + " = ?", new String[] { "" + rowId });
 +        } else {
 +            rowId = mDb.insert(Keys.TABLE_NAME, Keys.KEY_DATA, values);
 +        }
 +
 +        if (c != null) {
 +            c.close();
 +        }
 +
 +        return rowId;
 +    }
 +
 +    private long insertOrUpdateUserId(ContentValues values) {
 +        Cursor c = mDb.query(UserIds.TABLE_NAME, new String[] { UserIds._ID },
 +                             UserIds.KEY_ID + " = ? AND " + UserIds.USER_ID + " = ?",
 +                             new String[] {
 +                                 values.getAsString(UserIds.KEY_ID),
 +                                 values.getAsString(UserIds.USER_ID),
 +                             },
 +                             null, null, null);
 +        long rowId = -1;
 +        if (c != null && c.moveToFirst()) {
 +            rowId = c.getLong(0);
 +            mDb.update(UserIds.TABLE_NAME, values,
 +                       UserIds._ID + " = ?", new String[] { "" + rowId });
 +        } else {
 +            rowId = mDb.insert(UserIds.TABLE_NAME, UserIds.USER_ID, values);
 +        }
 +
 +        if (c != null) {
 +            c.close();
 +        }
 +
 +        return rowId;
 +    }
 +
 +    public Object getKeyRing(int keyRingId) {
 +        Cursor c = mDb.query(KeyRings.TABLE_NAME,
 +                             new String[] { KeyRings.KEY_RING_DATA, KeyRings.TYPE },
 +                             KeyRings._ID + " = ?",
 +                             new String[] {
 +                                 "" + keyRingId,
 +                             },
 +                             null, null, null);
 +        byte[] data = null;
 +        Object keyRing = null;
 +        if (c != null && c.moveToFirst()) {
 +            data = c.getBlob(0);
 +            if (data != null) {
 +                try {
 +                    if (c.getInt(1) == Id.database.type_public) {
 +                        keyRing = new PGPPublicKeyRing(data);
 +                    } else {
 +                        keyRing = new PGPSecretKeyRing(data);
 +                    }
 +                } catch (IOException e) {
 +                    // can't load it, then
 +                } catch (PGPException e) {
 +                    // can't load it, then
 +                }
 +            }
 +        }
 +
 +        if (c != null) {
 +            c.close();
 +        }
 +
 +        return keyRing;
 +    }
 +
 +    public byte[] getKeyRingDataFromKeyId(int type, long keyId) {
 +        Cursor c = mDb.query(Keys.TABLE_NAME + " INNER JOIN " + KeyRings.TABLE_NAME + " ON (" +
 +                             KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
 +                             Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + ")",
 +                             new String[] { KeyRings.TABLE_NAME + "." + KeyRings.KEY_RING_DATA },
 +                             Keys.TABLE_NAME + "." + Keys.KEY_ID + " = ? AND " +
 +                             KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
 +                             new String[] {
 +                                 "" + keyId,
 +                                 "" + type,
 +                             },
 +                             null, null, null);
 +
 +        byte[] data = null;
 +        if (c != null && c.moveToFirst()) {
 +            data = c.getBlob(0);
 +        }
 +
 +        if (c != null) {
 +            c.close();
 +        }
 +
 +        return data;
 +    }
 +
 +    public byte[] getKeyDataFromKeyId(int type, long keyId) {
 +        Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys.KEY_DATA },
 +                             Keys.KEY_ID + " = ? AND " + Keys.TYPE + " = ?",
 +                             new String[] {
 +                                 "" + keyId,
 +                                 "" + type,
 +                             },
 +                             null, null, null);
 +        byte[] data = null;
 +        if (c != null && c.moveToFirst()) {
 +            data = c.getBlob(0);
 +        }
 +
 +        if (c != null) {
 +            c.close();
 +        }
 +
 +        return data;
 +    }
 +
 +    public void deleteKeyRing(int keyRingId) {
 +        mDb.beginTransaction();
 +        mDb.delete(KeyRings.TABLE_NAME,
 +                   KeyRings._ID + " = ?", new String[] { "" + keyRingId });
 +
 +        Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys._ID },
 +                             Keys.KEY_RING_ID + " = ?",
 +                             new String[] {
 +                                 "" + keyRingId,
 +                             },
 +                                    null, null, null);
 +        if (c != null && c.moveToFirst()) {
 +            do {
 +                int keyId = c.getInt(0);
 +                deleteKey(keyId);
 +            } while (c.moveToNext());
 +        }
 +
 +        if (c != null) {
 +            c.close();
 +        }
 +
 +        mDb.setTransactionSuccessful();
 +        mDb.endTransaction();
 +    }
 +
 +    private void deleteKey(int keyId) {
 +        mDb.delete(Keys.TABLE_NAME,
 +                   Keys._ID + " = ?", new String[] { "" + keyId });
 +
 +        mDb.delete(UserIds.TABLE_NAME,
 +                   UserIds.KEY_ID + " = ?", new String[] { "" + keyId });
 +    }
 +
 +    public SQLiteDatabase db() {
 +        return mDb;
 +    }
 +}
 diff --git a/src/org/thialfihar/android/apg/provider/Accounts1.java b/src/org/thialfihar/android/apg/provider/KeyRings.java index 9009e4598..a4eae6695 100644 --- a/src/org/thialfihar/android/apg/provider/Accounts1.java +++ b/src/org/thialfihar/android/apg/provider/KeyRings.java @@ -1,36 +1,33 @@ -/* - * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - *      http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.provider; - -import android.net.Uri; -import android.provider.BaseColumns; - -class Accounts1 implements BaseColumns { -    public static final String TABLE_NAME = "accounts"; - -    public static final String _ID_type = "INTEGER PRIMARY KEY"; -    public static final String NAME = "c_name"; -    public static final String NAME_type = "TEXT"; - -    public static final Uri CONTENT_URI = -            Uri.parse("content://" + DataProvider.AUTHORITY + "/accounts"); -    public static final String CONTENT_TYPE = -            "vnd.android.cursor.dir/vnd.thialfihar.apg.account"; -    public static final String CONTENT_ITEM_TYPE = -            "vnd.android.cursor.item/vnd.thialfihar.apg.account"; -    public static final String DEFAULT_SORT_ORDER = _ID + " DESC"; -}
\ No newline at end of file +/*
 + * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at
 + *
 + *      http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +package org.thialfihar.android.apg.provider;
 +
 +import android.provider.BaseColumns;
 +
 +public class KeyRings implements BaseColumns {
 +    public static final String TABLE_NAME = "key_rings";
 +
 +    public static final String _ID_type = "INTEGER PRIMARY KEY";
 +    public static final String MASTER_KEY_ID = "c_master_key_id";
 +    public static final String MASTER_KEY_ID_type = "INT64";
 +    public static final String TYPE = "c_type";
 +    public static final String TYPE_type = "INTEGER";
 +    public static final String WHO_ID = "c_who_id";
 +    public static final String WHO_ID_type = "INTEGER";
 +    public static final String KEY_RING_DATA = "c_key_ring_data";
 +    public static final String KEY_RING_DATA_type = "BLOB";
 +}
 diff --git a/src/org/thialfihar/android/apg/provider/Keys.java b/src/org/thialfihar/android/apg/provider/Keys.java new file mode 100644 index 000000000..618c5e920 --- /dev/null +++ b/src/org/thialfihar/android/apg/provider/Keys.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thialfihar.android.apg.provider; + +import android.provider.BaseColumns; + +public class Keys implements BaseColumns { +    public static final String TABLE_NAME = "keys"; + +    public static final String _ID_type = "INTEGER PRIMARY KEY"; +    public static final String KEY_ID = "c_key_id"; +    public static final String KEY_ID_type = "INT64"; +    public static final String TYPE = "c_type"; +    public static final String TYPE_type = "INTEGER"; +    public static final String IS_MASTER_KEY = "c_is_master_key"; +    public static final String IS_MASTER_KEY_type = "INTEGER"; +    public static final String ALGORITHM = "c_algorithm"; +    public static final String ALGORITHM_type = "INTEGER"; +    public static final String KEY_SIZE = "c_key_size"; +    public static final String KEY_SIZE_type = "INTEGER"; +    public static final String CAN_SIGN = "c_can_sign"; +    public static final String CAN_SIGN_type = "INTEGER"; +    public static final String CAN_ENCRYPT = "c_can_encrypt"; +    public static final String CAN_ENCRYPT_type = "INTEGER"; +    public static final String IS_REVOKED = "c_is_revoked"; +    public static final String IS_REVOKED_type = "INTEGER"; +    public static final String CREATION = "c_creation"; +    public static final String CREATION_type = "INTEGER"; +    public static final String EXPIRY = "c_expiry"; +    public static final String EXPIRY_type = "INTEGER"; +    public static final String KEY_RING_ID = "c_key_ring_id"; +    public static final String KEY_RING_ID_type = "INTEGER"; +    public static final String KEY_DATA = "c_key_data"; +    public static final String KEY_DATA_type = "BLOB"; +    public static final String RANK = "c_key_data"; +    public static final String RANK_type = "INTEGER"; +} diff --git a/src/org/thialfihar/android/apg/provider/PublicKeys.java b/src/org/thialfihar/android/apg/provider/PublicKeys.java deleted file mode 100644 index f15841fa5..000000000 --- a/src/org/thialfihar/android/apg/provider/PublicKeys.java +++ /dev/null @@ -1,22 +0,0 @@ -/*
 - * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
 - *
 - * Licensed under the Apache License, Version 2.0 (the "License");
 - * you may not use this file except in compliance with the License.
 - * You may obtain a copy of the License at
 - *
 - *      http://www.apache.org/licenses/LICENSE-2.0
 - *
 - * Unless required by applicable law or agreed to in writing, software
 - * distributed under the License is distributed on an "AS IS" BASIS,
 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 - * See the License for the specific language governing permissions and
 - * limitations under the License.
 - */
 -
 -package org.thialfihar.android.apg.provider;
 -
 -public class PublicKeys extends PublicKeys1 {
 -    private PublicKeys() {
 -    }
 -}
\ No newline at end of file diff --git a/src/org/thialfihar/android/apg/provider/PublicKeys1.java b/src/org/thialfihar/android/apg/provider/PublicKeys1.java deleted file mode 100644 index d12a67a17..000000000 --- a/src/org/thialfihar/android/apg/provider/PublicKeys1.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - *      http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.provider; - -import android.net.Uri; -import android.provider.BaseColumns; - -class PublicKeys1 implements BaseColumns { -    public static final String TABLE_NAME = "public_keys"; - -    public static final String _ID_type = "INTEGER PRIMARY KEY"; -    public static final String KEY_ID = "c_key_id"; -    public static final String KEY_ID_type = "INT64"; -    public static final String KEY_DATA = "c_key_data"; -    public static final String KEY_DATA_type = "BLOB"; -    public static final String WHO_ID = "c_who_id"; -    public static final String WHO_ID_type = "INTEGER"; - -    public static final Uri CONTENT_URI = -            Uri.parse("content://" + DataProvider.AUTHORITY + "/public_keys"); -    public static final Uri CONTENT_URI_BY_KEY_ID = -            Uri.parse("content://" + DataProvider.AUTHORITY + "/public_keys/key_id"); -    public static final String CONTENT_TYPE = -            "vnd.android.cursor.dir/vnd.thialfihar.apg.public_key"; -    public static final String CONTENT_ITEM_TYPE = -            "vnd.android.cursor.item/vnd.thialfihar.apg.public_key"; -    public static final String DEFAULT_SORT_ORDER = _ID + " DESC"; -}
\ No newline at end of file diff --git a/src/org/thialfihar/android/apg/provider/SecretKeys1.java b/src/org/thialfihar/android/apg/provider/SecretKeys1.java deleted file mode 100644 index 3ca405f70..000000000 --- a/src/org/thialfihar/android/apg/provider/SecretKeys1.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - *      http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.provider; - -import android.net.Uri; -import android.provider.BaseColumns; - -class SecretKeys1 implements BaseColumns { -    public static final String TABLE_NAME = "secret_keys"; - -    public static final String _ID_type = "INTEGER PRIMARY KEY"; -    public static final String KEY_ID = "c_key_id"; -    public static final String KEY_ID_type = "INT64"; -    public static final String KEY_DATA = "c_key_data"; -    public static final String KEY_DATA_type = "BLOB"; -    public static final String WHO_ID = "c_who_id"; -    public static final String WHO_ID_type = "INTEGER"; - -    public static final Uri CONTENT_URI = -            Uri.parse("content://" + DataProvider.AUTHORITY + "/secret_keys"); -    public static final Uri CONTENT_URI_BY_KEY_ID = -            Uri.parse("content://" + DataProvider.AUTHORITY + "/secret_keys/key_id"); -    public static final String CONTENT_TYPE = -            "vnd.android.cursor.dir/vnd.thialfihar.apg.secret_key"; -    public static final String CONTENT_ITEM_TYPE = -            "vnd.android.cursor.item/vnd.thialfihar.apg.secret_key"; -    public static final String DEFAULT_SORT_ORDER = _ID + " DESC"; -}
\ No newline at end of file diff --git a/src/org/thialfihar/android/apg/provider/SecretKeys.java b/src/org/thialfihar/android/apg/provider/UserIds.java index d31f306ae..2b1162beb 100644 --- a/src/org/thialfihar/android/apg/provider/SecretKeys.java +++ b/src/org/thialfihar/android/apg/provider/UserIds.java @@ -16,7 +16,16 @@  package org.thialfihar.android.apg.provider;
 -public class SecretKeys extends SecretKeys1 {
 -    private SecretKeys() {
 -    }
 -}
\ No newline at end of file +import android.provider.BaseColumns;
 +
 +public class UserIds implements BaseColumns {
 +    public static final String TABLE_NAME = "user_ids";
 +
 +    public static final String _ID_type = "INTEGER PRIMARY KEY";
 +    public static final String KEY_ID = "c_key_id";
 +    public static final String KEY_ID_type = "INTEGER";
 +    public static final String USER_ID = "c_user_id";
 +    public static final String USER_ID_type = "TEXT";
 +    public static final String RANK = "c_rank";
 +    public static final String RANK_type = "INTEGER";
 +}
 diff --git a/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java b/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java index bc38fba4c..044da2db2 100644 --- a/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java +++ b/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java @@ -233,7 +233,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {      }
      public GregorianCalendar getExpiryDate() {
 -     return mExpiryDate;
 +        return mExpiryDate;
      }
      public int getUsage() {
 diff --git a/src/org/thialfihar/android/apg/ui/widget/SectionView.java b/src/org/thialfihar/android/apg/ui/widget/SectionView.java index cc1410c26..e5dcd4aca 100644 --- a/src/org/thialfihar/android/apg/ui/widget/SectionView.java +++ b/src/org/thialfihar/android/apg/ui/widget/SectionView.java @@ -76,7 +76,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor                      }
                  }
 -                String error = data.getString("error");
 +                String error = data.getString(Apg.EXTRA_ERROR);
                  if (error != null) {
                      Toast.makeText(getContext(),
                                     getContext().getString(R.string.errorMessage, error),
 @@ -310,24 +310,24 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor                                      mNewKeySize, passPhrase,
                                      masterKey);
          } catch (NoSuchProviderException e) {
 -            error = e.getMessage();
 +            error = "" + e;
          } catch (NoSuchAlgorithmException e) {
 -            error = e.getMessage();
 +            error = "" + e;
          } catch (PGPException e) {
 -            error = e.getMessage();
 +            error = "" + e;
          } catch (InvalidParameterException e) {
 -            error = e.getMessage();
 +            error = "" + e;
          } catch (InvalidAlgorithmParameterException e) {
 -            error = e.getMessage();
 +            error = "" + e;
          } catch (Apg.GeneralException e) {
 -            error = e.getMessage();
 +            error = "" + e;
          }
          Message message = new Message();
          Bundle data = new Bundle();
          data.putBoolean("closeProgressDialog", true);
          if (error != null) {
 -            data.putString("error", error);
 +            data.putString(Apg.EXTRA_ERROR, error);
          } else {
              data.putBoolean("gotNewKey", true);
          }
  | 
