mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-27 19:22:14 -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
|
<manifest
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.thialfihar.android.apg"
|
package="org.thialfihar.android.apg"
|
||||||
android:versionName="0.9.5" android:versionCode="10">
|
android:versionCode="15" android:versionName="1.0.1">
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:icon="@drawable/icon"
|
android:icon="@drawable/icon"
|
||||||
@ -53,12 +53,28 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".SelectPublicKeyListActivity"
|
android:name=".SelectPublicKeyListActivity"
|
||||||
android:label="@string/title_selectRecipients"
|
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
|
<activity
|
||||||
android:name=".SelectSecretKeyListActivity"
|
android:name=".SelectSecretKeyListActivity"
|
||||||
android:label="@string/title_selectSignature"
|
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
|
<activity
|
||||||
android:name=".EncryptActivity"
|
android:name=".EncryptActivity"
|
||||||
@ -68,6 +84,9 @@
|
|||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.thialfihar.android.apg.intent.ENCRYPT" />
|
<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_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>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
@ -92,6 +111,9 @@
|
|||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.thialfihar.android.apg.intent.DECRYPT" />
|
<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_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>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
@ -106,13 +128,22 @@
|
|||||||
android:label="@string/title_preferences"
|
android:label="@string/title_preferences"
|
||||||
android:configChanges="keyboardHidden|orientation|keyboard"/>
|
android:configChanges="keyboardHidden|orientation|keyboard"/>
|
||||||
|
|
||||||
|
<service android:name=".Service" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
|
android:readPermission="org.thialfihar.android.apg.permission.READ_KEY_DETAILS"
|
||||||
android:name="org.thialfihar.android.apg.provider.DataProvider"
|
android:name="org.thialfihar.android.apg.provider.DataProvider"
|
||||||
android:authorities="org.thialfihar.android.apg.provider"/>
|
android:authorities="org.thialfihar.android.apg.provider"/>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="5" />
|
<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="5" />
|
||||||
|
|
||||||
|
<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="com.google.android.providers.gmail.permission.READ_GMAIL" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@ -77,6 +77,7 @@
|
|||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/message"
|
android:id="@+id/message"
|
||||||
android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
|
android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
|
||||||
|
android:scrollHorizontally="true"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="fill_parent"
|
||||||
android:gravity="top"/>
|
android:gravity="top"/>
|
||||||
|
@ -69,20 +69,6 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="fill_parent"/>
|
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
|
<TextView
|
||||||
android:id="@+id/status"
|
android:id="@+id/status"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
@ -62,20 +62,6 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="fill_parent"/>
|
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
|
<TextView
|
||||||
android:id="@+id/status"
|
android:id="@+id/status"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
@ -38,10 +38,12 @@
|
|||||||
<string name="title_importKeys">Import Keys</string>
|
<string name="title_importKeys">Import Keys</string>
|
||||||
<string name="title_exportKey">Export Key</string>
|
<string name="title_exportKey">Export Key</string>
|
||||||
<string name="title_exportKeys">Export Keys</string>
|
<string name="title_exportKeys">Export Keys</string>
|
||||||
|
<string name="title_keyNotFound">Key Not Found</string>
|
||||||
|
|
||||||
<!-- section_lowerCase: capitalized words, no punctuation -->
|
<!-- section_lowerCase: capitalized words, no punctuation -->
|
||||||
<string name="section_userIds">User IDs</string>
|
<string name="section_userIds">User IDs</string>
|
||||||
<string name="section_keys">Keys</string>
|
<string name="section_keys">Keys</string>
|
||||||
|
<string name="section_general">General</string>
|
||||||
<string name="section_defaults">Defaults</string>
|
<string name="section_defaults">Defaults</string>
|
||||||
|
|
||||||
<!-- btn_lowerCase: capitalized words, no punctuation -->
|
<!-- btn_lowerCase: capitalized words, no punctuation -->
|
||||||
@ -92,6 +94,9 @@
|
|||||||
<string name="label_hashAlgorithm">Hash Algorithm</string>
|
<string name="label_hashAlgorithm">Hash Algorithm</string>
|
||||||
<string name="label_asymmetric">Public Key</string>
|
<string name="label_asymmetric">Public Key</string>
|
||||||
<string name="label_symmetric">Pass Phrase</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="noKeysSelected">Select</string>
|
||||||
<string name="oneKeySelected">1 Selected</string>
|
<string name="oneKeySelected">1 Selected</string>
|
||||||
@ -108,9 +113,16 @@
|
|||||||
<string name="notValid">not valid</string>
|
<string name="notValid">not valid</string>
|
||||||
|
|
||||||
<!-- choice_lowerCase: capitalized firwst word, no punctuation -->
|
<!-- choice_lowerCase: capitalized firwst word, no punctuation -->
|
||||||
|
<string name="choice_none">None</string>
|
||||||
<string name="choice_signOnly">Sign only</string>
|
<string name="choice_signOnly">Sign only</string>
|
||||||
<string name="choice_encryptOnly">Encrypt only</string>
|
<string name="choice_encryptOnly">Encrypt only</string>
|
||||||
<string name="choice_signAndEncrypt">Sign and Encrypt</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="dsa">DSA</string>
|
||||||
<string name="elgamal">ElGamal</string>
|
<string name="elgamal">ElGamal</string>
|
||||||
@ -133,7 +145,7 @@
|
|||||||
<string name="usingClipboardContent">Using clipboard content.</string>
|
<string name="usingClipboardContent">Using clipboard content.</string>
|
||||||
<string name="keySaved">Key saved.</string>
|
<string name="keySaved">Key saved.</string>
|
||||||
<string name="setAPassPhrase">Set a pass phrase via the option menu first.</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="passPhrasesDoNotMatch">The pass phrases didn't match.</string>
|
||||||
<string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>
|
<string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>
|
||||||
<string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</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="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="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="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="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="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="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="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="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string>
|
||||||
<string name="keysAdded">Succssfully added %s keys.</string>
|
<string name="keysAdded">Succssfully added %s key(s).</string>
|
||||||
<string name="keysUpdated">Succssfully updated %s keys.</string>
|
<string name="keysUpdated">Succssfully updated %s key(s).</string>
|
||||||
<string name="noKeysAddedOrUpdated">No keys added or updated.</string>
|
<string name="noKeysAddedOrUpdated">No keys added or updated.</string>
|
||||||
<string name="keyExported">Succssfully exported 1 key.</string>
|
<string name="keyExported">Succssfully exported 1 key.</string>
|
||||||
<string name="keysExported">Succssfully exported %s keys.</string>
|
<string name="keysExported">Succssfully exported %s keys.</string>
|
||||||
<string name="noKeysExported">No keys exported.</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="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,
|
<!-- error_lowerCase: phrases, no punctuation, all lowercase,
|
||||||
they will be put after "errorMessage", e.g. "Error: file not found" -->
|
they will be put after "errorMessage", e.g. "Error: file not found" -->
|
||||||
@ -227,4 +240,3 @@
|
|||||||
<string name="progress_verifyingIntegrity">verifying integrity...</string>
|
<string name="progress_verifyingIntegrity">verifying integrity...</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
|
@ -38,10 +38,12 @@
|
|||||||
<string name="title_importKeys">Import Keys</string>
|
<string name="title_importKeys">Import Keys</string>
|
||||||
<string name="title_exportKey">Export Key</string>
|
<string name="title_exportKey">Export Key</string>
|
||||||
<string name="title_exportKeys">Export Keys</string>
|
<string name="title_exportKeys">Export Keys</string>
|
||||||
|
<string name="title_keyNotFound">Key Not Found</string>
|
||||||
|
|
||||||
<!-- section_lowerCase: capitalized words, no punctuation -->
|
<!-- section_lowerCase: capitalized words, no punctuation -->
|
||||||
<string name="section_userIds">User IDs</string>
|
<string name="section_userIds">User IDs</string>
|
||||||
<string name="section_keys">Keys</string>
|
<string name="section_keys">Keys</string>
|
||||||
|
<string name="section_general">General</string>
|
||||||
<string name="section_defaults">Defaults</string>
|
<string name="section_defaults">Defaults</string>
|
||||||
|
|
||||||
<!-- btn_lowerCase: capitalized words, no punctuation -->
|
<!-- btn_lowerCase: capitalized words, no punctuation -->
|
||||||
@ -92,6 +94,9 @@
|
|||||||
<string name="label_hashAlgorithm">Hash Algorithm</string>
|
<string name="label_hashAlgorithm">Hash Algorithm</string>
|
||||||
<string name="label_asymmetric">Public Key</string>
|
<string name="label_asymmetric">Public Key</string>
|
||||||
<string name="label_symmetric">Pass Phrase</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="noKeysSelected">Select</string>
|
||||||
<string name="oneKeySelected">1 Selected</string>
|
<string name="oneKeySelected">1 Selected</string>
|
||||||
@ -108,9 +113,16 @@
|
|||||||
<string name="notValid">not valid</string>
|
<string name="notValid">not valid</string>
|
||||||
|
|
||||||
<!-- choice_lowerCase: capitalized firwst word, no punctuation -->
|
<!-- choice_lowerCase: capitalized firwst word, no punctuation -->
|
||||||
|
<string name="choice_none">None</string>
|
||||||
<string name="choice_signOnly">Sign only</string>
|
<string name="choice_signOnly">Sign only</string>
|
||||||
<string name="choice_encryptOnly">Encrypt only</string>
|
<string name="choice_encryptOnly">Encrypt only</string>
|
||||||
<string name="choice_signAndEncrypt">Sign and Encrypt</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="dsa">DSA</string>
|
||||||
<string name="elgamal">ElGamal</string>
|
<string name="elgamal">ElGamal</string>
|
||||||
@ -133,7 +145,7 @@
|
|||||||
<string name="usingClipboardContent">Using clipboard content.</string>
|
<string name="usingClipboardContent">Using clipboard content.</string>
|
||||||
<string name="keySaved">Key saved.</string>
|
<string name="keySaved">Key saved.</string>
|
||||||
<string name="setAPassPhrase">Set a pass phrase via the option menu first.</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="passPhrasesDoNotMatch">The pass phrases didn't match.</string>
|
||||||
<string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>
|
<string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>
|
||||||
<string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</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="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="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="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="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="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="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="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="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string>
|
||||||
<string name="keysAdded">Succssfully added %s keys.</string>
|
<string name="keysAdded">Succssfully added %s key(s).</string>
|
||||||
<string name="keysUpdated">Succssfully updated %s keys.</string>
|
<string name="keysUpdated">Succssfully updated %s key(s).</string>
|
||||||
<string name="noKeysAddedOrUpdated">No keys added or updated.</string>
|
<string name="noKeysAddedOrUpdated">No keys added or updated.</string>
|
||||||
<string name="keyExported">Succssfully exported 1 key.</string>
|
<string name="keyExported">Succssfully exported 1 key.</string>
|
||||||
<string name="keysExported">Succssfully exported %s keys.</string>
|
<string name="keysExported">Succssfully exported %s keys.</string>
|
||||||
<string name="noKeysExported">No keys exported.</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="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,
|
<!-- error_lowerCase: phrases, no punctuation, all lowercase,
|
||||||
they will be put after "errorMessage", e.g. "Error: file not found" -->
|
they will be put after "errorMessage", e.g. "Error: file not found" -->
|
||||||
@ -227,4 +240,3 @@
|
|||||||
<string name="progress_verifyingIntegrity">verifying integrity...</string>
|
<string name="progress_verifyingIntegrity">verifying integrity...</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
|
@ -38,10 +38,12 @@
|
|||||||
<string name="title_importKeys">Import Keys</string>
|
<string name="title_importKeys">Import Keys</string>
|
||||||
<string name="title_exportKey">Export Key</string>
|
<string name="title_exportKey">Export Key</string>
|
||||||
<string name="title_exportKeys">Export Keys</string>
|
<string name="title_exportKeys">Export Keys</string>
|
||||||
|
<string name="title_keyNotFound">Key Not Found</string>
|
||||||
|
|
||||||
<!-- section_lowerCase: capitalized words, no punctuation -->
|
<!-- section_lowerCase: capitalized words, no punctuation -->
|
||||||
<string name="section_userIds">User IDs</string>
|
<string name="section_userIds">User IDs</string>
|
||||||
<string name="section_keys">Keys</string>
|
<string name="section_keys">Keys</string>
|
||||||
|
<string name="section_general">General</string>
|
||||||
<string name="section_defaults">Defaults</string>
|
<string name="section_defaults">Defaults</string>
|
||||||
|
|
||||||
<!-- btn_lowerCase: capitalized words, no punctuation -->
|
<!-- btn_lowerCase: capitalized words, no punctuation -->
|
||||||
@ -92,6 +94,9 @@
|
|||||||
<string name="label_hashAlgorithm">Hash Algorithm</string>
|
<string name="label_hashAlgorithm">Hash Algorithm</string>
|
||||||
<string name="label_asymmetric">Public Key</string>
|
<string name="label_asymmetric">Public Key</string>
|
||||||
<string name="label_symmetric">Pass Phrase</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="noKeysSelected">Select</string>
|
||||||
<string name="oneKeySelected">1 Selected</string>
|
<string name="oneKeySelected">1 Selected</string>
|
||||||
@ -108,9 +113,16 @@
|
|||||||
<string name="notValid">not valid</string>
|
<string name="notValid">not valid</string>
|
||||||
|
|
||||||
<!-- choice_lowerCase: capitalized firwst word, no punctuation -->
|
<!-- choice_lowerCase: capitalized firwst word, no punctuation -->
|
||||||
|
<string name="choice_none">None</string>
|
||||||
<string name="choice_signOnly">Sign only</string>
|
<string name="choice_signOnly">Sign only</string>
|
||||||
<string name="choice_encryptOnly">Encrypt only</string>
|
<string name="choice_encryptOnly">Encrypt only</string>
|
||||||
<string name="choice_signAndEncrypt">Sign and Encrypt</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="dsa">DSA</string>
|
||||||
<string name="elgamal">ElGamal</string>
|
<string name="elgamal">ElGamal</string>
|
||||||
@ -133,7 +145,7 @@
|
|||||||
<string name="usingClipboardContent">Using clipboard content.</string>
|
<string name="usingClipboardContent">Using clipboard content.</string>
|
||||||
<string name="keySaved">Key saved.</string>
|
<string name="keySaved">Key saved.</string>
|
||||||
<string name="setAPassPhrase">Set a pass phrase via the option menu first.</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="passPhrasesDoNotMatch">The pass phrases didn't match.</string>
|
||||||
<string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>
|
<string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>
|
||||||
<string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</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="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="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="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="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="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="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="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="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string>
|
||||||
<string name="keysAdded">Succssfully added %s keys.</string>
|
<string name="keysAdded">Succssfully added %s key(s).</string>
|
||||||
<string name="keysUpdated">Succssfully updated %s keys.</string>
|
<string name="keysUpdated">Succssfully updated %s key(s).</string>
|
||||||
<string name="noKeysAddedOrUpdated">No keys added or updated.</string>
|
<string name="noKeysAddedOrUpdated">No keys added or updated.</string>
|
||||||
<string name="keyExported">Succssfully exported 1 key.</string>
|
<string name="keyExported">Succssfully exported 1 key.</string>
|
||||||
<string name="keysExported">Succssfully exported %s keys.</string>
|
<string name="keysExported">Succssfully exported %s keys.</string>
|
||||||
<string name="noKeysExported">No keys exported.</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="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,
|
<!-- error_lowerCase: phrases, no punctuation, all lowercase,
|
||||||
they will be put after "errorMessage", e.g. "Error: file not found" -->
|
they will be put after "errorMessage", e.g. "Error: file not found" -->
|
||||||
@ -227,4 +240,3 @@
|
|||||||
<string name="progress_verifyingIntegrity">verifying integrity...</string>
|
<string name="progress_verifyingIntegrity">verifying integrity...</string>
|
||||||
|
|
||||||
</resources>
|
</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_importKeys">Import Keys</string>
|
||||||
<string name="title_exportKey">Export Key</string>
|
<string name="title_exportKey">Export Key</string>
|
||||||
<string name="title_exportKeys">Export Keys</string>
|
<string name="title_exportKeys">Export Keys</string>
|
||||||
|
<string name="title_keyNotFound">Key Not Found</string>
|
||||||
|
|
||||||
<!-- section_lowerCase: capitalized words, no punctuation -->
|
<!-- section_lowerCase: capitalized words, no punctuation -->
|
||||||
<string name="section_userIds">User IDs</string>
|
<string name="section_userIds">User IDs</string>
|
||||||
@ -144,7 +145,7 @@
|
|||||||
<string name="usingClipboardContent">Using clipboard content.</string>
|
<string name="usingClipboardContent">Using clipboard content.</string>
|
||||||
<string name="keySaved">Key saved.</string>
|
<string name="keySaved">Key saved.</string>
|
||||||
<string name="setAPassPhrase">Set a pass phrase via the option menu first.</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="passPhrasesDoNotMatch">The pass phrases didn't match.</string>
|
||||||
<string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>
|
<string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>
|
||||||
<string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</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="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="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="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="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="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="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="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="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string>
|
||||||
<string name="keysAdded">Succssfully added %s keys.</string>
|
<string name="keysAdded">Succssfully added %s key(s).</string>
|
||||||
<string name="keysUpdated">Succssfully updated %s keys.</string>
|
<string name="keysUpdated">Succssfully updated %s key(s).</string>
|
||||||
<string name="noKeysAddedOrUpdated">No keys added or updated.</string>
|
<string name="noKeysAddedOrUpdated">No keys added or updated.</string>
|
||||||
<string name="keyExported">Succssfully exported 1 key.</string>
|
<string name="keyExported">Succssfully exported 1 key.</string>
|
||||||
<string name="keysExported">Succssfully exported %s keys.</string>
|
<string name="keysExported">Succssfully exported %s keys.</string>
|
||||||
<string name="noKeysExported">No keys exported.</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="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,
|
<!-- error_lowerCase: phrases, no punctuation, all lowercase,
|
||||||
they will be put after "errorMessage", e.g. "Error: file not found" -->
|
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_decompressingData">decompressing data...</string>
|
||||||
<string name="progress_verifyingIntegrity">verifying integrity...</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>
|
</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";
|
|
||||||
|
|
||||||
}
|
|
@ -19,6 +19,7 @@ package org.thialfihar.android.apg;
|
|||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.EOFException;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
@ -35,8 +36,6 @@ import java.security.SecureRandom;
|
|||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -80,18 +79,19 @@ import org.bouncycastle2.openpgp.PGPSignatureList;
|
|||||||
import org.bouncycastle2.openpgp.PGPSignatureSubpacketGenerator;
|
import org.bouncycastle2.openpgp.PGPSignatureSubpacketGenerator;
|
||||||
import org.bouncycastle2.openpgp.PGPSignatureSubpacketVector;
|
import org.bouncycastle2.openpgp.PGPSignatureSubpacketVector;
|
||||||
import org.bouncycastle2.openpgp.PGPUtil;
|
import org.bouncycastle2.openpgp.PGPUtil;
|
||||||
import org.thialfihar.android.apg.provider.PublicKeys;
|
import org.thialfihar.android.apg.provider.Database;
|
||||||
import org.thialfihar.android.apg.provider.SecretKeys;
|
import org.thialfihar.android.apg.provider.KeyRings;
|
||||||
|
import org.thialfihar.android.apg.provider.Keys;
|
||||||
|
import org.thialfihar.android.apg.provider.UserIds;
|
||||||
import org.thialfihar.android.apg.ui.widget.KeyEditor;
|
import org.thialfihar.android.apg.ui.widget.KeyEditor;
|
||||||
import org.thialfihar.android.apg.ui.widget.SectionView;
|
import org.thialfihar.android.apg.ui.widget.SectionView;
|
||||||
import org.thialfihar.android.apg.ui.widget.UserIdEditor;
|
import org.thialfihar.android.apg.ui.widget.UserIdEditor;
|
||||||
import org.thialfihar.android.apg.utils.IterableIterator;
|
import org.thialfihar.android.apg.utils.IterableIterator;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -102,9 +102,35 @@ public class Apg {
|
|||||||
public static final String ENCRYPT = "org.thialfihar.android.apg.intent.ENCRYPT";
|
public static final String ENCRYPT = "org.thialfihar.android.apg.intent.ENCRYPT";
|
||||||
public static final String DECRYPT_FILE = "org.thialfihar.android.apg.intent.DECRYPT_FILE";
|
public static final String DECRYPT_FILE = "org.thialfihar.android.apg.intent.DECRYPT_FILE";
|
||||||
public static final String ENCRYPT_FILE = "org.thialfihar.android.apg.intent.ENCRYPT_FILE";
|
public static final String ENCRYPT_FILE = "org.thialfihar.android.apg.intent.ENCRYPT_FILE";
|
||||||
|
public static final String DECRYPT_AND_RETURN = "org.thialfihar.android.apg.intent.DECRYPT_AND_RETURN";
|
||||||
|
public static final String ENCRYPT_AND_RETURN = "org.thialfihar.android.apg.intent.ENCRYPT_AND_RETURN";
|
||||||
|
public static final String SELECT_PUBLIC_KEYS = "org.thialfihar.android.apg.intent.SELECT_PUBLIC_KEYS";
|
||||||
|
public static final String SELECT_SECRET_KEY = "org.thialfihar.android.apg.intent.SELECT_SECRET_KEY";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String VERSION = "0.9.5";
|
public static final String EXTRA_DATA = "data";
|
||||||
|
public static final String EXTRA_STATUS = "status";
|
||||||
|
public static final String EXTRA_ERROR = "error";
|
||||||
|
public static final String EXTRA_DECRYPTED_MESSAGE = "decryptedMessage";
|
||||||
|
public static final String EXTRA_ENCRYPTED_MESSAGE = "decryptedMessage";
|
||||||
|
public static final String EXTRA_SIGNATURE = "signature";
|
||||||
|
public static final String EXTRA_SIGNATURE_KEY_ID = "signatureKeyId";
|
||||||
|
public static final String EXTRA_SIGNATURE_USER_ID = "signatureUserId";
|
||||||
|
public static final String EXTRA_SIGNATURE_SUCCESS = "signatureSuccess";
|
||||||
|
public static final String EXTRA_SIGNATURE_UNKNOWN = "signatureUnknown";
|
||||||
|
public static final String EXTRA_USER_ID = "userId";
|
||||||
|
public static final String EXTRA_KEY_ID = "keyId";
|
||||||
|
public static final String EXTRA_REPLY_TO = "replyTo";
|
||||||
|
public static final String EXTRA_SEND_TO = "sendTo";
|
||||||
|
public static final String EXTRA_SUBJECT = "subject";
|
||||||
|
public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryptionKeyIds";
|
||||||
|
public static final String EXTRA_SELECTION = "selection";
|
||||||
|
public static final String EXTRA_MESSAGE = "message";
|
||||||
|
public static final String EXTRA_PROGRESS = "progress";
|
||||||
|
public static final String EXTRA_MAX = "max";
|
||||||
|
public static final String EXTRA_ACCOUNT = "account";
|
||||||
|
|
||||||
|
public static String VERSION = "1.0.1";
|
||||||
public static String FULL_VERSION = "APG v" + VERSION;
|
public static String FULL_VERSION = "APG v" + VERSION;
|
||||||
|
|
||||||
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS =
|
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS =
|
||||||
@ -125,9 +151,6 @@ public class Apg {
|
|||||||
CompressionAlgorithmTags.BZIP2,
|
CompressionAlgorithmTags.BZIP2,
|
||||||
CompressionAlgorithmTags.ZIP };
|
CompressionAlgorithmTags.ZIP };
|
||||||
|
|
||||||
protected static Vector<PGPPublicKeyRing> mPublicKeyRings = new Vector<PGPPublicKeyRing>();
|
|
||||||
protected static Vector<PGPSecretKeyRing> mSecretKeyRings = new Vector<PGPSecretKeyRing>();
|
|
||||||
|
|
||||||
public static Pattern PGP_MESSAGE =
|
public static Pattern PGP_MESSAGE =
|
||||||
Pattern.compile(".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*",
|
Pattern.compile(".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*",
|
||||||
Pattern.DOTALL);
|
Pattern.DOTALL);
|
||||||
@ -136,30 +159,12 @@ public class Apg {
|
|||||||
Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
|
Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
|
||||||
Pattern.DOTALL);
|
Pattern.DOTALL);
|
||||||
|
|
||||||
protected static boolean mInitialized = false;
|
|
||||||
|
|
||||||
protected static HashMap<Long, Integer> mSecretKeyIdToIdMap;
|
|
||||||
protected static HashMap<Long, PGPSecretKeyRing> mSecretKeyIdToKeyRingMap;
|
|
||||||
protected static HashMap<Long, Integer> mPublicKeyIdToIdMap;
|
|
||||||
protected static HashMap<Long, PGPPublicKeyRing> mPublicKeyIdToKeyRingMap;
|
|
||||||
|
|
||||||
public static final String PUBLIC_KEY_PROJECTION[] =
|
|
||||||
new String[] {
|
|
||||||
PublicKeys._ID,
|
|
||||||
PublicKeys.KEY_ID,
|
|
||||||
PublicKeys.KEY_DATA,
|
|
||||||
PublicKeys.WHO_ID, };
|
|
||||||
public static final String SECRET_KEY_PROJECTION[] =
|
|
||||||
new String[] {
|
|
||||||
PublicKeys._ID,
|
|
||||||
PublicKeys.KEY_ID,
|
|
||||||
PublicKeys.KEY_DATA,
|
|
||||||
PublicKeys.WHO_ID, };
|
|
||||||
|
|
||||||
private static HashMap<Long, CachedPassPhrase> mPassPhraseCache =
|
private static HashMap<Long, CachedPassPhrase> mPassPhraseCache =
|
||||||
new HashMap<Long, CachedPassPhrase>();
|
new HashMap<Long, CachedPassPhrase>();
|
||||||
private static String mEditPassPhrase = null;
|
private static String mEditPassPhrase = null;
|
||||||
|
|
||||||
|
private static Database mDatabase = null;
|
||||||
|
|
||||||
public static class GeneralException extends Exception {
|
public static class GeneralException extends Exception {
|
||||||
static final long serialVersionUID = 0xf812773342L;
|
static final long serialVersionUID = 0xf812773342L;
|
||||||
|
|
||||||
@ -176,102 +181,14 @@ public class Apg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
public static void initialize(Context context) {
|
||||||
mPublicKeyRings = new Vector<PGPPublicKeyRing>();
|
if (mDatabase == null) {
|
||||||
mSecretKeyRings = new Vector<PGPSecretKeyRing>();
|
mDatabase = new Database(context);
|
||||||
mSecretKeyIdToIdMap = new HashMap<Long, Integer>();
|
|
||||||
mSecretKeyIdToKeyRingMap = new HashMap<Long, PGPSecretKeyRing>();
|
|
||||||
mPublicKeyIdToIdMap = new HashMap<Long, Integer>();
|
|
||||||
mPublicKeyIdToKeyRingMap = new HashMap<Long, PGPPublicKeyRing>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void initialize(Activity context) {
|
|
||||||
if (mInitialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
|
||||||
File dir = new File(Constants.path.app_dir);
|
|
||||||
if (!dir.exists() && !dir.mkdirs()) {
|
|
||||||
// ignore this for now, it's not crucial
|
|
||||||
// that the directory doesn't exist at this point
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadKeyRings(context, Id.type.public_key);
|
public static Database getDatabase() {
|
||||||
loadKeyRings(context, Id.type.secret_key);
|
return mDatabase;
|
||||||
|
|
||||||
mInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class PublicKeySorter implements Comparator<PGPPublicKeyRing> {
|
|
||||||
@Override
|
|
||||||
public int compare(PGPPublicKeyRing object1, PGPPublicKeyRing object2) {
|
|
||||||
PGPPublicKey key1 = getMasterKey(object1);
|
|
||||||
PGPPublicKey key2 = getMasterKey(object2);
|
|
||||||
if (key1 == null && key2 == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key1 == null) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key2 == null) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
String uid1 = getMainUserId(key1);
|
|
||||||
String uid2 = getMainUserId(key2);
|
|
||||||
if (uid1 == null && uid2 == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uid1 == null) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uid2 == null) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return uid1.compareTo(uid2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SecretKeySorter implements Comparator<PGPSecretKeyRing> {
|
|
||||||
@Override
|
|
||||||
public int compare(PGPSecretKeyRing object1, PGPSecretKeyRing object2) {
|
|
||||||
PGPSecretKey key1 = getMasterKey(object1);
|
|
||||||
PGPSecretKey key2 = getMasterKey(object2);
|
|
||||||
if (key1 == null && key2 == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key1 == null) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key2 == null) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
String uid1 = getMainUserId(key1);
|
|
||||||
String uid2 = getMainUserId(key2);
|
|
||||||
if (uid1 == null && uid2 == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uid1 == null) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uid2 == null) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return uid1.compareTo(uid2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setEditPassPhrase(String passPhrase) {
|
public static void setEditPassPhrase(String passPhrase) {
|
||||||
@ -289,7 +206,7 @@ public class Apg {
|
|||||||
public static String getCachedPassPhrase(long keyId) {
|
public static String getCachedPassPhrase(long keyId) {
|
||||||
long realId = keyId;
|
long realId = keyId;
|
||||||
if (realId != Id.key.symmetric) {
|
if (realId != Id.key.symmetric) {
|
||||||
PGPSecretKeyRing keyRing = findSecretKeyRing(keyId);
|
PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
|
||||||
if (keyRing == null) {
|
if (keyRing == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -308,19 +225,30 @@ public class Apg {
|
|||||||
return cpp.passPhrase;
|
return cpp.passPhrase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void cleanUpCache(int ttl) {
|
public static int cleanUpCache(int ttl, int initialDelay) {
|
||||||
|
int delay = initialDelay;
|
||||||
|
long realTtl = ttl * 1000;
|
||||||
long now = new Date().getTime();
|
long now = new Date().getTime();
|
||||||
|
|
||||||
Vector<Long> oldKeys = new Vector<Long>();
|
Vector<Long> oldKeys = new Vector<Long>();
|
||||||
for (Map.Entry<Long, CachedPassPhrase> pair : mPassPhraseCache.entrySet()) {
|
for (Map.Entry<Long, CachedPassPhrase> pair : mPassPhraseCache.entrySet()) {
|
||||||
if ((now - pair.getValue().timestamp) >= 1000 * ttl) {
|
long lived = now - pair.getValue().timestamp;
|
||||||
|
if (lived >= realTtl) {
|
||||||
oldKeys.add(pair.getKey());
|
oldKeys.add(pair.getKey());
|
||||||
|
} else {
|
||||||
|
// see, whether the remaining time for this cache entry improves our
|
||||||
|
// check delay
|
||||||
|
long nextCheck = realTtl - lived + 1000;
|
||||||
|
if (nextCheck < delay) {
|
||||||
|
delay = (int)nextCheck;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (long keyId : oldKeys) {
|
for (long keyId : oldKeys) {
|
||||||
mPassPhraseCache.remove(keyId);
|
mPassPhraseCache.remove(keyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PGPSecretKey createKey(Context context,
|
public static PGPSecretKey createKey(Context context,
|
||||||
@ -413,11 +341,10 @@ public class Apg {
|
|||||||
secretKey = (PGPSecretKey) it.next();
|
secretKey = (PGPSecretKey) it.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return secretKey;
|
return secretKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long getNumDatesBetween(GregorianCalendar first, GregorianCalendar second) {
|
private static long getNumDaysBetween(GregorianCalendar first, GregorianCalendar second) {
|
||||||
GregorianCalendar tmp = new GregorianCalendar();
|
GregorianCalendar tmp = new GregorianCalendar();
|
||||||
tmp.setTime(first.getTime());
|
tmp.setTime(first.getTime());
|
||||||
long numDays = (second.getTimeInMillis() - first.getTimeInMillis()) / 1000 / 86400;
|
long numDays = (second.getTimeInMillis() - first.getTimeInMillis()) / 1000 / 86400;
|
||||||
@ -434,7 +361,7 @@ public class Apg {
|
|||||||
String oldPassPhrase, String newPassPhrase,
|
String oldPassPhrase, String newPassPhrase,
|
||||||
ProgressDialogUpdater progress)
|
ProgressDialogUpdater progress)
|
||||||
throws Apg.GeneralException, NoSuchProviderException, PGPException,
|
throws Apg.GeneralException, NoSuchProviderException, PGPException,
|
||||||
NoSuchAlgorithmException, SignatureException {
|
NoSuchAlgorithmException, SignatureException, IOException, Database.GeneralException {
|
||||||
|
|
||||||
progress.setProgress(R.string.progress_buildingKey, 0, 100);
|
progress.setProgress(R.string.progress_buildingKey, 0, 100);
|
||||||
|
|
||||||
@ -465,7 +392,7 @@ public class Apg {
|
|||||||
} catch (UserIdEditor.NoEmailException e) {
|
} catch (UserIdEditor.NoEmailException e) {
|
||||||
throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAnEmailAddress));
|
throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAnEmailAddress));
|
||||||
} catch (UserIdEditor.InvalidEmailException e) {
|
} catch (UserIdEditor.InvalidEmailException e) {
|
||||||
throw new Apg.GeneralException(e.getMessage());
|
throw new Apg.GeneralException("" + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userId.equals("")) {
|
if (userId.equals("")) {
|
||||||
@ -549,11 +476,12 @@ public class Apg {
|
|||||||
hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
|
hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
|
||||||
hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
|
hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
|
||||||
|
|
||||||
|
// TODO: this doesn't work quite right yet
|
||||||
if (keyEditor.getExpiryDate() != null) {
|
if (keyEditor.getExpiryDate() != null) {
|
||||||
GregorianCalendar creationDate = new GregorianCalendar();
|
GregorianCalendar creationDate = new GregorianCalendar();
|
||||||
creationDate.setTime(getCreationDate(masterKey));
|
creationDate.setTime(getCreationDate(masterKey));
|
||||||
GregorianCalendar expiryDate = keyEditor.getExpiryDate();
|
GregorianCalendar expiryDate = keyEditor.getExpiryDate();
|
||||||
long numDays = getNumDatesBetween(creationDate, expiryDate);
|
long numDays = getNumDaysBetween(creationDate, expiryDate);
|
||||||
if (numDays <= 0) {
|
if (numDays <= 0) {
|
||||||
throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
|
throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
|
||||||
}
|
}
|
||||||
@ -600,11 +528,12 @@ public class Apg {
|
|||||||
}
|
}
|
||||||
hashedPacketsGen.setKeyFlags(true, keyFlags);
|
hashedPacketsGen.setKeyFlags(true, keyFlags);
|
||||||
|
|
||||||
|
// TODO: this doesn't work quite right yet
|
||||||
if (keyEditor.getExpiryDate() != null) {
|
if (keyEditor.getExpiryDate() != null) {
|
||||||
GregorianCalendar creationDate = new GregorianCalendar();
|
GregorianCalendar creationDate = new GregorianCalendar();
|
||||||
creationDate.setTime(getCreationDate(masterKey));
|
creationDate.setTime(getCreationDate(masterKey));
|
||||||
GregorianCalendar expiryDate = keyEditor.getExpiryDate();
|
GregorianCalendar expiryDate = keyEditor.getExpiryDate();
|
||||||
long numDays = getNumDatesBetween(creationDate, expiryDate);
|
long numDays = getNumDaysBetween(creationDate, expiryDate);
|
||||||
if (numDays <= 0) {
|
if (numDays <= 0) {
|
||||||
throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
|
throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
|
||||||
}
|
}
|
||||||
@ -619,79 +548,16 @@ public class Apg {
|
|||||||
PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
|
PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
|
||||||
|
|
||||||
progress.setProgress(R.string.progress_savingKeyRing, 90, 100);
|
progress.setProgress(R.string.progress_savingKeyRing, 90, 100);
|
||||||
saveKeyRing(context, secretKeyRing);
|
mDatabase.saveKeyRing(secretKeyRing);
|
||||||
saveKeyRing(context, publicKeyRing);
|
mDatabase.saveKeyRing(publicKeyRing);
|
||||||
|
|
||||||
loadKeyRings(context, Id.type.public_key);
|
|
||||||
loadKeyRings(context, Id.type.secret_key);
|
|
||||||
progress.setProgress(R.string.progress_done, 100, 100);
|
progress.setProgress(R.string.progress_done, 100, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int saveKeyRing(Activity context, PGPPublicKeyRing keyRing) {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
|
|
||||||
PGPPublicKey masterKey = getMasterKey(keyRing);
|
|
||||||
if (masterKey == null) {
|
|
||||||
return Id.return_value.no_master_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
keyRing.encode(out);
|
|
||||||
out.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
return Id.return_value.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
values.put(PublicKeys.KEY_ID, masterKey.getKeyID());
|
|
||||||
values.put(PublicKeys.KEY_DATA, out.toByteArray());
|
|
||||||
|
|
||||||
Uri uri = Uri.withAppendedPath(PublicKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
|
|
||||||
Cursor cursor = context.managedQuery(uri, PUBLIC_KEY_PROJECTION, null, null, null);
|
|
||||||
if (cursor != null && cursor.getCount() > 0) {
|
|
||||||
context.getContentResolver().update(uri, values, null, null);
|
|
||||||
return Id.return_value.updated;
|
|
||||||
} else {
|
|
||||||
context.getContentResolver().insert(PublicKeys.CONTENT_URI, values);
|
|
||||||
return Id.return_value.ok;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int saveKeyRing(Activity context, PGPSecretKeyRing keyRing) {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
|
|
||||||
PGPSecretKey masterKey = getMasterKey(keyRing);
|
|
||||||
if (masterKey == null) {
|
|
||||||
return Id.return_value.no_master_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
keyRing.encode(out);
|
|
||||||
out.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
return Id.return_value.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
values.put(SecretKeys.KEY_ID, masterKey.getKeyID());
|
|
||||||
values.put(SecretKeys.KEY_DATA, out.toByteArray());
|
|
||||||
|
|
||||||
Uri uri = Uri.withAppendedPath(SecretKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
|
|
||||||
Cursor cursor = context.managedQuery(uri, SECRET_KEY_PROJECTION, null, null, null);
|
|
||||||
if (cursor != null && cursor.getCount() > 0) {
|
|
||||||
context.getContentResolver().update(uri, values, null, null);
|
|
||||||
return Id.return_value.updated;
|
|
||||||
} else {
|
|
||||||
context.getContentResolver().insert(SecretKeys.CONTENT_URI, values);
|
|
||||||
return Id.return_value.ok;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Bundle importKeyRings(Activity context, int type, String filename,
|
public static Bundle importKeyRings(Activity context, int type, String filename,
|
||||||
ProgressDialogUpdater progress)
|
ProgressDialogUpdater progress)
|
||||||
throws GeneralException, FileNotFoundException, PGPException, IOException {
|
throws GeneralException, FileNotFoundException, PGPException, IOException {
|
||||||
Bundle returnData = new Bundle();
|
Bundle returnData = new Bundle();
|
||||||
PGPObjectFactory objectFactory = null;
|
|
||||||
|
|
||||||
if (type == Id.type.secret_key) {
|
if (type == Id.type.secret_key) {
|
||||||
progress.setProgress(R.string.progress_importingSecretKeys, 0, 100);
|
progress.setProgress(R.string.progress_importingSecretKeys, 0, 100);
|
||||||
@ -704,37 +570,42 @@ public class Apg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FileInputStream fileIn = new FileInputStream(filename);
|
FileInputStream fileIn = new FileInputStream(filename);
|
||||||
InputStream in = PGPUtil.getDecoderStream(fileIn);
|
long fileSize = new File(filename).length();
|
||||||
objectFactory = new PGPObjectFactory(in);
|
PositionAwareInputStream progressIn = new PositionAwareInputStream(fileIn);
|
||||||
|
// need to have access to the bufferedInput, so we can reuse it for the possible
|
||||||
Vector<Object> objects = new Vector<Object>();
|
// PGPObject chunks after the first one, e.g. files with several consecutive ASCII
|
||||||
Object obj = objectFactory.nextObject();
|
// armour blocks
|
||||||
while (obj != null) {
|
BufferedInputStream bufferedInput = new BufferedInputStream(progressIn);
|
||||||
objects.add(obj);
|
|
||||||
obj = objectFactory.nextObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
int newKeys = 0;
|
int newKeys = 0;
|
||||||
int oldKeys = 0;
|
int oldKeys = 0;
|
||||||
for (int i = 0; i < objects.size(); ++i) {
|
try {
|
||||||
progress.setProgress(i * 100 / objects.size(), 100);
|
while (true) {
|
||||||
obj = objects.get(i);
|
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
|
||||||
|
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
|
||||||
|
Object obj = objectFactory.nextObject();
|
||||||
|
// if the first is already a null object, then we can stop trying
|
||||||
|
if (obj == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while (obj != null) {
|
||||||
PGPPublicKeyRing publicKeyRing;
|
PGPPublicKeyRing publicKeyRing;
|
||||||
PGPSecretKeyRing secretKeyRing;
|
PGPSecretKeyRing secretKeyRing;
|
||||||
int retValue;
|
// a return value that doesn't match any Id.return_value.* values, in case
|
||||||
|
// saveKeyRing is never called
|
||||||
|
int retValue = 2107;
|
||||||
|
|
||||||
if (type == Id.type.secret_key) {
|
try {
|
||||||
if (!(obj instanceof PGPSecretKeyRing)) {
|
if (type == Id.type.secret_key && obj instanceof PGPSecretKeyRing) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
secretKeyRing = (PGPSecretKeyRing) obj;
|
secretKeyRing = (PGPSecretKeyRing) obj;
|
||||||
retValue = saveKeyRing(context, secretKeyRing);
|
retValue = mDatabase.saveKeyRing(secretKeyRing);
|
||||||
} else {
|
} else if (type == Id.type.public_key && obj instanceof PGPPublicKeyRing) {
|
||||||
if (!(obj instanceof PGPPublicKeyRing)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
publicKeyRing = (PGPPublicKeyRing) obj;
|
publicKeyRing = (PGPPublicKeyRing) obj;
|
||||||
retValue = saveKeyRing(context, publicKeyRing);
|
retValue = mDatabase.saveKeyRing(publicKeyRing);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
retValue = Id.return_value.error;
|
||||||
|
} catch (Database.GeneralException e) {
|
||||||
|
retValue = Id.return_value.error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (retValue == Id.return_value.error) {
|
if (retValue == Id.return_value.error) {
|
||||||
@ -746,10 +617,13 @@ public class Apg {
|
|||||||
} else if (retValue == Id.return_value.ok) {
|
} else if (retValue == Id.return_value.ok) {
|
||||||
++newKeys;
|
++newKeys;
|
||||||
}
|
}
|
||||||
|
progress.setProgress((int)(100 * progressIn.position() / fileSize), 100);
|
||||||
|
obj = objectFactory.nextObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (EOFException e) {
|
||||||
|
// nothing to do, we are done
|
||||||
}
|
}
|
||||||
|
|
||||||
progress.setProgress(R.string.progress_reloadingKeys, 100, 100);
|
|
||||||
loadKeyRings(context, type);
|
|
||||||
|
|
||||||
returnData.putInt("added", newKeys);
|
returnData.putInt("added", newKeys);
|
||||||
returnData.putInt("updated", oldKeys);
|
returnData.putInt("updated", oldKeys);
|
||||||
@ -759,12 +633,13 @@ public class Apg {
|
|||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bundle exportKeyRings(Activity context, Vector<Object> keys, String filename,
|
public static Bundle exportKeyRings(Activity context, Vector<Integer> keyRingIds,
|
||||||
|
String filename,
|
||||||
ProgressDialogUpdater progress)
|
ProgressDialogUpdater progress)
|
||||||
throws GeneralException, FileNotFoundException, PGPException, IOException {
|
throws GeneralException, FileNotFoundException, PGPException, IOException {
|
||||||
Bundle returnData = new Bundle();
|
Bundle returnData = new Bundle();
|
||||||
|
|
||||||
if (keys.size() == 1) {
|
if (keyRingIds.size() == 1) {
|
||||||
progress.setProgress(R.string.progress_exportingKey, 0, 100);
|
progress.setProgress(R.string.progress_exportingKey, 0, 100);
|
||||||
} else {
|
} else {
|
||||||
progress.setProgress(R.string.progress_exportingKeys, 0, 100);
|
progress.setProgress(R.string.progress_exportingKeys, 0, 100);
|
||||||
@ -777,9 +652,9 @@ public class Apg {
|
|||||||
ArmoredOutputStream out = new ArmoredOutputStream(fileOut);
|
ArmoredOutputStream out = new ArmoredOutputStream(fileOut);
|
||||||
|
|
||||||
int numKeys = 0;
|
int numKeys = 0;
|
||||||
for (int i = 0; i < keys.size(); ++i) {
|
for (int i = 0; i < keyRingIds.size(); ++i) {
|
||||||
progress.setProgress(i * 100 / keys.size(), 100);
|
progress.setProgress(i * 100 / keyRingIds.size(), 100);
|
||||||
Object obj = keys.get(i);
|
Object obj = mDatabase.getKeyRing(keyRingIds.get(i));
|
||||||
PGPPublicKeyRing publicKeyRing;
|
PGPPublicKeyRing publicKeyRing;
|
||||||
PGPSecretKeyRing secretKeyRing;
|
PGPSecretKeyRing secretKeyRing;
|
||||||
|
|
||||||
@ -803,61 +678,6 @@ public class Apg {
|
|||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void loadKeyRings(Activity context, int type) {
|
|
||||||
Cursor cursor;
|
|
||||||
if (type == Id.type.secret_key) {
|
|
||||||
mSecretKeyRings.clear();
|
|
||||||
mSecretKeyIdToIdMap.clear();
|
|
||||||
mSecretKeyIdToKeyRingMap.clear();
|
|
||||||
cursor = context.managedQuery(SecretKeys.CONTENT_URI, SECRET_KEY_PROJECTION,
|
|
||||||
null, null, null);
|
|
||||||
} else {
|
|
||||||
mPublicKeyRings.clear();
|
|
||||||
mPublicKeyIdToIdMap.clear();
|
|
||||||
mPublicKeyIdToKeyRingMap.clear();
|
|
||||||
cursor = context.managedQuery(PublicKeys.CONTENT_URI, PUBLIC_KEY_PROJECTION,
|
|
||||||
null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < cursor.getCount(); ++i) {
|
|
||||||
cursor.moveToPosition(i);
|
|
||||||
String sharedIdColumn = PublicKeys._ID; // same in both
|
|
||||||
String sharedKeyIdColumn = PublicKeys.KEY_ID; // same in both
|
|
||||||
String sharedKeyDataColumn = PublicKeys.KEY_DATA; // same in both
|
|
||||||
int idIndex = cursor.getColumnIndex(sharedIdColumn);
|
|
||||||
int keyIdIndex = cursor.getColumnIndex(sharedKeyIdColumn);
|
|
||||||
int keyDataIndex = cursor.getColumnIndex(sharedKeyDataColumn);
|
|
||||||
|
|
||||||
byte keyData[] = cursor.getBlob(keyDataIndex);
|
|
||||||
int id = cursor.getInt(idIndex);
|
|
||||||
long keyId = cursor.getLong(keyIdIndex);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (type == Id.type.secret_key) {
|
|
||||||
PGPSecretKeyRing key = new PGPSecretKeyRing(keyData);
|
|
||||||
mSecretKeyRings.add(key);
|
|
||||||
mSecretKeyIdToIdMap.put(keyId, id);
|
|
||||||
mSecretKeyIdToKeyRingMap.put(keyId, key);
|
|
||||||
} else {
|
|
||||||
PGPPublicKeyRing key = new PGPPublicKeyRing(keyData);
|
|
||||||
mPublicKeyRings.add(key);
|
|
||||||
mPublicKeyIdToIdMap.put(keyId, id);
|
|
||||||
mPublicKeyIdToKeyRingMap.put(keyId, key);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
// TODO: some error handling
|
|
||||||
} catch (PGPException e) {
|
|
||||||
// TODO: some error handling
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == Id.type.secret_key) {
|
|
||||||
Collections.sort(mSecretKeyRings, new SecretKeySorter());
|
|
||||||
} else {
|
|
||||||
Collections.sort(mPublicKeyRings, new PublicKeySorter());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Date getCreationDate(PGPPublicKey key) {
|
public static Date getCreationDate(PGPPublicKey key) {
|
||||||
return key.getCreationTime();
|
return key.getCreationTime();
|
||||||
}
|
}
|
||||||
@ -867,6 +687,9 @@ public class Apg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) {
|
public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) {
|
||||||
|
if (keyRing == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
|
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
|
||||||
if (key.isMasterKey()) {
|
if (key.isMasterKey()) {
|
||||||
return key;
|
return key;
|
||||||
@ -877,6 +700,9 @@ public class Apg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) {
|
public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) {
|
||||||
|
if (keyRing == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
|
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
|
||||||
if (key.isMasterKey()) {
|
if (key.isMasterKey()) {
|
||||||
return key;
|
return key;
|
||||||
@ -982,7 +808,7 @@ public class Apg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static PGPPublicKey getEncryptPublicKey(long masterKeyId) {
|
public static PGPPublicKey getEncryptPublicKey(long masterKeyId) {
|
||||||
PGPPublicKeyRing keyRing = mPublicKeyIdToKeyRingMap.get(masterKeyId);
|
PGPPublicKeyRing keyRing = getPublicKeyRing(masterKeyId);
|
||||||
if (keyRing == null) {
|
if (keyRing == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -994,7 +820,7 @@ public class Apg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static PGPSecretKey getSigningKey(long masterKeyId) {
|
public static PGPSecretKey getSigningKey(long masterKeyId) {
|
||||||
PGPSecretKeyRing keyRing = mSecretKeyIdToKeyRingMap.get(masterKeyId);
|
PGPSecretKeyRing keyRing = getSecretKeyRing(masterKeyId);
|
||||||
if (keyRing == null) {
|
if (keyRing == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -1035,14 +861,6 @@ public class Apg {
|
|||||||
return userId;
|
return userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PGPPublicKeyRing getPublicKeyRing(long keyId) {
|
|
||||||
return mPublicKeyIdToKeyRingMap.get(keyId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PGPSecretKeyRing getSecretKeyRing(long keyId) {
|
|
||||||
return mSecretKeyIdToKeyRingMap.get(keyId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isEncryptionKey(PGPPublicKey key) {
|
public static boolean isEncryptionKey(PGPPublicKey key) {
|
||||||
if (!key.isEncryptionKey()) {
|
if (!key.isEncryptionKey()) {
|
||||||
return false;
|
return false;
|
||||||
@ -1122,9 +940,17 @@ public class Apg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String getAlgorithmInfo(PGPPublicKey key) {
|
public static String getAlgorithmInfo(PGPPublicKey key) {
|
||||||
|
return getAlgorithmInfo(key.getAlgorithm(), key.getBitStrength());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getAlgorithmInfo(PGPSecretKey key) {
|
||||||
|
return getAlgorithmInfo(key.getPublicKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getAlgorithmInfo(int algorithm, int keySize) {
|
||||||
String algorithmStr = null;
|
String algorithmStr = null;
|
||||||
|
|
||||||
switch (key.getAlgorithm()) {
|
switch (algorithm) {
|
||||||
case PGPPublicKey.RSA_ENCRYPT:
|
case PGPPublicKey.RSA_ENCRYPT:
|
||||||
case PGPPublicKey.RSA_GENERAL:
|
case PGPPublicKey.RSA_GENERAL:
|
||||||
case PGPPublicKey.RSA_SIGN: {
|
case PGPPublicKey.RSA_SIGN: {
|
||||||
@ -1148,98 +974,122 @@ public class Apg {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return algorithmStr + ", " + key.getBitStrength() + "bit";
|
return algorithmStr + ", " + keySize + "bit";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getAlgorithmInfo(PGPSecretKey key) {
|
public static void deleteKey(int keyRingId) {
|
||||||
return getAlgorithmInfo(key.getPublicKey());
|
mDatabase.deleteKeyRing(keyRingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deleteKey(Activity context, PGPPublicKeyRing keyRing) {
|
public static Object getKeyRing(int keyRingId) {
|
||||||
PGPPublicKey masterKey = getMasterKey(keyRing);
|
return mDatabase.getKeyRing(keyRingId);
|
||||||
Uri uri = Uri.withAppendedPath(PublicKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
|
|
||||||
context.getContentResolver().delete(uri, null, null);
|
|
||||||
loadKeyRings(context, Id.type.public_key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deleteKey(Activity context, PGPSecretKeyRing keyRing) {
|
public static PGPSecretKeyRing getSecretKeyRing(long keyId) {
|
||||||
PGPSecretKey masterKey = getMasterKey(keyRing);
|
byte[] data = mDatabase.getKeyRingDataFromKeyId(Id.database.type_secret, keyId);
|
||||||
Uri uri = Uri.withAppendedPath(SecretKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
|
if (data == null) {
|
||||||
context.getContentResolver().delete(uri, null, null);
|
return null;
|
||||||
loadKeyRings(context, Id.type.secret_key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PGPPublicKey findPublicKey(long keyId) {
|
|
||||||
PGPPublicKey key = null;
|
|
||||||
for (int i = 0; i < mPublicKeyRings.size(); ++i) {
|
|
||||||
PGPPublicKeyRing keyRing = mPublicKeyRings.get(i);
|
|
||||||
try {
|
try {
|
||||||
key = keyRing.getPublicKey(keyId);
|
return new PGPSecretKeyRing(data);
|
||||||
if (key != null) {
|
} catch (IOException e) {
|
||||||
return key;
|
// no good way to handle this, return null
|
||||||
}
|
// TODO: some info?
|
||||||
} catch (PGPException e) {
|
} catch (PGPException e) {
|
||||||
// just not found, can ignore this
|
// no good way to handle this, return null
|
||||||
}
|
// TODO: some info?
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PGPSecretKey findSecretKey(long keyId) {
|
public static PGPPublicKeyRing getPublicKeyRing(long keyId) {
|
||||||
PGPSecretKey key = null;
|
byte[] data = mDatabase.getKeyRingDataFromKeyId(Id.database.type_public, keyId);
|
||||||
for (int i = 0; i < mSecretKeyRings.size(); ++i) {
|
if (data == null) {
|
||||||
PGPSecretKeyRing keyRing = mSecretKeyRings.get(i);
|
|
||||||
key = keyRing.getSecretKey(keyId);
|
|
||||||
if (key != null) {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PGPSecretKeyRing findSecretKeyRing(long keyId) {
|
|
||||||
for (int i = 0; i < mSecretKeyRings.size(); ++i) {
|
|
||||||
PGPSecretKeyRing keyRing = mSecretKeyRings.get(i);
|
|
||||||
PGPSecretKey key = null;
|
|
||||||
key = keyRing.getSecretKey(keyId);
|
|
||||||
if (key != null) {
|
|
||||||
return keyRing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PGPPublicKeyRing findPublicKeyRing(long keyId) {
|
|
||||||
for (int i = 0; i < mPublicKeyRings.size(); ++i) {
|
|
||||||
PGPPublicKeyRing keyRing = mPublicKeyRings.get(i);
|
|
||||||
PGPPublicKey key = null;
|
|
||||||
try {
|
try {
|
||||||
key = keyRing.getPublicKey(keyId);
|
return new PGPPublicKeyRing(data);
|
||||||
if (key != null) {
|
} catch (IOException e) {
|
||||||
return keyRing;
|
// no good way to handle this, return null
|
||||||
}
|
// TODO: some info?
|
||||||
} catch (PGPException e) {
|
|
||||||
// key not found
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PGPPublicKey getPublicMasterKey(long keyId) {
|
public static PGPSecretKey getSecretKey(long keyId) {
|
||||||
PGPPublicKey key = null;
|
PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
|
||||||
for (int i = 0; i < mPublicKeyRings.size(); ++i) {
|
if (keyRing == null) {
|
||||||
PGPPublicKeyRing keyRing = mPublicKeyRings.get(i);
|
|
||||||
try {
|
|
||||||
key = keyRing.getPublicKey(keyId);
|
|
||||||
if (key != null) {
|
|
||||||
return getMasterKey(keyRing);
|
|
||||||
}
|
|
||||||
} catch (PGPException e) {
|
|
||||||
// just not found, can ignore this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
return keyRing.getSecretKey(keyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PGPPublicKey getPublicKey(long keyId) {
|
||||||
|
PGPPublicKeyRing keyRing = getPublicKeyRing(keyId);
|
||||||
|
if (keyRing == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return keyRing.getPublicKey(keyId);
|
||||||
|
} catch (PGPException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector<Integer> getKeyRingIds(int type) {
|
||||||
|
SQLiteDatabase db = mDatabase.db();
|
||||||
|
Vector<Integer> keyIds = new Vector<Integer>();
|
||||||
|
Cursor c = db.query(KeyRings.TABLE_NAME,
|
||||||
|
new String[] { KeyRings._ID },
|
||||||
|
KeyRings.TYPE + " = ?", new String[] { "" + type },
|
||||||
|
null, null, null);
|
||||||
|
if (c != null && c.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
keyIds.add(c.getInt(0));
|
||||||
|
} while (c.moveToNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c != null) {
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getMainUserId(long keyId, int type) {
|
||||||
|
SQLiteDatabase db = mDatabase.db();
|
||||||
|
Cursor c = db.query(Keys.TABLE_NAME + " INNER JOIN " + KeyRings.TABLE_NAME + " ON (" +
|
||||||
|
KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
|
||||||
|
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + ") " +
|
||||||
|
" INNER JOIN " + Keys.TABLE_NAME + " AS masterKey ON (" +
|
||||||
|
KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
|
||||||
|
"masterKey." + Keys.KEY_RING_ID + " AND " +
|
||||||
|
"masterKey." + Keys.IS_MASTER_KEY + " = '1') " +
|
||||||
|
" INNER JOIN " + UserIds.TABLE_NAME + " ON (" +
|
||||||
|
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " = " +
|
||||||
|
"masterKey." + Keys._ID + " AND " +
|
||||||
|
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0')",
|
||||||
|
new String[] { UserIds.USER_ID },
|
||||||
|
Keys.TABLE_NAME + "." + Keys.KEY_ID + " = ? AND " +
|
||||||
|
KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
|
||||||
|
new String[] {
|
||||||
|
"" + keyId,
|
||||||
|
"" + type,
|
||||||
|
},
|
||||||
|
null, null, null);
|
||||||
|
String userId = "";
|
||||||
|
if (c != null && c.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
userId = c.getString(0);
|
||||||
|
} while (c.moveToNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c != null) {
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
public static void encrypt(Context context,
|
public static void encrypt(Context context,
|
||||||
InputStream inStream, OutputStream outStream,
|
InputStream inStream, OutputStream outStream,
|
||||||
@ -1277,7 +1127,7 @@ public class Apg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (signatureKeyId != 0) {
|
if (signatureKeyId != 0) {
|
||||||
signingKeyRing = findSecretKeyRing(signatureKeyId);
|
signingKeyRing = getSecretKeyRing(signatureKeyId);
|
||||||
signingKey = getSigningKey(signatureKeyId);
|
signingKey = getSigningKey(signatureKeyId);
|
||||||
if (signingKey == null) {
|
if (signingKey == null) {
|
||||||
throw new GeneralException(context.getString(R.string.error_signatureFailed));
|
throw new GeneralException(context.getString(R.string.error_signatureFailed));
|
||||||
@ -1329,7 +1179,7 @@ public class Apg {
|
|||||||
if (compression == Id.choice.compression.none) {
|
if (compression == Id.choice.compression.none) {
|
||||||
bcpgOut = new BCPGOutputStream(encryptOut);
|
bcpgOut = new BCPGOutputStream(encryptOut);
|
||||||
} else {
|
} else {
|
||||||
compressGen = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZLIB);
|
compressGen = new PGPCompressedDataGenerator(compression);
|
||||||
bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut));
|
bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut));
|
||||||
}
|
}
|
||||||
if (signatureKeyId != 0) {
|
if (signatureKeyId != 0) {
|
||||||
@ -1393,7 +1243,7 @@ public class Apg {
|
|||||||
throw new GeneralException(context.getString(R.string.error_noSignatureKey));
|
throw new GeneralException(context.getString(R.string.error_noSignatureKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
signingKeyRing = findSecretKeyRing(signatureKeyId);
|
signingKeyRing = getSecretKeyRing(signatureKeyId);
|
||||||
signingKey = getSigningKey(signatureKeyId);
|
signingKey = getSigningKey(signatureKeyId);
|
||||||
if (signingKey == null) {
|
if (signingKey == null) {
|
||||||
throw new GeneralException(context.getString(R.string.error_signatureFailed));
|
throw new GeneralException(context.getString(R.string.error_signatureFailed));
|
||||||
@ -1479,7 +1329,7 @@ public class Apg {
|
|||||||
if (obj instanceof PGPPublicKeyEncryptedData) {
|
if (obj instanceof PGPPublicKeyEncryptedData) {
|
||||||
gotAsymmetricEncryption = true;
|
gotAsymmetricEncryption = true;
|
||||||
PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj;
|
PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj;
|
||||||
secretKey = findSecretKey(pbe.getKeyID());
|
secretKey = getSecretKey(pbe.getKeyID());
|
||||||
if (secretKey != null) {
|
if (secretKey != null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1532,6 +1382,9 @@ public class Apg {
|
|||||||
String passPhrase, ProgressDialogUpdater progress,
|
String passPhrase, ProgressDialogUpdater progress,
|
||||||
boolean assumeSymmetric)
|
boolean assumeSymmetric)
|
||||||
throws IOException, GeneralException, PGPException, SignatureException {
|
throws IOException, GeneralException, PGPException, SignatureException {
|
||||||
|
if (passPhrase == null) {
|
||||||
|
passPhrase = "";
|
||||||
|
}
|
||||||
Bundle returnData = new Bundle();
|
Bundle returnData = new Bundle();
|
||||||
InputStream in = PGPUtil.getDecoderStream(inStream);
|
InputStream in = PGPUtil.getDecoderStream(inStream);
|
||||||
PGPObjectFactory pgpF = new PGPObjectFactory(in);
|
PGPObjectFactory pgpF = new PGPObjectFactory(in);
|
||||||
@ -1589,7 +1442,7 @@ public class Apg {
|
|||||||
Object obj = it.next();
|
Object obj = it.next();
|
||||||
if (obj instanceof PGPPublicKeyEncryptedData) {
|
if (obj instanceof PGPPublicKeyEncryptedData) {
|
||||||
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
|
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
|
||||||
secretKey = findSecretKey(encData.getKeyID());
|
secretKey = getSecretKey(encData.getKeyID());
|
||||||
if (secretKey != null) {
|
if (secretKey != null) {
|
||||||
pbe = encData;
|
pbe = encData;
|
||||||
break;
|
break;
|
||||||
@ -1634,11 +1487,11 @@ public class Apg {
|
|||||||
|
|
||||||
if (dataChunk instanceof PGPOnePassSignatureList) {
|
if (dataChunk instanceof PGPOnePassSignatureList) {
|
||||||
progress.setProgress(R.string.progress_processingSignature, currentProgress, 100);
|
progress.setProgress(R.string.progress_processingSignature, currentProgress, 100);
|
||||||
returnData.putBoolean("signature", true);
|
returnData.putBoolean(EXTRA_SIGNATURE, true);
|
||||||
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
|
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
|
||||||
for (int i = 0; i < sigList.size(); ++i) {
|
for (int i = 0; i < sigList.size(); ++i) {
|
||||||
signature = sigList.get(i);
|
signature = sigList.get(i);
|
||||||
signatureKey = findPublicKey(signature.getKeyID());
|
signatureKey = getPublicKey(signature.getKeyID());
|
||||||
if (signatureKeyId == 0) {
|
if (signatureKeyId == 0) {
|
||||||
signatureKeyId = signature.getKeyID();
|
signatureKeyId = signature.getKeyID();
|
||||||
}
|
}
|
||||||
@ -1648,21 +1501,21 @@ public class Apg {
|
|||||||
signatureIndex = i;
|
signatureIndex = i;
|
||||||
signatureKeyId = signature.getKeyID();
|
signatureKeyId = signature.getKeyID();
|
||||||
String userId = null;
|
String userId = null;
|
||||||
PGPPublicKeyRing sigKeyRing = findPublicKeyRing(signatureKeyId);
|
PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
|
||||||
if (sigKeyRing != null) {
|
if (sigKeyRing != null) {
|
||||||
userId = getMainUserId(getMasterKey(sigKeyRing));
|
userId = getMainUserId(getMasterKey(sigKeyRing));
|
||||||
}
|
}
|
||||||
returnData.putString("signatureUserId", userId);
|
returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
returnData.putLong("signatureKeyId", signatureKeyId);
|
returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
|
||||||
|
|
||||||
if (signature != null) {
|
if (signature != null) {
|
||||||
signature.initVerify(signatureKey, new BouncyCastleProvider());
|
signature.initVerify(signatureKey, new BouncyCastleProvider());
|
||||||
} else {
|
} else {
|
||||||
returnData.putBoolean("signatureUnknown", true);
|
returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
dataChunk = plainFact.nextObject();
|
dataChunk = plainFact.nextObject();
|
||||||
@ -1694,7 +1547,7 @@ public class Apg {
|
|||||||
try {
|
try {
|
||||||
signature.update(buffer, 0, n);
|
signature.update(buffer, 0, n);
|
||||||
} catch (SignatureException e) {
|
} catch (SignatureException e) {
|
||||||
returnData.putBoolean("signatureSuccess", false);
|
returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, false);
|
||||||
signature = null;
|
signature = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1714,9 +1567,9 @@ public class Apg {
|
|||||||
PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
|
PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
|
||||||
PGPSignature messageSignature = (PGPSignature) signatureList.get(signatureIndex);
|
PGPSignature messageSignature = (PGPSignature) signatureList.get(signatureIndex);
|
||||||
if (signature.verify(messageSignature)) {
|
if (signature.verify(messageSignature)) {
|
||||||
returnData.putBoolean("signatureSuccess", true);
|
returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, true);
|
||||||
} else {
|
} else {
|
||||||
returnData.putBoolean("signatureSuccess", false);
|
returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1769,7 +1622,7 @@ public class Apg {
|
|||||||
byte[] clearText = out.toByteArray();
|
byte[] clearText = out.toByteArray();
|
||||||
outStream.write(clearText);
|
outStream.write(clearText);
|
||||||
|
|
||||||
returnData.putBoolean("signature", true);
|
returnData.putBoolean(EXTRA_SIGNATURE, true);
|
||||||
|
|
||||||
progress.setProgress(R.string.progress_processingSignature, 60, 100);
|
progress.setProgress(R.string.progress_processingSignature, 60, 100);
|
||||||
PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
|
PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
|
||||||
@ -1783,7 +1636,7 @@ public class Apg {
|
|||||||
PGPPublicKey signatureKey = null;
|
PGPPublicKey signatureKey = null;
|
||||||
for (int i = 0; i < sigList.size(); ++i) {
|
for (int i = 0; i < sigList.size(); ++i) {
|
||||||
signature = sigList.get(i);
|
signature = sigList.get(i);
|
||||||
signatureKey = findPublicKey(signature.getKeyID());
|
signatureKey = getPublicKey(signature.getKeyID());
|
||||||
if (signatureKeyId == 0) {
|
if (signatureKeyId == 0) {
|
||||||
signatureKeyId = signature.getKeyID();
|
signatureKeyId = signature.getKeyID();
|
||||||
}
|
}
|
||||||
@ -1792,19 +1645,19 @@ public class Apg {
|
|||||||
} else {
|
} else {
|
||||||
signatureKeyId = signature.getKeyID();
|
signatureKeyId = signature.getKeyID();
|
||||||
String userId = null;
|
String userId = null;
|
||||||
PGPPublicKeyRing sigKeyRing = findPublicKeyRing(signatureKeyId);
|
PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
|
||||||
if (sigKeyRing != null) {
|
if (sigKeyRing != null) {
|
||||||
userId = getMainUserId(getMasterKey(sigKeyRing));
|
userId = getMainUserId(getMasterKey(sigKeyRing));
|
||||||
}
|
}
|
||||||
returnData.putString("signatureUserId", userId);
|
returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
returnData.putLong("signatureKeyId", signatureKeyId);
|
returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
|
||||||
|
|
||||||
if (signature == null) {
|
if (signature == null) {
|
||||||
returnData.putBoolean("signatureUnknown", true);
|
returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
|
||||||
progress.setProgress(R.string.progress_done, 100, 100);
|
progress.setProgress(R.string.progress_done, 100, 100);
|
||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
@ -1829,21 +1682,12 @@ public class Apg {
|
|||||||
while (lookAhead != -1);
|
while (lookAhead != -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
returnData.putBoolean("signatureSuccess", signature.verify());
|
returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, signature.verify());
|
||||||
|
|
||||||
progress.setProgress(R.string.progress_done, 100, 100);
|
progress.setProgress(R.string.progress_done, 100, 100);
|
||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Vector<PGPPublicKeyRing> getPublicKeyRings() {
|
|
||||||
return mPublicKeyRings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Vector<PGPSecretKeyRing> getSecretKeyRings() {
|
|
||||||
return mSecretKeyRings;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// taken from ClearSignedFileProcessor in BC
|
// taken from ClearSignedFileProcessor in BC
|
||||||
private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
|
private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
@ -25,6 +25,7 @@ import android.app.AlertDialog;
|
|||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.content.DialogInterface.OnClickListener;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
@ -42,14 +43,24 @@ public class AskForSecretKeyPassPhrase {
|
|||||||
alert.setTitle(R.string.title_authentification);
|
alert.setTitle(R.string.title_authentification);
|
||||||
|
|
||||||
final PGPSecretKey secretKey;
|
final PGPSecretKey secretKey;
|
||||||
|
final Activity activity = context;
|
||||||
|
|
||||||
if (secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none) {
|
if (secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none) {
|
||||||
secretKey = null;
|
secretKey = null;
|
||||||
alert.setMessage(context.getString(R.string.passPhraseForSymmetricEncryption));
|
alert.setMessage(context.getString(R.string.passPhraseForSymmetricEncryption));
|
||||||
} else {
|
} else {
|
||||||
secretKey = Apg.getMasterKey(Apg.findSecretKeyRing(secretKeyId));
|
secretKey = Apg.getMasterKey(Apg.getSecretKeyRing(secretKeyId));
|
||||||
if (secretKey == null) {
|
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);
|
String userId = Apg.getMainUserIdSafe(context, secretKey);
|
||||||
alert.setMessage(context.getString(R.string.passPhraseFor, userId));
|
alert.setMessage(context.getString(R.string.passPhraseFor, userId));
|
||||||
@ -65,7 +76,6 @@ public class AskForSecretKeyPassPhrase {
|
|||||||
alert.setView(view);
|
alert.setView(view);
|
||||||
|
|
||||||
final PassPhraseCallbackInterface cb = callback;
|
final PassPhraseCallbackInterface cb = callback;
|
||||||
final Activity activity = context;
|
|
||||||
alert.setPositiveButton(android.R.string.ok,
|
alert.setPositiveButton(android.R.string.ok,
|
||||||
new DialogInterface.OnClickListener() {
|
new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
|
@ -17,10 +17,7 @@
|
|||||||
package org.thialfihar.android.apg;
|
package org.thialfihar.android.apg;
|
||||||
|
|
||||||
import java.io.File;
|
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.bcpg.HashAlgorithmTags;
|
||||||
import org.bouncycastle2.openpgp.PGPEncryptedData;
|
import org.bouncycastle2.openpgp.PGPEncryptedData;
|
||||||
|
|
||||||
@ -33,6 +30,7 @@ import android.content.DialogInterface;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@ -53,8 +51,6 @@ public class BaseActivity extends Activity
|
|||||||
private String mDeleteFile = null;
|
private String mDeleteFile = null;
|
||||||
protected static SharedPreferences mPreferences = null;
|
protected static SharedPreferences mPreferences = null;
|
||||||
|
|
||||||
private static Timer mCacheTimer = new Timer();
|
|
||||||
|
|
||||||
private Handler mHandler = new Handler() {
|
private Handler mHandler = new Handler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(Message msg) {
|
public void handleMessage(Message msg) {
|
||||||
@ -66,33 +62,23 @@ public class BaseActivity extends Activity
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
Apg.initialize(this);
|
||||||
|
|
||||||
if (mPreferences == null) {
|
if (mPreferences == null) {
|
||||||
mPreferences = getPreferences(MODE_PRIVATE);
|
mPreferences = getPreferences(MODE_PRIVATE);
|
||||||
}
|
}
|
||||||
Apg.initialize(this);
|
|
||||||
if (mCacheTimer == null) {
|
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||||
setPassPhraseCacheTimer();
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setPassPhraseCacheTimer() {
|
Intent intent = new Intent(this, Service.class);
|
||||||
if (mCacheTimer != null) {
|
intent.putExtra(Service.EXTRA_TTL, getPassPhraseCacheTtl());
|
||||||
mCacheTimer.cancel();
|
startService(intent);
|
||||||
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());
|
|
||||||
}
|
|
||||||
}, 0, ttl * 1000 / 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -282,7 +268,7 @@ public class BaseActivity extends Activity
|
|||||||
case Id.request.secret_keys: {
|
case Id.request.secret_keys: {
|
||||||
if (resultCode == RESULT_OK) {
|
if (resultCode == RESULT_OK) {
|
||||||
Bundle bundle = data.getExtras();
|
Bundle bundle = data.getExtras();
|
||||||
setSecretKeyId(bundle.getLong("selectedKeyId"));
|
setSecretKeyId(bundle.getLong(Apg.EXTRA_KEY_ID));
|
||||||
} else {
|
} else {
|
||||||
setSecretKeyId(Id.key.none);
|
setSecretKeyId(Id.key.none);
|
||||||
}
|
}
|
||||||
@ -304,9 +290,9 @@ public class BaseActivity extends Activity
|
|||||||
public void setProgress(int progress, int max) {
|
public void setProgress(int progress, int max) {
|
||||||
Message msg = new Message();
|
Message msg = new Message();
|
||||||
Bundle data = new Bundle();
|
Bundle data = new Bundle();
|
||||||
data.putInt("type", Id.message.progress_update);
|
data.putInt(Apg.EXTRA_STATUS, Id.message.progress_update);
|
||||||
data.putInt("progress", progress);
|
data.putInt(Apg.EXTRA_PROGRESS, progress);
|
||||||
data.putInt("max", max);
|
data.putInt(Apg.EXTRA_MAX, max);
|
||||||
msg.setData(data);
|
msg.setData(data);
|
||||||
mHandler.sendMessage(msg);
|
mHandler.sendMessage(msg);
|
||||||
}
|
}
|
||||||
@ -314,10 +300,10 @@ public class BaseActivity extends Activity
|
|||||||
public void setProgress(String message, int progress, int max) {
|
public void setProgress(String message, int progress, int max) {
|
||||||
Message msg = new Message();
|
Message msg = new Message();
|
||||||
Bundle data = new Bundle();
|
Bundle data = new Bundle();
|
||||||
data.putInt("type", Id.message.progress_update);
|
data.putInt(Apg.EXTRA_STATUS, Id.message.progress_update);
|
||||||
data.putString("message", message);
|
data.putString(Apg.EXTRA_MESSAGE, message);
|
||||||
data.putInt("progress", progress);
|
data.putInt(Apg.EXTRA_PROGRESS, progress);
|
||||||
data.putInt("max", max);
|
data.putInt(Apg.EXTRA_MAX, max);
|
||||||
msg.setData(data);
|
msg.setData(data);
|
||||||
mHandler.sendMessage(msg);
|
mHandler.sendMessage(msg);
|
||||||
}
|
}
|
||||||
@ -328,16 +314,16 @@ public class BaseActivity extends Activity
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int type = data.getInt("type");
|
int type = data.getInt(Apg.EXTRA_STATUS);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Id.message.progress_update: {
|
case Id.message.progress_update: {
|
||||||
String message = data.getString("message");
|
String message = data.getString(Apg.EXTRA_MESSAGE);
|
||||||
if (mProgressDialog != null) {
|
if (mProgressDialog != null) {
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
mProgressDialog.setMessage(message);
|
mProgressDialog.setMessage(message);
|
||||||
}
|
}
|
||||||
mProgressDialog.setMax(data.getInt("max"));
|
mProgressDialog.setMax(data.getInt(Apg.EXTRA_MAX));
|
||||||
mProgressDialog.setProgress(data.getInt("progress"));
|
mProgressDialog.setProgress(data.getInt(Apg.EXTRA_PROGRESS));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -382,7 +368,14 @@ public class BaseActivity extends Activity
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getPassPhraseCacheTtl() {
|
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) {
|
public void setPassPhraseCacheTtl(int value) {
|
||||||
@ -390,7 +383,9 @@ public class BaseActivity extends Activity
|
|||||||
editor.putInt(Constants.pref.pass_phrase_cache_ttl, value);
|
editor.putInt(Constants.pref.pass_phrase_cache_ttl, value);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
|
|
||||||
setPassPhraseCacheTimer();
|
Intent intent = new Intent(this, Service.class);
|
||||||
|
intent.putExtra(Service.EXTRA_TTL, value);
|
||||||
|
startService(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDefaultEncryptionAlgorithm() {
|
public int getDefaultEncryptionAlgorithm() {
|
||||||
@ -417,7 +412,7 @@ public class BaseActivity extends Activity
|
|||||||
|
|
||||||
public int getDefaultMessageCompression() {
|
public int getDefaultMessageCompression() {
|
||||||
return mPreferences.getInt(Constants.pref.default_message_compression,
|
return mPreferences.getInt(Constants.pref.default_message_compression,
|
||||||
CompressionAlgorithmTags.ZLIB);
|
Id.choice.compression.zlib);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefaultMessageCompression(int value) {
|
public void setDefaultMessageCompression(int value) {
|
||||||
@ -428,7 +423,7 @@ public class BaseActivity extends Activity
|
|||||||
|
|
||||||
public int getDefaultFileCompression() {
|
public int getDefaultFileCompression() {
|
||||||
return mPreferences.getInt(Constants.pref.default_file_compression,
|
return mPreferences.getInt(Constants.pref.default_file_compression,
|
||||||
CompressionAlgorithmTags.ZLIB);
|
Id.choice.compression.none);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefaultFileCompression(int value) {
|
public void setDefaultFileCompression(int value) {
|
||||||
|
@ -10,6 +10,12 @@ public class CachedPassPhrase {
|
|||||||
this.passPhrase = passPhrase;
|
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) {
|
public boolean equals(Object other) {
|
||||||
if (!(other instanceof CachedPassPhrase)) {
|
if (!(other instanceof CachedPassPhrase)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -31,8 +31,6 @@ import java.util.regex.Matcher;
|
|||||||
|
|
||||||
import org.bouncycastle2.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle2.jce.provider.BouncyCastleProvider;
|
||||||
import org.bouncycastle2.openpgp.PGPException;
|
import org.bouncycastle2.openpgp.PGPException;
|
||||||
import org.bouncycastle2.util.Strings;
|
|
||||||
import org.openintents.intents.FileManager;
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
@ -57,6 +55,9 @@ import android.widget.ViewFlipper;
|
|||||||
public class DecryptActivity extends BaseActivity {
|
public class DecryptActivity extends BaseActivity {
|
||||||
private long mSignatureKeyId = 0;
|
private long mSignatureKeyId = 0;
|
||||||
|
|
||||||
|
private Intent mIntent;
|
||||||
|
|
||||||
|
private boolean mReturnResult = false;
|
||||||
private String mReplyTo = null;
|
private String mReplyTo = null;
|
||||||
private String mSubject = null;
|
private String mSubject = null;
|
||||||
private boolean mSignedOnly = false;
|
private boolean mSignedOnly = false;
|
||||||
@ -158,9 +159,9 @@ public class DecryptActivity extends BaseActivity {
|
|||||||
mSource.showNext();
|
mSource.showNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent intent = getIntent();
|
mIntent = getIntent();
|
||||||
if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_VIEW)) {
|
if (Intent.ACTION_VIEW.equals(mIntent.getAction())) {
|
||||||
Uri uri = intent.getData();
|
Uri uri = mIntent.getData();
|
||||||
try {
|
try {
|
||||||
InputStream attachment = getContentResolver().openInputStream(uri);
|
InputStream attachment = getContentResolver().openInputStream(uri);
|
||||||
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
||||||
@ -170,15 +171,15 @@ public class DecryptActivity extends BaseActivity {
|
|||||||
byteOut.write(bytes, 0, length);
|
byteOut.write(bytes, 0, length);
|
||||||
}
|
}
|
||||||
byteOut.close();
|
byteOut.close();
|
||||||
String data = Strings.fromUTF8ByteArray(byteOut.toByteArray());
|
String data = new String(byteOut.toByteArray());
|
||||||
mMessage.setText(data);
|
mMessage.setText(data);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
// ignore, then
|
// ignore, then
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// ignore, then
|
// ignore, then
|
||||||
}
|
}
|
||||||
} else if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_SEND)) {
|
} else if (Intent.ACTION_SEND.equals(mIntent.getAction())) {
|
||||||
Bundle extras = intent.getExtras();
|
Bundle extras = mIntent.getExtras();
|
||||||
if (extras == null) {
|
if (extras == null) {
|
||||||
extras = new Bundle();
|
extras = new Bundle();
|
||||||
}
|
}
|
||||||
@ -187,15 +188,15 @@ public class DecryptActivity extends BaseActivity {
|
|||||||
mMessage.setText(data);
|
mMessage.setText(data);
|
||||||
}
|
}
|
||||||
mSubject = extras.getString(Intent.EXTRA_SUBJECT);
|
mSubject = extras.getString(Intent.EXTRA_SUBJECT);
|
||||||
if (mSubject.startsWith("Fwd: ")) {
|
if (mSubject != null && mSubject.startsWith("Fwd: ")) {
|
||||||
mSubject = mSubject.substring(5);
|
mSubject = mSubject.substring(5);
|
||||||
}
|
}
|
||||||
} else if (intent.getAction() != null && intent.getAction().equals(Apg.Intent.DECRYPT)) {
|
} else if (Apg.Intent.DECRYPT.equals(mIntent.getAction())) {
|
||||||
Bundle extras = intent.getExtras();
|
Bundle extras = mIntent.getExtras();
|
||||||
if (extras == null) {
|
if (extras == null) {
|
||||||
extras = new Bundle();
|
extras = new Bundle();
|
||||||
}
|
}
|
||||||
String data = extras.getString("data");
|
String data = extras.getString(Apg.EXTRA_DATA);
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
Matcher matcher = Apg.PGP_MESSAGE.matcher(data);
|
Matcher matcher = Apg.PGP_MESSAGE.matcher(data);
|
||||||
if (matcher.matches()) {
|
if (matcher.matches()) {
|
||||||
@ -214,14 +215,39 @@ public class DecryptActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mReplyTo = extras.getString("replyTo");
|
mReplyTo = extras.getString(Apg.EXTRA_REPLY_TO);
|
||||||
mSubject = extras.getString("subject");
|
mSubject = extras.getString(Apg.EXTRA_SUBJECT);
|
||||||
} else if (intent.getAction() != null && intent.getAction().equals(Apg.Intent.DECRYPT_FILE)) {
|
} else if (Apg.Intent.DECRYPT_FILE.equals(mIntent.getAction())) {
|
||||||
mSource.setInAnimation(null);
|
mSource.setInAnimation(null);
|
||||||
mSource.setOutAnimation(null);
|
mSource.setOutAnimation(null);
|
||||||
while (mSource.getCurrentView().getId() != R.id.sourceFile) {
|
while (mSource.getCurrentView().getId() != R.id.sourceFile) {
|
||||||
mSource.showNext();
|
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 &&
|
if (mSource.getCurrentView().getId() == R.id.sourceMessage &&
|
||||||
@ -256,29 +282,41 @@ public class DecryptActivity extends BaseActivity {
|
|||||||
});
|
});
|
||||||
mReplyButton.setVisibility(View.INVISIBLE);
|
mReplyButton.setVisibility(View.INVISIBLE);
|
||||||
|
|
||||||
|
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 &&
|
if (mSource.getCurrentView().getId() == R.id.sourceMessage &&
|
||||||
mMessage.getText().length() > 0) {
|
mMessage.getText().length() > 0) {
|
||||||
mDecryptButton.performClick();
|
mDecryptButton.performClick();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSource();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openFile() {
|
private void openFile() {
|
||||||
String filename = mFilename.getText().toString();
|
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.setData(Uri.parse("file://" + filename));
|
||||||
|
intent.setType("*/*");
|
||||||
intent.putExtra(FileManager.EXTRA_TITLE, getString(R.string.filemanager_titleDecrypt));
|
|
||||||
intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, R.string.filemanager_btnOpen);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
startActivityForResult(intent, Id.request.filename);
|
startActivityForResult(intent, Id.request.filename);
|
||||||
} catch (ActivityNotFoundException e) {
|
} catch (ActivityNotFoundException e) {
|
||||||
// No compatible file manager was found.
|
// 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
|
// look at the file/message again to check whether there's
|
||||||
// symmetric encryption data in there
|
// symmetric encryption data in there
|
||||||
if (mDecryptTarget == Id.target.file) {
|
if (mDecryptTarget == Id.target.file) {
|
||||||
((FileInputStream) in).reset();
|
in = new FileInputStream(mInputFilename);
|
||||||
} else {
|
} else {
|
||||||
((ByteArrayInputStream) in).reset();
|
in = new ByteArrayInputStream(mMessage.getText().toString().getBytes());
|
||||||
}
|
}
|
||||||
if (!Apg.hasSymmetricEncryption(this, in)) {
|
if (!Apg.hasSymmetricEncryption(this, in)) {
|
||||||
throw new Apg.GeneralException(getString(R.string.error_noKnownEncryptionFound));
|
throw new Apg.GeneralException(getString(R.string.error_noKnownEncryptionFound));
|
||||||
@ -396,9 +434,9 @@ public class DecryptActivity extends BaseActivity {
|
|||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
error = getString(R.string.error_fileNotFound);
|
error = getString(R.string.error_fileNotFound);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
error = e.getLocalizedMessage();
|
error = "" + e;
|
||||||
} catch (Apg.GeneralException e) {
|
} catch (Apg.GeneralException e) {
|
||||||
error = e.getLocalizedMessage();
|
error = "" + e;
|
||||||
}
|
}
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
Toast.makeText(this, getString(R.string.errorMessage, error),
|
Toast.makeText(this, getString(R.string.errorMessage, error),
|
||||||
@ -412,12 +450,11 @@ public class DecryptActivity extends BaseActivity {
|
|||||||
String data = mMessage.getText().toString();
|
String data = mMessage.getText().toString();
|
||||||
data = data.replaceAll("(?m)^", "> ");
|
data = data.replaceAll("(?m)^", "> ");
|
||||||
data = "\n\n" + data;
|
data = "\n\n" + data;
|
||||||
intent.putExtra("data", data);
|
intent.putExtra(Apg.EXTRA_DATA, data);
|
||||||
intent.putExtra("subject", "Re: " + mSubject);
|
intent.putExtra(Apg.EXTRA_SUBJECT, "Re: " + mSubject);
|
||||||
intent.putExtra("sendTo", mReplyTo);
|
intent.putExtra(Apg.EXTRA_SEND_TO, mReplyTo);
|
||||||
intent.putExtra("eyId", mSignatureKeyId);
|
intent.putExtra(Apg.EXTRA_SIGNATURE_KEY_ID, getSecretKeyId());
|
||||||
intent.putExtra("signatureKeyId", getSecretKeyId());
|
intent.putExtra(Apg.EXTRA_ENCRYPTION_KEY_IDS, new long[] { mSignatureKeyId });
|
||||||
intent.putExtra("encryptionKeyIds", new long[] { mSignatureKeyId });
|
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,25 +511,23 @@ public class DecryptActivity extends BaseActivity {
|
|||||||
|
|
||||||
out.close();
|
out.close();
|
||||||
if (mDecryptTarget == Id.target.message) {
|
if (mDecryptTarget == Id.target.message) {
|
||||||
data.putString("decryptedMessage",
|
data.putByteArray(Apg.EXTRA_DECRYPTED_MESSAGE,
|
||||||
Strings.fromUTF8ByteArray(((ByteArrayOutputStream)
|
((ByteArrayOutputStream) out).toByteArray());
|
||||||
out).toByteArray()));
|
|
||||||
}
|
}
|
||||||
} catch (PGPException e) {
|
} catch (PGPException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
} catch (SignatureException e) {
|
} catch (SignatureException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
e.printStackTrace();
|
|
||||||
} catch (Apg.GeneralException 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) {
|
if (error != null) {
|
||||||
data.putString("error", error);
|
data.putString(Apg.EXTRA_ERROR, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.setData(data);
|
msg.setData(data);
|
||||||
@ -509,20 +544,20 @@ public class DecryptActivity extends BaseActivity {
|
|||||||
mSignatureLayout.setVisibility(View.GONE);
|
mSignatureLayout.setVisibility(View.GONE);
|
||||||
mReplyButton.setVisibility(View.INVISIBLE);
|
mReplyButton.setVisibility(View.INVISIBLE);
|
||||||
|
|
||||||
String error = data.getString("error");
|
String error = data.getString(Apg.EXTRA_ERROR);
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
Toast.makeText(DecryptActivity.this,
|
Toast.makeText(DecryptActivity.this,
|
||||||
getString(R.string.errorMessage,
|
getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
|
||||||
data.getString("error")),
|
|
||||||
Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toast.makeText(this, R.string.decryptionSuccessful, Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, R.string.decryptionSuccessful, Toast.LENGTH_SHORT).show();
|
||||||
switch (mDecryptTarget) {
|
switch (mDecryptTarget) {
|
||||||
case Id.target.message: {
|
case Id.target.message: {
|
||||||
String decryptedMessage = data.getString("decryptedMessage");
|
String decryptedMessage =
|
||||||
|
new String(data.getByteArray(Apg.EXTRA_DECRYPTED_MESSAGE));
|
||||||
mMessage.setText(decryptedMessage);
|
mMessage.setText(decryptedMessage);
|
||||||
|
mMessage.setHorizontallyScrolling(false);
|
||||||
mReplyButton.setVisibility(View.VISIBLE);
|
mReplyButton.setVisibility(View.VISIBLE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -541,9 +576,9 @@ public class DecryptActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.getBoolean("signature")) {
|
if (data.getBoolean(Apg.EXTRA_SIGNATURE)) {
|
||||||
String userId = data.getString("signatureUserId");
|
String userId = data.getString(Apg.EXTRA_SIGNATURE_USER_ID);
|
||||||
mSignatureKeyId = data.getLong("signatureKeyId");
|
mSignatureKeyId = data.getLong(Apg.EXTRA_SIGNATURE_KEY_ID);
|
||||||
mUserIdRest.setText("id: " + Long.toHexString(mSignatureKeyId & 0xffffffffL));
|
mUserIdRest.setText("id: " + Long.toHexString(mSignatureKeyId & 0xffffffffL));
|
||||||
if (userId == null) {
|
if (userId == null) {
|
||||||
userId = getResources().getString(R.string.unknownUserId);
|
userId = getResources().getString(R.string.unknownUserId);
|
||||||
@ -555,15 +590,22 @@ public class DecryptActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
mUserId.setText(userId);
|
mUserId.setText(userId);
|
||||||
|
|
||||||
if (data.getBoolean("signatureSuccess")) {
|
if (data.getBoolean(Apg.EXTRA_SIGNATURE_SUCCESS)) {
|
||||||
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
|
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);
|
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
|
||||||
} else {
|
} else {
|
||||||
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
|
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
|
||||||
}
|
}
|
||||||
mSignatureLayout.setVisibility(View.VISIBLE);
|
mSignatureLayout.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mReturnResult) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.putExtras(data);
|
||||||
|
setResult(RESULT_OK, intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.thialfihar.android.apg;
|
package org.thialfihar.android.apg;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.NoSuchProviderException;
|
import java.security.NoSuchProviderException;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
@ -24,6 +25,7 @@ import java.util.Vector;
|
|||||||
import org.bouncycastle2.openpgp.PGPException;
|
import org.bouncycastle2.openpgp.PGPException;
|
||||||
import org.bouncycastle2.openpgp.PGPSecretKey;
|
import org.bouncycastle2.openpgp.PGPSecretKey;
|
||||||
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
|
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.KeyEditor;
|
||||||
import org.thialfihar.android.apg.ui.widget.SectionView;
|
import org.thialfihar.android.apg.ui.widget.SectionView;
|
||||||
import org.thialfihar.android.apg.utils.IterableIterator;
|
import org.thialfihar.android.apg.utils.IterableIterator;
|
||||||
@ -69,7 +71,7 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
|
|||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
long keyId = 0;
|
long keyId = 0;
|
||||||
if (intent.getExtras() != null) {
|
if (intent.getExtras() != null) {
|
||||||
keyId = intent.getExtras().getLong("keyId");
|
keyId = intent.getExtras().getLong(Apg.EXTRA_KEY_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyId != 0) {
|
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();
|
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) {
|
if (mKeys.getEditors().getChildCount() == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -243,22 +245,27 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
|
|||||||
newPassPhrase = oldPassPhrase;
|
newPassPhrase = oldPassPhrase;
|
||||||
}
|
}
|
||||||
Apg.buildSecretKey(this, mUserIds, mKeys, oldPassPhrase, newPassPhrase, this);
|
Apg.buildSecretKey(this, mUserIds, mKeys, oldPassPhrase, newPassPhrase, this);
|
||||||
|
Apg.setCachedPassPhrase(getMasterKeyId(), newPassPhrase);
|
||||||
} catch (NoSuchProviderException e) {
|
} catch (NoSuchProviderException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
} catch (PGPException e) {
|
} catch (PGPException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
} catch (SignatureException e) {
|
} catch (SignatureException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
} catch (Apg.GeneralException 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) {
|
if (error != null) {
|
||||||
data.putString("error", error);
|
data.putString(Apg.EXTRA_ERROR, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.setData(data);
|
msg.setData(data);
|
||||||
@ -272,11 +279,10 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
|
|||||||
Bundle data = msg.getData();
|
Bundle data = msg.getData();
|
||||||
removeDialog(Id.dialog.saving);
|
removeDialog(Id.dialog.saving);
|
||||||
|
|
||||||
String error = data.getString("error");
|
String error = data.getString(Apg.EXTRA_ERROR);
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
Toast.makeText(EditKeyActivity.this,
|
Toast.makeText(EditKeyActivity.this,
|
||||||
getString(R.string.errorMessage, data.getString("error")),
|
getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
|
||||||
Toast.LENGTH_SHORT).show();
|
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(EditKeyActivity.this, R.string.keySaved, Toast.LENGTH_SHORT).show();
|
Toast.makeText(EditKeyActivity.this, R.string.keySaved, Toast.LENGTH_SHORT).show();
|
||||||
setResult(RESULT_OK);
|
setResult(RESULT_OK);
|
||||||
|
@ -35,7 +35,6 @@ import org.bouncycastle2.openpgp.PGPPublicKeyRing;
|
|||||||
import org.bouncycastle2.openpgp.PGPSecretKey;
|
import org.bouncycastle2.openpgp.PGPSecretKey;
|
||||||
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle2.util.Strings;
|
import org.bouncycastle2.util.Strings;
|
||||||
import org.openintents.intents.FileManager;
|
|
||||||
import org.thialfihar.android.apg.Apg.GeneralException;
|
import org.thialfihar.android.apg.Apg.GeneralException;
|
||||||
import org.thialfihar.android.apg.utils.Choice;
|
import org.thialfihar.android.apg.utils.Choice;
|
||||||
|
|
||||||
@ -62,11 +61,13 @@ import android.widget.Toast;
|
|||||||
import android.widget.ViewFlipper;
|
import android.widget.ViewFlipper;
|
||||||
|
|
||||||
public class EncryptActivity extends BaseActivity {
|
public class EncryptActivity extends BaseActivity {
|
||||||
|
private Intent mIntent = null;
|
||||||
private String mSubject = null;
|
private String mSubject = null;
|
||||||
private String mSendTo = null;
|
private String mSendTo = null;
|
||||||
|
|
||||||
private long mEncryptionKeyIds[] = null;
|
private long mEncryptionKeyIds[] = null;
|
||||||
|
|
||||||
|
private boolean mReturnResult = false;
|
||||||
private EditText mMessage = null;
|
private EditText mMessage = null;
|
||||||
private Button mSelectKeysButton = null;
|
private Button mSelectKeysButton = null;
|
||||||
private Button mEncryptButton = null;
|
private Button mEncryptButton = null;
|
||||||
@ -265,21 +266,26 @@ public class EncryptActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Intent intent = getIntent();
|
mIntent = getIntent();
|
||||||
if (intent.getAction() != null &&
|
if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) ||
|
||||||
(intent.getAction().equals(Apg.Intent.ENCRYPT) ||
|
Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction()) ||
|
||||||
intent.getAction().equals(Apg.Intent.ENCRYPT_FILE))) {
|
Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) {
|
||||||
Bundle extras = intent.getExtras();
|
Bundle extras = mIntent.getExtras();
|
||||||
if (extras == null) {
|
if (extras == null) {
|
||||||
extras = new Bundle();
|
extras = new Bundle();
|
||||||
}
|
}
|
||||||
String data = extras.getString("data");
|
|
||||||
mSendTo = extras.getString("sendTo");
|
if (Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) {
|
||||||
mSubject = extras.getString("subject");
|
mReturnResult = true;
|
||||||
long signatureKeyId = extras.getLong("signatureKeyId");
|
}
|
||||||
long encryptionKeyIds[] = extras.getLongArray("encryptionKeyIds");
|
|
||||||
|
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) {
|
if (signatureKeyId != 0) {
|
||||||
PGPSecretKeyRing keyRing = Apg.findSecretKeyRing(signatureKeyId);
|
PGPSecretKeyRing keyRing = Apg.getSecretKeyRing(signatureKeyId);
|
||||||
PGPSecretKey masterKey = null;
|
PGPSecretKey masterKey = null;
|
||||||
if (keyRing != null) {
|
if (keyRing != null) {
|
||||||
masterKey = Apg.getMasterKey(keyRing);
|
masterKey = Apg.getMasterKey(keyRing);
|
||||||
@ -295,7 +301,7 @@ public class EncryptActivity extends BaseActivity {
|
|||||||
if (encryptionKeyIds != null) {
|
if (encryptionKeyIds != null) {
|
||||||
Vector<Long> goodIds = new Vector<Long>();
|
Vector<Long> goodIds = new Vector<Long>();
|
||||||
for (int i = 0; i < encryptionKeyIds.length; ++i) {
|
for (int i = 0; i < encryptionKeyIds.length; ++i) {
|
||||||
PGPPublicKeyRing keyRing = Apg.findPublicKeyRing(encryptionKeyIds[i]);
|
PGPPublicKeyRing keyRing = Apg.getPublicKeyRing(encryptionKeyIds[i]);
|
||||||
PGPPublicKey masterKey = null;
|
PGPPublicKey masterKey = null;
|
||||||
if (keyRing == null) {
|
if (keyRing == null) {
|
||||||
continue;
|
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) {
|
if (data != null) {
|
||||||
mMessage.setText(data);
|
mMessage.setText(data);
|
||||||
}
|
}
|
||||||
@ -327,7 +334,7 @@ public class EncryptActivity extends BaseActivity {
|
|||||||
while (mSource.getCurrentView().getId() != R.id.sourceMessage) {
|
while (mSource.getCurrentView().getId() != R.id.sourceMessage) {
|
||||||
mSource.showNext();
|
mSource.showNext();
|
||||||
}
|
}
|
||||||
} else if (intent.getAction().equals(Apg.Intent.ENCRYPT_FILE)) {
|
} else if (Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction())) {
|
||||||
mSource.setInAnimation(null);
|
mSource.setInAnimation(null);
|
||||||
mSource.setOutAnimation(null);
|
mSource.setOutAnimation(null);
|
||||||
while (mSource.getCurrentView().getId() != R.id.sourceFile) {
|
while (mSource.getCurrentView().getId() != R.id.sourceFile) {
|
||||||
@ -339,23 +346,47 @@ public class EncryptActivity extends BaseActivity {
|
|||||||
updateView();
|
updateView();
|
||||||
updateSource();
|
updateSource();
|
||||||
updateMode();
|
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() {
|
private void openFile() {
|
||||||
String filename = mFilename.getText().toString();
|
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.setData(Uri.parse("file://" + filename));
|
||||||
|
intent.setType("*/*");
|
||||||
intent.putExtra(FileManager.EXTRA_TITLE, R.string.filemanager_titleEncrypt);
|
|
||||||
intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, R.string.filemanager_btnOpen);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
startActivityForResult(intent, Id.request.filename);
|
startActivityForResult(intent, Id.request.filename);
|
||||||
} catch (ActivityNotFoundException e) {
|
} catch (ActivityNotFoundException e) {
|
||||||
// No compatible file manager was found.
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
// for now require at least one form of encryption for files
|
||||||
if (!encryptIt && mEncryptTarget == Id.target.file) {
|
if (!encryptIt && mEncryptTarget == Id.target.file) {
|
||||||
Toast.makeText(this, R.string.selectEncryptionKey, Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, R.string.selectEncryptionKey, Toast.LENGTH_SHORT).show();
|
||||||
@ -527,7 +558,7 @@ public class EncryptActivity extends BaseActivity {
|
|||||||
} else {
|
} else {
|
||||||
encryptionKeyIds = mEncryptionKeyIds;
|
encryptionKeyIds = mEncryptionKeyIds;
|
||||||
signatureKeyId = getSecretKeyId();
|
signatureKeyId = getSecretKeyId();
|
||||||
signOnly = mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0;
|
signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mEncryptTarget == Id.target.file) {
|
if (mEncryptTarget == Id.target.file) {
|
||||||
@ -548,7 +579,7 @@ public class EncryptActivity extends BaseActivity {
|
|||||||
} else {
|
} else {
|
||||||
String message = mMessage.getText().toString();
|
String message = mMessage.getText().toString();
|
||||||
|
|
||||||
if (signOnly) {
|
if (signOnly && mReturnResult) {
|
||||||
// fix the message a bit, trailing spaces and newlines break stuff,
|
// fix the message a bit, trailing spaces and newlines break stuff,
|
||||||
// because GMail sends as HTML and such things fuck up the signature,
|
// because GMail sends as HTML and such things fuck up the signature,
|
||||||
// TODO: things like "<" and ">" also fuck up the signature
|
// TODO: things like "<" and ">" also fuck up the signature
|
||||||
@ -582,26 +613,27 @@ public class EncryptActivity extends BaseActivity {
|
|||||||
|
|
||||||
out.close();
|
out.close();
|
||||||
if (mEncryptTarget != Id.target.file) {
|
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) {
|
} catch (IOException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
} catch (PGPException e) {
|
} catch (PGPException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
} catch (NoSuchProviderException e) {
|
} catch (NoSuchProviderException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
} catch (SignatureException e) {
|
} catch (SignatureException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
} catch (Apg.GeneralException 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) {
|
if (error != null) {
|
||||||
data.putString("error", error);
|
data.putString(Apg.EXTRA_ERROR, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.setData(data);
|
msg.setData(data);
|
||||||
@ -645,7 +677,7 @@ public class EncryptActivity extends BaseActivity {
|
|||||||
|
|
||||||
private void selectPublicKeys() {
|
private void selectPublicKeys() {
|
||||||
Intent intent = new Intent(this, SelectPublicKeyListActivity.class);
|
Intent intent = new Intent(this, SelectPublicKeyListActivity.class);
|
||||||
intent.putExtra("selection", mEncryptionKeyIds);
|
intent.putExtra(Apg.EXTRA_SELECTION, mEncryptionKeyIds);
|
||||||
startActivityForResult(intent, Id.request.public_keys);
|
startActivityForResult(intent, Id.request.public_keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -702,7 +734,7 @@ public class EncryptActivity extends BaseActivity {
|
|||||||
case Id.request.public_keys: {
|
case Id.request.public_keys: {
|
||||||
if (resultCode == RESULT_OK) {
|
if (resultCode == RESULT_OK) {
|
||||||
Bundle bundle = data.getExtras();
|
Bundle bundle = data.getExtras();
|
||||||
mEncryptionKeyIds = bundle.getLongArray("selection");
|
mEncryptionKeyIds = bundle.getLongArray(Apg.EXTRA_SELECTION);
|
||||||
}
|
}
|
||||||
updateView();
|
updateView();
|
||||||
break;
|
break;
|
||||||
@ -723,16 +755,15 @@ public class EncryptActivity extends BaseActivity {
|
|||||||
removeDialog(Id.dialog.encrypting);
|
removeDialog(Id.dialog.encrypting);
|
||||||
|
|
||||||
Bundle data = msg.getData();
|
Bundle data = msg.getData();
|
||||||
String error = data.getString("error");
|
String error = data.getString(Apg.EXTRA_ERROR);
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
Toast.makeText(EncryptActivity.this,
|
Toast.makeText(EncryptActivity.this,
|
||||||
getString(R.string.errorMessage, data.getString("error")),
|
getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
|
||||||
Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
return;
|
||||||
} else {
|
}
|
||||||
String message = data.getString("message");
|
|
||||||
switch (mEncryptTarget) {
|
switch (mEncryptTarget) {
|
||||||
case Id.target.clipboard: {
|
case Id.target.clipboard: {
|
||||||
|
String message = new String(data.getByteArray(Apg.EXTRA_ENCRYPTED_MESSAGE));
|
||||||
ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
||||||
clip.setText(message);
|
clip.setText(message);
|
||||||
Toast.makeText(this, R.string.encryptionToClipboardSuccessful,
|
Toast.makeText(this, R.string.encryptionToClipboardSuccessful,
|
||||||
@ -741,6 +772,15 @@ public class EncryptActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Id.target.email: {
|
case Id.target.email: {
|
||||||
|
if (mReturnResult) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.putExtras(data);
|
||||||
|
setResult(RESULT_OK, intent);
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String message = new String(data.getByteArray(Apg.EXTRA_ENCRYPTED_MESSAGE));
|
||||||
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
|
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
|
||||||
emailIntent.setType("text/plain; charset=utf-8");
|
emailIntent.setType("text/plain; charset=utf-8");
|
||||||
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
|
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
|
||||||
@ -755,6 +795,7 @@ public class EncryptActivity extends BaseActivity {
|
|||||||
EncryptActivity.this.
|
EncryptActivity.this.
|
||||||
startActivity(Intent.createChooser(emailIntent,
|
startActivity(Intent.createChooser(emailIntent,
|
||||||
getString(R.string.title_sendEmail)));
|
getString(R.string.title_sendEmail)));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Id.target.file: {
|
case Id.target.file: {
|
||||||
@ -772,7 +813,6 @@ public class EncryptActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dialog onCreateDialog(int id) {
|
protected Dialog onCreateDialog(int id) {
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package org.thialfihar.android.apg;
|
package org.thialfihar.android.apg;
|
||||||
|
|
||||||
import org.openintents.intents.FileManager;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
@ -48,6 +46,8 @@ public class FileDialog {
|
|||||||
String defaultFile, OnClickListener onClickListener,
|
String defaultFile, OnClickListener onClickListener,
|
||||||
String fileManagerTitle, String fileManagerButton,
|
String fileManagerTitle, String fileManagerButton,
|
||||||
int requestCode) {
|
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 inflater =
|
||||||
(LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
(LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
|
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
|
||||||
@ -102,18 +102,17 @@ public class FileDialog {
|
|||||||
private static void openFile() {
|
private static void openFile() {
|
||||||
String filename = mFilename.getText().toString();
|
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.setData(Uri.parse("file://" + filename));
|
||||||
|
intent.setType("*/*");
|
||||||
intent.putExtra(FileManager.EXTRA_TITLE, mFileManagerTitle);
|
|
||||||
intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, mFileManagerButton);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mActivity.startActivityForResult(intent, mRequestCode);
|
mActivity.startActivityForResult(intent, mRequestCode);
|
||||||
} catch (ActivityNotFoundException e) {
|
} catch (ActivityNotFoundException e) {
|
||||||
// No compatible file manager was found.
|
// 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 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 class type {
|
||||||
public static final int public_key = 0x21070001;
|
public static final int public_key = 0x21070001;
|
||||||
public static final int secret_key = 0x21070002;
|
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>();
|
mconversations = new Vector<Conversation>();
|
||||||
mmessages = new Vector<Message>();
|
mmessages = new Vector<Message>();
|
||||||
|
|
||||||
String account = getIntent().getExtras().getString("account");
|
String account = getIntent().getExtras().getString(Apg.EXTRA_ACCOUNT);
|
||||||
// TODO: what if account is null?
|
// TODO: what if account is null?
|
||||||
Uri uri = Uri.parse("content://gmail-ls/conversations/" + account);
|
Uri uri = Uri.parse("content://gmail-ls/conversations/" + account);
|
||||||
Cursor cursor =
|
Cursor cursor =
|
||||||
@ -153,9 +153,9 @@ public class MailListActivity extends ListActivity {
|
|||||||
Intent intent = new Intent(MailListActivity.this, DecryptActivity.class);
|
Intent intent = new Intent(MailListActivity.this, DecryptActivity.class);
|
||||||
intent.setAction(Apg.Intent.DECRYPT);
|
intent.setAction(Apg.Intent.DECRYPT);
|
||||||
Message message = (Message) ((MailboxAdapter) getListAdapter()).getItem(position);
|
Message message = (Message) ((MailboxAdapter) getListAdapter()).getItem(position);
|
||||||
intent.putExtra("data", message.data);
|
intent.putExtra(Apg.EXTRA_DATA, message.data);
|
||||||
intent.putExtra("subject", message.subject);
|
intent.putExtra(Apg.EXTRA_SUBJECT, message.subject);
|
||||||
intent.putExtra("replyTo", message.replyTo);
|
intent.putExtra(Apg.EXTRA_REPLY_TO, message.replyTo);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -47,6 +47,8 @@ import android.widget.AdapterView.OnItemClickListener;
|
|||||||
|
|
||||||
public class MainActivity extends BaseActivity {
|
public class MainActivity extends BaseActivity {
|
||||||
private ListView mAccounts = null;
|
private ListView mAccounts = null;
|
||||||
|
private AccountListAdapter mListAdapter = null;
|
||||||
|
private Cursor mAccountCursor;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
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() {
|
mAccounts.setOnItemClickListener(new OnItemClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onItemClick(AdapterView<?> arg0, View view, int index, long id) {
|
public void onItemClick(AdapterView<?> arg0, View view, int index, long id) {
|
||||||
Cursor cursor =
|
String accountName = (String) mAccounts.getItemAtPosition(index);
|
||||||
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)
|
startActivity(new Intent(MainActivity.this, MailListActivity.class)
|
||||||
.putExtra("account", accountName));
|
.putExtra(Apg.EXTRA_ACCOUNT, accountName));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
registerForContextMenu(mAccounts);
|
registerForContextMenu(mAccounts);
|
||||||
@ -154,9 +156,10 @@ public class MainActivity extends BaseActivity {
|
|||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(Accounts.NAME, accountName);
|
values.put(Accounts.NAME, accountName);
|
||||||
try {
|
try {
|
||||||
MainActivity.this.getContentResolver()
|
Apg.getDatabase().db().insert(Accounts.TABLE_NAME,
|
||||||
.insert(Accounts.CONTENT_URI,
|
Accounts.NAME, values);
|
||||||
values);
|
mAccountCursor.requery();
|
||||||
|
mListAdapter.notifyDataSetChanged();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
Toast.makeText(MainActivity.this,
|
Toast.makeText(MainActivity.this,
|
||||||
getString(R.string.errorMessage,
|
getString(R.string.errorMessage,
|
||||||
@ -188,6 +191,12 @@ public class MainActivity extends BaseActivity {
|
|||||||
|
|
||||||
message.setText("Read the warnings!\n\n" +
|
message.setText("Read the warnings!\n\n" +
|
||||||
"Changes:\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" +
|
"\n" +
|
||||||
"WARNING: be careful editing your existing keys, as they " +
|
"WARNING: be careful editing your existing keys, as they " +
|
||||||
"WILL be stripped of certificates right now.\n" +
|
"WILL be stripped of certificates right now.\n" +
|
||||||
@ -277,8 +286,11 @@ public class MainActivity extends BaseActivity {
|
|||||||
|
|
||||||
switch (menuItem.getItemId()) {
|
switch (menuItem.getItemId()) {
|
||||||
case Id.menu.delete: {
|
case Id.menu.delete: {
|
||||||
Uri uri = Uri.withAppendedPath(Accounts.CONTENT_URI, "" + info.id);
|
Apg.getDatabase().db().delete(Accounts.TABLE_NAME,
|
||||||
this.getContentResolver().delete(uri, null, null);
|
Accounts._ID + " = ?",
|
||||||
|
new String[] { "" + info.id });
|
||||||
|
mAccountCursor.requery();
|
||||||
|
mListAdapter.notifyDataSetChanged();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,6 +309,13 @@ public class MainActivity extends BaseActivity {
|
|||||||
minflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
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
|
@Override
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
return super.getCount();
|
return super.getCount();
|
||||||
|
@ -50,7 +50,6 @@ public class PreferencesActivity extends BaseActivity {
|
|||||||
new Choice(180, getString(R.string.choice_3mins)),
|
new Choice(180, getString(R.string.choice_3mins)),
|
||||||
new Choice(300, getString(R.string.choice_5mins)),
|
new Choice(300, getString(R.string.choice_5mins)),
|
||||||
new Choice(600, getString(R.string.choice_10mins)),
|
new Choice(600, getString(R.string.choice_10mins)),
|
||||||
new Choice(0, getString(R.string.choice_untilQuit)),
|
|
||||||
};
|
};
|
||||||
ArrayAdapter<Choice> adapter =
|
ArrayAdapter<Choice> adapter =
|
||||||
new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices);
|
new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices);
|
||||||
|
@ -16,54 +16,19 @@
|
|||||||
|
|
||||||
package org.thialfihar.android.apg;
|
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.Bundle;
|
||||||
import android.os.Message;
|
|
||||||
import android.view.ContextMenu;
|
import android.view.ContextMenu;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.ContextMenu.ContextMenuInfo;
|
import android.view.ContextMenu.ContextMenuInfo;
|
||||||
import android.widget.BaseExpandableListAdapter;
|
|
||||||
import android.widget.ExpandableListView;
|
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
|
@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);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.key_list);
|
|
||||||
|
|
||||||
mList = (ExpandableListView) findViewById(R.id.list);
|
|
||||||
mList.setAdapter(new PublicKeyListAdapter(this));
|
|
||||||
registerForContextMenu(mList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -79,507 +44,17 @@ public class PublicKeyListActivity extends BaseActivity {
|
|||||||
return true;
|
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
|
@Override
|
||||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
||||||
super.onCreateContextMenu(menu, v, menuInfo);
|
super.onCreateContextMenu(menu, v, menuInfo);
|
||||||
ExpandableListView.ExpandableListContextMenuInfo info =
|
ExpandableListView.ExpandableListContextMenuInfo info =
|
||||||
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
|
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
|
||||||
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
|
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
|
||||||
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
|
|
||||||
|
|
||||||
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
|
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
|
||||||
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
|
// TODO: user id? menu.setHeaderTitle("Key");
|
||||||
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
|
|
||||||
menu.setHeaderTitle(userId);
|
|
||||||
menu.add(0, Id.menu.export, 0, R.string.menu_exportKey);
|
menu.add(0, Id.menu.export, 0, R.string.menu_exportKey);
|
||||||
menu.add(0, Id.menu.delete, 1, R.string.menu_deleteKey);
|
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;
|
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.app.Dialog;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Message;
|
|
||||||
import android.view.ContextMenu;
|
import android.view.ContextMenu;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.ContextMenu.ContextMenuInfo;
|
import android.view.ContextMenu.ContextMenuInfo;
|
||||||
import android.widget.BaseExpandableListAdapter;
|
|
||||||
import android.widget.ExpandableListView;
|
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.ExpandableListContextMenuInfo;
|
||||||
import android.widget.ExpandableListView.OnChildClickListener;
|
import android.widget.ExpandableListView.OnChildClickListener;
|
||||||
|
|
||||||
public class SecretKeyListActivity extends BaseActivity implements OnChildClickListener {
|
public class SecretKeyListActivity extends KeyListActivity 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";
|
|
||||||
|
|
||||||
@Override
|
@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);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.key_list);
|
|
||||||
|
|
||||||
mList = (ExpandableListView) findViewById(R.id.list);
|
|
||||||
mList.setAdapter(new SecretKeyListAdapter(this));
|
|
||||||
registerForContextMenu(mList);
|
|
||||||
mList.setOnChildClickListener(this);
|
mList.setOnChildClickListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,16 +55,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
|
|||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
switch (item.getItemId()) {
|
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: {
|
case Id.menu.option.create: {
|
||||||
createKey();
|
createKey();
|
||||||
return true;
|
return true;
|
||||||
@ -113,12 +72,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
|
|||||||
ExpandableListView.ExpandableListContextMenuInfo info =
|
ExpandableListView.ExpandableListContextMenuInfo info =
|
||||||
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
|
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
|
||||||
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
|
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
|
||||||
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
|
|
||||||
|
|
||||||
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
|
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
|
||||||
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
|
// TODO: user id? menu.setHeaderTitle("Key");
|
||||||
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
|
|
||||||
menu.setHeaderTitle(userId);
|
|
||||||
menu.add(0, Id.menu.edit, 0, R.string.menu_editKey);
|
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.export, 1, R.string.menu_exportKey);
|
||||||
menu.add(0, Id.menu.delete, 2, R.string.menu_deleteKey);
|
menu.add(0, Id.menu.delete, 2, R.string.menu_deleteKey);
|
||||||
@ -142,18 +98,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
|
|||||||
return true;
|
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: {
|
default: {
|
||||||
return super.onContextItemSelected(menuItem);
|
return super.onContextItemSelected(menuItem);
|
||||||
}
|
}
|
||||||
@ -170,96 +114,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dialog onCreateDialog(int id) {
|
protected Dialog onCreateDialog(int id) {
|
||||||
boolean singleKeyExport = false;
|
|
||||||
|
|
||||||
switch (id) {
|
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: {
|
case Id.dialog.pass_phrase: {
|
||||||
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
|
long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
|
||||||
long keyId = keyRing.getSecretKey().getKeyID();
|
|
||||||
return AskForSecretKeyPassPhrase.createDialog(this, keyId, this);
|
return AskForSecretKeyPassPhrase.createDialog(this, keyId, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,8 +127,7 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void checkPassPhraseAndEdit() {
|
public void checkPassPhraseAndEdit() {
|
||||||
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
|
long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
|
||||||
long keyId = keyRing.getSecretKey().getKeyID();
|
|
||||||
String passPhrase = Apg.getCachedPassPhrase(keyId);
|
String passPhrase = Apg.getCachedPassPhrase(keyId);
|
||||||
if (passPhrase == null) {
|
if (passPhrase == null) {
|
||||||
showDialog(Id.dialog.pass_phrase);
|
showDialog(Id.dialog.pass_phrase);
|
||||||
@ -295,10 +151,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void editKey() {
|
private void editKey() {
|
||||||
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
|
long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
|
||||||
long keyId = keyRing.getSecretKey().getKeyID();
|
|
||||||
Intent intent = new Intent(this, EditKeyActivity.class);
|
Intent intent = new Intent(this, EditKeyActivity.class);
|
||||||
intent.putExtra("keyId", keyId);
|
intent.putExtra(Apg.EXTRA_KEY_ID, keyId);
|
||||||
startActivityForResult(intent, Id.message.edit_key);
|
startActivityForResult(intent, Id.message.edit_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,24 +168,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
|
|||||||
break;
|
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: {
|
default: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -338,320 +175,4 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
|
|||||||
|
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
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;
|
package org.thialfihar.android.apg;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import org.bouncycastle2.openpgp.PGPPublicKey;
|
|
||||||
import org.bouncycastle2.openpgp.PGPPublicKeyRing;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -42,27 +38,21 @@ public class SelectPublicKeyListActivity extends BaseActivity {
|
|||||||
mIntent = getIntent();
|
mIntent = getIntent();
|
||||||
long selectedKeyIds[] = null;
|
long selectedKeyIds[] = null;
|
||||||
if (mIntent.getExtras() != null) {
|
if (mIntent.getExtras() != null) {
|
||||||
selectedKeyIds = mIntent.getExtras().getLongArray("selection");
|
selectedKeyIds = mIntent.getExtras().getLongArray(Apg.EXTRA_SELECTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
mList = (ListView) findViewById(R.id.list);
|
mList = (ListView) findViewById(R.id.list);
|
||||||
// needed in Android 1.5, where the XML attribute gets ignored
|
// needed in Android 1.5, where the XML attribute gets ignored
|
||||||
mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||||
|
|
||||||
Vector<PGPPublicKeyRing> keyRings =
|
SelectPublicKeyListAdapter adapter = new SelectPublicKeyListAdapter(this, mList);
|
||||||
(Vector<PGPPublicKeyRing>) Apg.getPublicKeyRings().clone();
|
mList.setAdapter(adapter);
|
||||||
Collections.sort(keyRings, new Apg.PublicKeySorter());
|
|
||||||
mList.setAdapter(new SelectPublicKeyListAdapter(mList, keyRings));
|
|
||||||
|
|
||||||
if (selectedKeyIds != null) {
|
if (selectedKeyIds != null) {
|
||||||
for (int i = 0; i < keyRings.size(); ++i) {
|
for (int i = 0; i < adapter.getCount(); ++i) {
|
||||||
PGPPublicKeyRing keyRing = keyRings.get(i);
|
long keyId = adapter.getItemId(i);
|
||||||
PGPPublicKey key = Apg.getMasterKey(keyRing);
|
|
||||||
if (key == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (int j = 0; j < selectedKeyIds.length; ++j) {
|
for (int j = 0; j < selectedKeyIds.length; ++j) {
|
||||||
if (key.getKeyID() == selectedKeyIds[j]) {
|
if (keyId == selectedKeyIds[j]) {
|
||||||
mList.setItemChecked(i, true);
|
mList.setItemChecked(i, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -106,7 +96,7 @@ public class SelectPublicKeyListActivity extends BaseActivity {
|
|||||||
for (int i = 0; i < vector.size(); ++i) {
|
for (int i = 0; i < vector.size(); ++i) {
|
||||||
selectedKeyIds[i] = vector.get(i);
|
selectedKeyIds[i] = vector.get(i);
|
||||||
}
|
}
|
||||||
data.putExtra("selection", selectedKeyIds);
|
data.putExtra(Apg.EXTRA_SELECTION, selectedKeyIds);
|
||||||
setResult(RESULT_OK, data);
|
setResult(RESULT_OK, data);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
@ -16,15 +16,16 @@
|
|||||||
|
|
||||||
package org.thialfihar.android.apg;
|
package org.thialfihar.android.apg;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import org.bouncycastle2.openpgp.PGPPublicKey;
|
import org.thialfihar.android.apg.provider.KeyRings;
|
||||||
import org.bouncycastle2.openpgp.PGPPublicKeyRing;
|
import org.thialfihar.android.apg.provider.Keys;
|
||||||
import org.thialfihar.android.apg.utils.IterableIterator;
|
import org.thialfihar.android.apg.provider.UserIds;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -34,40 +35,55 @@ import android.widget.ListView;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
public class SelectPublicKeyListAdapter extends BaseAdapter {
|
public class SelectPublicKeyListAdapter extends BaseAdapter {
|
||||||
protected Vector<PGPPublicKeyRing> mKeyRings;
|
|
||||||
protected LayoutInflater mInflater;
|
protected LayoutInflater mInflater;
|
||||||
protected ListView mParent;
|
protected ListView mParent;
|
||||||
|
protected SQLiteDatabase mDatabase;
|
||||||
|
protected Cursor mCursor;
|
||||||
|
|
||||||
public SelectPublicKeyListAdapter(ListView parent,
|
public SelectPublicKeyListAdapter(Activity activity, ListView parent) {
|
||||||
Vector<PGPPublicKeyRing> keyRings) {
|
|
||||||
setKeyRings(keyRings);
|
|
||||||
mParent = parent;
|
mParent = parent;
|
||||||
|
mDatabase = Apg.getDatabase().db();
|
||||||
mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
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) {
|
activity.startManagingCursor(mCursor);
|
||||||
mKeyRings = keyRings;
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector<PGPPublicKeyRing> getKeyRings() {
|
|
||||||
return mKeyRings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled(int position) {
|
public boolean isEnabled(int position) {
|
||||||
PGPPublicKeyRing keyRing = mKeyRings.get(position);
|
mCursor.moveToPosition(position);
|
||||||
|
return mCursor.getInt(4) > 0; // valid CAN_ENCRYPT
|
||||||
if (Apg.getMasterKey(keyRing) == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector<PGPPublicKey> encryptKeys = Apg.getUsableEncryptKeys(keyRing);
|
|
||||||
if (encryptKeys.size() == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -77,57 +93,37 @@ public class SelectPublicKeyListAdapter extends BaseAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
return mKeyRings.size();
|
return mCursor.getCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getItem(int position) {
|
public Object getItem(int position) {
|
||||||
return mKeyRings.get(position);
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getItemId(int position) {
|
public long getItemId(int position) {
|
||||||
PGPPublicKeyRing keyRing = mKeyRings.get(position);
|
mCursor.moveToPosition(position);
|
||||||
PGPPublicKey key = Apg.getMasterKey(keyRing);
|
return mCursor.getLong(1); // MASTER_KEY_ID
|
||||||
if (key != null) {
|
|
||||||
return key.getKeyID();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
mCursor.moveToPosition(position);
|
||||||
|
|
||||||
View view = mInflater.inflate(R.layout.select_public_key_item, null);
|
View view = mInflater.inflate(R.layout.select_public_key_item, null);
|
||||||
boolean enabled = isEnabled(position);
|
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);
|
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
|
||||||
mainUserId.setText(R.string.unknownUserId);
|
mainUserId.setText(R.string.unknownUserId);
|
||||||
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
|
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
|
||||||
mainUserIdRest.setText("");
|
mainUserIdRest.setText("");
|
||||||
TextView keyId = (TextView) view.findViewById(R.id.keyId);
|
TextView keyId = (TextView) view.findViewById(R.id.keyId);
|
||||||
keyId.setText(R.string.noKey);
|
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);
|
TextView status = (TextView) view.findViewById(R.id.status);
|
||||||
status.setText(R.string.unknownStatus);
|
status.setText(R.string.unknownStatus);
|
||||||
|
|
||||||
if (key != null) {
|
String userId = mCursor.getString(2); // USER_ID
|
||||||
String userId = Apg.getMainUserId(key);
|
|
||||||
if (userId != null) {
|
if (userId != null) {
|
||||||
String chunks[] = userId.split(" <", 2);
|
String chunks[] = userId.split(" <", 2);
|
||||||
userId = chunks[0];
|
userId = chunks[0];
|
||||||
@ -137,33 +133,22 @@ public class SelectPublicKeyListAdapter extends BaseAdapter {
|
|||||||
mainUserId.setText(userId);
|
mainUserId.setText(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
keyId.setText("" + Long.toHexString(key.getKeyID() & 0xffffffffL));
|
long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
|
||||||
}
|
keyId.setText("" + Long.toHexString(masterKeyId & 0xffffffffL));
|
||||||
|
|
||||||
if (mainUserIdRest.getText().length() == 0) {
|
if (mainUserIdRest.getText().length() == 0) {
|
||||||
mainUserIdRest.setVisibility(View.GONE);
|
mainUserIdRest.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
PGPPublicKey timespanKey = key;
|
if (enabled) {
|
||||||
if (usableKeys.size() > 0) {
|
|
||||||
timespanKey = usableKeys.get(0);
|
|
||||||
status.setText(R.string.canEncrypt);
|
status.setText(R.string.canEncrypt);
|
||||||
} else if (encryptKeys.size() > 0) {
|
|
||||||
timespanKey = encryptKeys.get(0);
|
|
||||||
Date now = new Date();
|
|
||||||
if (now.compareTo(Apg.getCreationDate(timespanKey)) > 0) {
|
|
||||||
status.setText(R.string.notValid);
|
|
||||||
} else {
|
} else {
|
||||||
|
if (mCursor.getInt(3) > 0) {
|
||||||
|
// has some CAN_ENCRYPT keys, but col(4) = 0, so must be revoked or expired
|
||||||
status.setText(R.string.expired);
|
status.setText(R.string.expired);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
status.setText(R.string.noKey);
|
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() + " ");
|
status.setText(status.getText() + " ");
|
||||||
@ -176,8 +161,6 @@ public class SelectPublicKeyListAdapter extends BaseAdapter {
|
|||||||
mainUserId.setEnabled(enabled);
|
mainUserId.setEnabled(enabled);
|
||||||
mainUserIdRest.setEnabled(enabled);
|
mainUserIdRest.setEnabled(enabled);
|
||||||
keyId.setEnabled(enabled);
|
keyId.setEnabled(enabled);
|
||||||
creation.setEnabled(enabled);
|
|
||||||
expiry.setEnabled(enabled);
|
|
||||||
selected.setEnabled(enabled);
|
selected.setEnabled(enabled);
|
||||||
status.setEnabled(enabled);
|
status.setEnabled(enabled);
|
||||||
|
|
||||||
|
@ -16,32 +16,16 @@
|
|||||||
|
|
||||||
package org.thialfihar.android.apg;
|
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.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.BaseAdapter;
|
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.AdapterView.OnItemClickListener;
|
import android.widget.AdapterView.OnItemClickListener;
|
||||||
|
|
||||||
public class SelectSecretKeyListActivity extends BaseActivity {
|
public class SelectSecretKeyListActivity extends BaseActivity {
|
||||||
protected Vector<PGPSecretKeyRing> mKeyRings;
|
|
||||||
protected LayoutInflater mInflater;
|
|
||||||
protected Intent mIntent;
|
|
||||||
protected ListView mList;
|
protected ListView mList;
|
||||||
|
protected SelectSecretKeyListAdapter mListAdapter;
|
||||||
|
|
||||||
protected long mSelectedKeyId = 0;
|
protected long mSelectedKeyId = 0;
|
||||||
|
|
||||||
@ -49,158 +33,20 @@ public class SelectSecretKeyListActivity extends BaseActivity {
|
|||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(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);
|
setContentView(R.layout.select_secret_key);
|
||||||
|
|
||||||
mList = (ListView) findViewById(R.id.list);
|
mList = (ListView) findViewById(R.id.list);
|
||||||
mList.setAdapter(new SecretKeyListAdapter(this));
|
mListAdapter = new SelectSecretKeyListAdapter(this, mList);
|
||||||
|
mList.setAdapter(mListAdapter);
|
||||||
|
|
||||||
mList.setOnItemClickListener(new OnItemClickListener() {
|
mList.setOnItemClickListener(new OnItemClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
|
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
|
||||||
Intent data = new Intent();
|
Intent data = new Intent();
|
||||||
data.putExtra("selectedKeyId", id);
|
data.putExtra(Apg.EXTRA_KEY_ID, id);
|
||||||
setResult(RESULT_OK, data);
|
setResult(RESULT_OK, data);
|
||||||
finish();
|
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;
|
package org.thialfihar.android.apg.provider;
|
||||||
|
|
||||||
public class Accounts extends Accounts1 {
|
import android.provider.BaseColumns;
|
||||||
private Accounts() {
|
|
||||||
}
|
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 java.util.HashMap;
|
||||||
|
|
||||||
|
import org.thialfihar.android.apg.Id;
|
||||||
|
|
||||||
import android.content.ContentProvider;
|
import android.content.ContentProvider;
|
||||||
import android.content.ContentUris;
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.UriMatcher;
|
import android.content.UriMatcher;
|
||||||
import android.database.Cursor;
|
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.database.sqlite.SQLiteQueryBuilder;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@ -34,153 +31,190 @@ import android.text.TextUtils;
|
|||||||
public class DataProvider extends ContentProvider {
|
public class DataProvider extends ContentProvider {
|
||||||
public static final String AUTHORITY = "org.thialfihar.android.apg.provider";
|
public static final String AUTHORITY = "org.thialfihar.android.apg.provider";
|
||||||
|
|
||||||
private static final String DATABASE_NAME = "apg";
|
private static final int PUBLIC_KEY_RINGS = 101;
|
||||||
private static final int DATABASE_VERSION = 1;
|
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 SECRET_KEY_RINGS = 201;
|
||||||
private static final int PUBLIC_KEY_ID = 102;
|
private static final int SECRET_KEY_RING_ID = 202;
|
||||||
private static final int PUBLIC_KEY_BY_KEY_ID = 103;
|
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 String PUBLIC_KEY_RING_CONTENT_DIR_TYPE =
|
||||||
private static final int SECRET_KEY_ID = 202;
|
"vnd.android.cursor.dir/vnd.thialfihar.apg.public.key_ring";
|
||||||
private static final int SECRET_KEY_BY_KEY_ID = 203;
|
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 String PUBLIC_KEY_CONTENT_DIR_TYPE =
|
||||||
private static final int ACCOUNT_ID = 302;
|
"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 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 {
|
static {
|
||||||
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
mUriMatcher.addURI(DataProvider.AUTHORITY, "public_keys", PUBLIC_KEYS);
|
mUriMatcher.addURI(AUTHORITY, "key_rings/public/key_id/*", PUBLIC_KEY_RING_BY_KEY_ID);
|
||||||
mUriMatcher.addURI(DataProvider.AUTHORITY, "public_keys/#", PUBLIC_KEY_ID);
|
|
||||||
mUriMatcher.addURI(DataProvider.AUTHORITY, "public_keys/key_id/*", PUBLIC_KEY_BY_KEY_ID);
|
|
||||||
|
|
||||||
mUriMatcher.addURI(DataProvider.AUTHORITY, "secret_keys", SECRET_KEYS);
|
mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys", PUBLIC_KEY_RING_KEYS);
|
||||||
mUriMatcher.addURI(DataProvider.AUTHORITY, "secret_keys/#", SECRET_KEY_ID);
|
mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys/#", PUBLIC_KEY_RING_KEY_RANK);
|
||||||
mUriMatcher.addURI(DataProvider.AUTHORITY, "secret_keys/key_id/*", SECRET_KEY_BY_KEY_ID);
|
|
||||||
|
|
||||||
mUriMatcher.addURI(DataProvider.AUTHORITY, "accounts", ACCOUNTS);
|
mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/user_ids", PUBLIC_KEY_RING_USER_IDS);
|
||||||
mUriMatcher.addURI(DataProvider.AUTHORITY, "accounts/#", ACCOUNT_ID);
|
mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/user_ids/#", PUBLIC_KEY_RING_USER_ID_RANK);
|
||||||
|
|
||||||
mPublicKeysProjectionMap = new HashMap<String, String>();
|
mUriMatcher.addURI(AUTHORITY, "key_rings/public", PUBLIC_KEY_RINGS);
|
||||||
mPublicKeysProjectionMap.put(PublicKeys._ID, PublicKeys._ID);
|
mUriMatcher.addURI(AUTHORITY, "key_rings/public/*", PUBLIC_KEY_RING_ID);
|
||||||
mPublicKeysProjectionMap.put(PublicKeys.KEY_ID, PublicKeys.KEY_ID);
|
|
||||||
mPublicKeysProjectionMap.put(PublicKeys.KEY_DATA, PublicKeys.KEY_DATA);
|
|
||||||
mPublicKeysProjectionMap.put(PublicKeys.WHO_ID, PublicKeys.WHO_ID);
|
|
||||||
|
|
||||||
mSecretKeysProjectionMap = new HashMap<String, String>();
|
mUriMatcher.addURI(AUTHORITY, "key_rings/secret/key_id/*", SECRET_KEY_RING_BY_KEY_ID);
|
||||||
mSecretKeysProjectionMap.put(PublicKeys._ID, PublicKeys._ID);
|
|
||||||
mSecretKeysProjectionMap.put(PublicKeys.KEY_ID, PublicKeys.KEY_ID);
|
|
||||||
mSecretKeysProjectionMap.put(PublicKeys.KEY_DATA, PublicKeys.KEY_DATA);
|
|
||||||
mSecretKeysProjectionMap.put(PublicKeys.WHO_ID, PublicKeys.WHO_ID);
|
|
||||||
|
|
||||||
mAccountsProjectionMap = new HashMap<String, String>();
|
mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys", SECRET_KEY_RING_KEYS);
|
||||||
mAccountsProjectionMap.put(Accounts._ID, Accounts._ID);
|
mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys/#", SECRET_KEY_RING_KEY_RANK);
|
||||||
mAccountsProjectionMap.put(Accounts.NAME, Accounts.NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/user_ids", SECRET_KEY_RING_USER_IDS);
|
||||||
* This class helps open, create, and upgrade the database file.
|
mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/user_ids/#", SECRET_KEY_RING_USER_ID_RANK);
|
||||||
*/
|
|
||||||
private static class DatabaseHelper extends SQLiteOpenHelper {
|
|
||||||
|
|
||||||
DatabaseHelper(Context context) {
|
mUriMatcher.addURI(AUTHORITY, "key_rings/secret", SECRET_KEY_RINGS);
|
||||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*", SECRET_KEY_RING_ID);
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
mdbHelper = new DatabaseHelper(getContext());
|
mDb = new Database(getContext());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Cursor query(Uri uri, String[] projection, String selection,
|
public Cursor query(Uri uri, String[] projection, String selection,
|
||||||
String[] selectionArgs, String sortOrder) {
|
String[] selectionArgs, String sortOrder) {
|
||||||
|
// TODO: implement the others, then use them for the lists
|
||||||
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case PUBLIC_KEY_ID: {
|
case PUBLIC_KEY_RING_ID:
|
||||||
qb.setTables(PublicKeys.TABLE_NAME);
|
case SECRET_KEY_RING_ID: {
|
||||||
qb.setProjectionMap(mPublicKeysProjectionMap);
|
qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
|
||||||
qb.appendWhere(PublicKeys._ID + "=" + uri.getPathSegments().get(1));
|
"(" + 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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case PUBLIC_KEY_BY_KEY_ID: {
|
case SECRET_KEY_RING_BY_KEY_ID:
|
||||||
qb.setTables(PublicKeys.TABLE_NAME);
|
case PUBLIC_KEY_RING_BY_KEY_ID: {
|
||||||
qb.setProjectionMap(mPublicKeysProjectionMap);
|
qb.setTables(Keys.TABLE_NAME + " AS tmp INNER JOIN " +
|
||||||
qb.appendWhere(PublicKeys.KEY_ID + "=" + uri.getPathSegments().get(2));
|
KeyRings.TABLE_NAME + " ON (" +
|
||||||
break;
|
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: {
|
projectionMap.put(MASTER_KEY_ID,
|
||||||
qb.setTables(SecretKeys.TABLE_NAME);
|
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
|
||||||
qb.setProjectionMap(mSecretKeysProjectionMap);
|
projectionMap.put(USER_ID,
|
||||||
break;
|
UserIds.TABLE_NAME + "." + UserIds.USER_ID);
|
||||||
}
|
|
||||||
|
|
||||||
case SECRET_KEY_ID: {
|
qb.appendWhere(" AND tmp." + Keys.KEY_ID + " = ");
|
||||||
qb.setTables(SecretKeys.TABLE_NAME);
|
qb.appendWhereEscapeString(uri.getPathSegments().get(3));
|
||||||
qb.setProjectionMap(mSecretKeysProjectionMap);
|
|
||||||
qb.appendWhere(SecretKeys._ID + "=" + uri.getPathSegments().get(1));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SECRET_KEY_BY_KEY_ID: {
|
|
||||||
qb.setTables(SecretKeys.TABLE_NAME);
|
|
||||||
qb.setProjectionMap(mSecretKeysProjectionMap);
|
|
||||||
qb.appendWhere(SecretKeys.KEY_ID + "=" + uri.getPathSegments().get(2));
|
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,20 +223,20 @@ public class DataProvider extends ContentProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qb.setProjectionMap(projectionMap);
|
||||||
|
|
||||||
// If no sort order is specified use the default
|
// If no sort order is specified use the default
|
||||||
String orderBy;
|
String orderBy;
|
||||||
if (TextUtils.isEmpty(sortOrder)) {
|
if (TextUtils.isEmpty(sortOrder)) {
|
||||||
orderBy = PublicKeys.DEFAULT_SORT_ORDER;
|
orderBy = null;
|
||||||
} else {
|
} else {
|
||||||
orderBy = sortOrder;
|
orderBy = sortOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the database and run the query
|
//System.out.println(qb.buildQuery(projection, selection, selectionArgs, null, null, sortOrder, null).replace("WHERE", "WHERE\n"));
|
||||||
SQLiteDatabase db = mdbHelper.getReadableDatabase();
|
Cursor c = qb.query(mDb.db(), projection, selection, selectionArgs, null, null, orderBy);
|
||||||
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
|
|
||||||
|
|
||||||
// Tell the cursor what uri to watch, so it knows when its source data
|
// Tell the cursor what uri to watch, so it knows when its source data changes
|
||||||
// changes
|
|
||||||
c.setNotificationUri(getContext().getContentResolver(), uri);
|
c.setNotificationUri(getContext().getContentResolver(), uri);
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
@ -210,278 +244,68 @@ public class DataProvider extends ContentProvider {
|
|||||||
@Override
|
@Override
|
||||||
public String getType(Uri uri) {
|
public String getType(Uri uri) {
|
||||||
switch (mUriMatcher.match(uri)) {
|
switch (mUriMatcher.match(uri)) {
|
||||||
case PUBLIC_KEYS: {
|
case PUBLIC_KEY_RINGS:
|
||||||
return PublicKeys.CONTENT_TYPE;
|
return PUBLIC_KEY_RING_CONTENT_DIR_TYPE;
|
||||||
}
|
|
||||||
|
|
||||||
case PUBLIC_KEY_ID: {
|
case PUBLIC_KEY_RING_ID:
|
||||||
return PublicKeys.CONTENT_ITEM_TYPE;
|
return PUBLIC_KEY_RING_CONTENT_ITEM_TYPE;
|
||||||
}
|
|
||||||
|
|
||||||
case PUBLIC_KEY_BY_KEY_ID: {
|
case PUBLIC_KEY_RING_BY_KEY_ID:
|
||||||
return PublicKeys.CONTENT_ITEM_TYPE;
|
return PUBLIC_KEY_RING_CONTENT_ITEM_TYPE;
|
||||||
}
|
|
||||||
|
|
||||||
case SECRET_KEYS: {
|
case PUBLIC_KEY_RING_KEYS:
|
||||||
return SecretKeys.CONTENT_TYPE;
|
return PUBLIC_KEY_CONTENT_DIR_TYPE;
|
||||||
}
|
|
||||||
|
|
||||||
case SECRET_KEY_ID: {
|
case PUBLIC_KEY_RING_KEY_RANK:
|
||||||
return SecretKeys.CONTENT_ITEM_TYPE;
|
return PUBLIC_KEY_CONTENT_ITEM_TYPE;
|
||||||
}
|
|
||||||
|
|
||||||
case SECRET_KEY_BY_KEY_ID: {
|
case PUBLIC_KEY_RING_USER_IDS:
|
||||||
return SecretKeys.CONTENT_ITEM_TYPE;
|
return USER_ID_CONTENT_DIR_TYPE;
|
||||||
}
|
|
||||||
|
|
||||||
case ACCOUNTS: {
|
case PUBLIC_KEY_RING_USER_ID_RANK:
|
||||||
return Accounts.CONTENT_TYPE;
|
return USER_ID_CONTENT_ITEM_TYPE;
|
||||||
}
|
|
||||||
|
|
||||||
case ACCOUNT_ID: {
|
case SECRET_KEY_RINGS:
|
||||||
return Accounts.CONTENT_ITEM_TYPE;
|
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);
|
throw new IllegalArgumentException("Unknown URI " + uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri insert(Uri uri, ContentValues initialValues) {
|
public Uri insert(Uri uri, ContentValues initialValues) {
|
||||||
switch (mUriMatcher.match(uri)) {
|
// not supported
|
||||||
case PUBLIC_KEYS: {
|
return null;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int delete(Uri uri, String where, String[] whereArgs) {
|
public int delete(Uri uri, String where, String[] whereArgs) {
|
||||||
SQLiteDatabase db = mdbHelper.getWritableDatabase();
|
// not supported
|
||||||
int count;
|
return 0;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
|
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
|
||||||
SQLiteDatabase db = mdbHelper.getWritableDatabase();
|
// not supported
|
||||||
int count;
|
return 0;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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;
|
package org.thialfihar.android.apg.provider;
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
|
|
||||||
class Accounts1 implements BaseColumns {
|
public class KeyRings implements BaseColumns {
|
||||||
public static final String TABLE_NAME = "accounts";
|
public static final String TABLE_NAME = "key_rings";
|
||||||
|
|
||||||
public static final String _ID_type = "INTEGER PRIMARY KEY";
|
public static final String _ID_type = "INTEGER PRIMARY KEY";
|
||||||
public static final String NAME = "c_name";
|
public static final String MASTER_KEY_ID = "c_master_key_id";
|
||||||
public static final String NAME_type = "TEXT";
|
public static final String MASTER_KEY_ID_type = "INT64";
|
||||||
|
public static final String TYPE = "c_type";
|
||||||
public static final Uri CONTENT_URI =
|
public static final String TYPE_type = "INTEGER";
|
||||||
Uri.parse("content://" + DataProvider.AUTHORITY + "/accounts");
|
public static final String WHO_ID = "c_who_id";
|
||||||
public static final String CONTENT_TYPE =
|
public static final String WHO_ID_type = "INTEGER";
|
||||||
"vnd.android.cursor.dir/vnd.thialfihar.apg.account";
|
public static final String KEY_RING_DATA = "c_key_ring_data";
|
||||||
public static final String CONTENT_ITEM_TYPE =
|
public static final String KEY_RING_DATA_type = "BLOB";
|
||||||
"vnd.android.cursor.item/vnd.thialfihar.apg.account";
|
|
||||||
public static final String DEFAULT_SORT_ORDER = _ID + " DESC";
|
|
||||||
}
|
}
|
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;
|
package org.thialfihar.android.apg.provider;
|
||||||
|
|
||||||
public class SecretKeys extends SecretKeys1 {
|
import android.provider.BaseColumns;
|
||||||
private SecretKeys() {
|
|
||||||
}
|
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";
|
||||||
}
|
}
|
@ -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) {
|
if (error != null) {
|
||||||
Toast.makeText(getContext(),
|
Toast.makeText(getContext(),
|
||||||
getContext().getString(R.string.errorMessage, error),
|
getContext().getString(R.string.errorMessage, error),
|
||||||
@ -310,24 +310,24 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
|
|||||||
mNewKeySize, passPhrase,
|
mNewKeySize, passPhrase,
|
||||||
masterKey);
|
masterKey);
|
||||||
} catch (NoSuchProviderException e) {
|
} catch (NoSuchProviderException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
} catch (PGPException e) {
|
} catch (PGPException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
} catch (InvalidParameterException e) {
|
} catch (InvalidParameterException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
} catch (InvalidAlgorithmParameterException e) {
|
} catch (InvalidAlgorithmParameterException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
} catch (Apg.GeneralException e) {
|
} catch (Apg.GeneralException e) {
|
||||||
error = e.getMessage();
|
error = "" + e;
|
||||||
}
|
}
|
||||||
|
|
||||||
Message message = new Message();
|
Message message = new Message();
|
||||||
Bundle data = new Bundle();
|
Bundle data = new Bundle();
|
||||||
data.putBoolean("closeProgressDialog", true);
|
data.putBoolean("closeProgressDialog", true);
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
data.putString("error", error);
|
data.putString(Apg.EXTRA_ERROR, error);
|
||||||
} else {
|
} else {
|
||||||
data.putBoolean("gotNewKey", true);
|
data.putBoolean("gotNewKey", true);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user