mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-23 17:22:16 -05:00
branching trunk out of latest 1.0.x to get a clean start for it
This commit is contained in:
commit
a85ae5e009
@ -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>
|
||||
<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>
|
@ -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"/>
|
||||
|
@ -69,20 +69,6 @@
|
||||
android:layout_width="wrap_content"
|
||||
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"
|
||||
|
@ -62,20 +62,6 @@
|
||||
android:layout_width="wrap_content"
|
||||
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"
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
242
res/values-sl/strings.xml
Normal file
242
res/values-sl/strings.xml
Normal file
@ -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>
|
@ -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>
|
||||
|
||||
|
@ -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";
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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() {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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: {
|
||||
if (mReturnResult) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtras(data);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
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)));
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
default: {
|
||||
// shouldn't happen
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
642
src/org/thialfihar/android/apg/KeyListActivity.java
Normal file
642
src/org/thialfihar/android/apg/KeyListActivity.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
@ -297,6 +309,13 @@ public class MainActivity extends BaseActivity {
|
||||
minflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
}
|
||||
|
||||
@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();
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
@ -79,507 +44,17 @@ public class PublicKeyListActivity extends BaseActivity {
|
||||
return true;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,7 +96,7 @@ 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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
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");
|
||||
|
||||
public void setKeyRings(Vector<PGPPublicKeyRing> keyRings) {
|
||||
mKeyRings = keyRings;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public Vector<PGPPublicKeyRing> getKeyRings() {
|
||||
return mKeyRings;
|
||||
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 {
|
||||
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));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
147
src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java
Normal file
147
src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
81
src/org/thialfihar/android/apg/Service.java
Normal file
81
src/org/thialfihar/android/apg/Service.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -16,7 +16,12 @@
|
||||
|
||||
package org.thialfihar.android.apg.provider;
|
||||
|
||||
public class Accounts extends Accounts1 {
|
||||
private Accounts() {
|
||||
}
|
||||
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";
|
||||
}
|
@ -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_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 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_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 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 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 int ACCOUNTS = 301;
|
||||
private static final int ACCOUNT_ID = 302;
|
||||
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(AUTHORITY, "key_rings/public/key_id/*", PUBLIC_KEY_RING_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(AUTHORITY, "key_rings/public/*/keys", PUBLIC_KEY_RING_KEYS);
|
||||
mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys/#", PUBLIC_KEY_RING_KEY_RANK);
|
||||
|
||||
mUriMatcher.addURI(DataProvider.AUTHORITY, "accounts", ACCOUNTS);
|
||||
mUriMatcher.addURI(DataProvider.AUTHORITY, "accounts/#", ACCOUNT_ID);
|
||||
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);
|
||||
|
||||
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);
|
||||
mUriMatcher.addURI(AUTHORITY, "key_rings/public", PUBLIC_KEY_RINGS);
|
||||
mUriMatcher.addURI(AUTHORITY, "key_rings/public/*", PUBLIC_KEY_RING_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);
|
||||
mUriMatcher.addURI(AUTHORITY, "key_rings/secret/key_id/*", SECRET_KEY_RING_BY_KEY_ID);
|
||||
|
||||
mAccountsProjectionMap = new HashMap<String, String>();
|
||||
mAccountsProjectionMap.put(Accounts._ID, Accounts._ID);
|
||||
mAccountsProjectionMap.put(Accounts.NAME, Accounts.NAME);
|
||||
}
|
||||
mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys", SECRET_KEY_RING_KEYS);
|
||||
mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys/#", SECRET_KEY_RING_KEY_RANK);
|
||||
|
||||
/**
|
||||
* This class helps open, create, and upgrade the database file.
|
||||
*/
|
||||
private static class DatabaseHelper extends SQLiteOpenHelper {
|
||||
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);
|
||||
|
||||
DatabaseHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@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 + ");");
|
||||
}
|
||||
|
||||
@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", 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();
|
||||
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 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;
|
||||
|
||||
default: {
|
||||
throw new IllegalArgumentException("Unknown URI " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
switch (mUriMatcher.match(uri)) {
|
||||
case PUBLIC_KEYS: {
|
||||
qb.setTables(PublicKeys.TABLE_NAME);
|
||||
qb.setProjectionMap(mPublicKeysProjectionMap);
|
||||
break;
|
||||
}
|
||||
|
||||
case PUBLIC_KEY_ID: {
|
||||
qb.setTables(PublicKeys.TABLE_NAME);
|
||||
qb.setProjectionMap(mPublicKeysProjectionMap);
|
||||
qb.appendWhere(PublicKeys._ID + "=" + uri.getPathSegments().get(1));
|
||||
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 PUBLIC_KEY_BY_KEY_ID: {
|
||||
qb.setTables(PublicKeys.TABLE_NAME);
|
||||
qb.setProjectionMap(mPublicKeysProjectionMap);
|
||||
qb.appendWhere(PublicKeys.KEY_ID + "=" + uri.getPathSegments().get(2));
|
||||
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') ");
|
||||
|
||||
case SECRET_KEYS: {
|
||||
qb.setTables(SecretKeys.TABLE_NAME);
|
||||
qb.setProjectionMap(mSecretKeysProjectionMap);
|
||||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
qb.appendWhere(" AND tmp." + Keys.KEY_ID + " = ");
|
||||
qb.appendWhereEscapeString(uri.getPathSegments().get(3));
|
||||
|
||||
case SECRET_KEY_BY_KEY_ID: {
|
||||
qb.setTables(SecretKeys.TABLE_NAME);
|
||||
qb.setProjectionMap(mSecretKeysProjectionMap);
|
||||
qb.appendWhere(SecretKeys.KEY_ID + "=" + uri.getPathSegments().get(2));
|
||||
break;
|
||||
}
|
||||
|
||||
case ACCOUNTS: {
|
||||
qb.setTables(Accounts.TABLE_NAME);
|
||||
qb.setProjectionMap(mAccountsProjectionMap);
|
||||
break;
|
||||
}
|
||||
|
||||
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: {
|
||||
case SECRET_KEY_RING_ID:
|
||||
return SECRET_KEY_RING_CONTENT_ITEM_TYPE;
|
||||
|
||||
case SECRET_KEY_RING_BY_KEY_ID:
|
||||
return SECRET_KEY_RING_CONTENT_ITEM_TYPE;
|
||||
|
||||
case SECRET_KEY_RING_KEYS:
|
||||
return SECRET_KEY_CONTENT_DIR_TYPE;
|
||||
|
||||
case SECRET_KEY_RING_KEY_RANK:
|
||||
return SECRET_KEY_CONTENT_ITEM_TYPE;
|
||||
|
||||
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 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_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 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);
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new IllegalArgumentException("Unknown URI " + uri);
|
||||
}
|
||||
}
|
||||
// not supported
|
||||
return null;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return count;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
605
src/org/thialfihar/android/apg/provider/Database.java
Normal file
605
src/org/thialfihar/android/apg/provider/Database.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -16,21 +16,18 @@
|
||||
|
||||
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 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 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";
|
||||
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";
|
||||
}
|
51
src/org/thialfihar/android/apg/provider/Keys.java
Normal file
51
src/org/thialfihar/android/apg/provider/Keys.java
Normal file
@ -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";
|
||||
}
|
@ -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() {
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
@ -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";
|
||||
}
|
@ -16,7 +16,16 @@
|
||||
|
||||
package org.thialfihar.android.apg.provider;
|
||||
|
||||
public class SecretKeys extends SecretKeys1 {
|
||||
private SecretKeys() {
|
||||
}
|
||||
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";
|
||||
}
|
@ -233,7 +233,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
|
||||
}
|
||||
|
||||
public GregorianCalendar getExpiryDate() {
|
||||
return mExpiryDate;
|
||||
return mExpiryDate;
|
||||
}
|
||||
|
||||
public int getUsage() {
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user