renaming whole package to org.apg to simplifiy name

This commit is contained in:
Dominik 2012-03-09 16:27:29 +01:00
parent df6933bfb8
commit 8452fb62b7
69 changed files with 1914 additions and 1628 deletions

View File

@ -3,7 +3,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="auto" android:installLocation="auto"
package="org.thialfihar.android.apg" package="org.apg"
android:versionCode="11000" android:versionCode="11000"
android:versionName="1.1" > android:versionName="1.1" >
@ -33,7 +33,7 @@
android:icon="@drawable/icon" android:icon="@drawable/icon"
android:label="@string/app_name" > android:label="@string/app_name" >
<activity <activity
android:name=".MainActivity" android:name=".ui.MainActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/app_name" > android:label="@string/app_name" >
<intent-filter> <intent-filter>
@ -43,7 +43,7 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".PublicKeyListActivity" android:name=".ui.PublicKeyListActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_managePublicKeys" android:label="@string/title_managePublicKeys"
android:launchMode="singleTop" > android:launchMode="singleTop" >
@ -56,7 +56,7 @@
android:resource="@xml/searchable_public_keys" /> android:resource="@xml/searchable_public_keys" />
</activity> </activity>
<activity <activity
android:name=".SecretKeyListActivity" android:name=".ui.SecretKeyListActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_manageSecretKeys" android:label="@string/title_manageSecretKeys"
android:launchMode="singleTop" > android:launchMode="singleTop" >
@ -69,11 +69,11 @@
android:resource="@xml/searchable_secret_keys" /> android:resource="@xml/searchable_secret_keys" />
</activity> </activity>
<activity <activity
android:name=".EditKeyActivity" android:name=".ui.EditKeyActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_editKey" /> android:label="@string/title_editKey" />
<activity <activity
android:name=".SelectPublicKeyListActivity" android:name=".ui.SelectPublicKeyListActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_selectRecipients" android:label="@string/title_selectRecipients"
android:launchMode="singleTop" > android:launchMode="singleTop" >
@ -91,7 +91,7 @@
android:resource="@xml/searchable_public_keys" /> android:resource="@xml/searchable_public_keys" />
</activity> </activity>
<activity <activity
android:name=".SelectSecretKeyListActivity" android:name=".ui.SelectSecretKeyListActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_selectSignature" android:label="@string/title_selectSignature"
android:launchMode="singleTop" > android:launchMode="singleTop" >
@ -109,7 +109,7 @@
android:resource="@xml/searchable_secret_keys" /> android:resource="@xml/searchable_secret_keys" />
</activity> </activity>
<activity <activity
android:name=".EncryptActivity" android:name=".ui.EncryptActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_encrypt" > android:label="@string/title_encrypt" >
<intent-filter> <intent-filter>
@ -124,7 +124,7 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".DecryptActivity" android:name=".ui.DecryptActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_decrypt" > android:label="@string/title_decrypt" >
<intent-filter> <intent-filter>
@ -138,7 +138,7 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".GeneralActivity" android:name=".ui.GeneralActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@android:style/Theme.Dialog" > android:theme="@android:style/Theme.Dialog" >
@ -170,31 +170,31 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".MailListActivity" android:name=".ui.MailListActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_mailInbox" /> android:label="@string/title_mailInbox" />
<activity <activity
android:name=".KeyServerQueryActivity" android:name=".ui.KeyServerQueryActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_keyServerQuery" /> android:label="@string/title_keyServerQuery" />
<activity <activity
android:name=".SendKeyActivity" android:name=".ui.SendKeyActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_sendKey" /> android:label="@string/title_sendKey" />
<activity <activity
android:name=".PreferencesActivity" android:name=".ui.PreferencesActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_preferences" /> android:label="@string/title_preferences" />
<activity <activity
android:name=".KeyServerPreferenceActivity" android:name=".ui.KeyServerPreferenceActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_keyServerPreference" /> android:label="@string/title_keyServerPreference" />
<activity <activity
android:name=".SignKeyActivity" android:name=".ui.SignKeyActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_signKey" /> android:label="@string/title_signKey" />
<activity <activity
android:name=".ImportFromQRCodeActivity" android:name=".ui.ImportFromQRCodeActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="keyboardHidden|orientation|keyboard"
android:label="@string/title_importFromQRCode" /> android:label="@string/title_importFromQRCode" />
@ -215,11 +215,11 @@
</service> </service>
<provider <provider
android:name="org.thialfihar.android.apg.provider.DataProvider" android:name=".provider.DataProvider"
android:authorities="org.thialfihar.android.apg.provider" android:authorities="org.thialfihar.android.apg.provider"
android:readPermission="org.thialfihar.android.apg.permission.READ_KEY_DETAILS" /> android:readPermission="org.thialfihar.android.apg.permission.READ_KEY_DETAILS" />
<provider <provider
android:name="org.thialfihar.android.apg.provider.ApgServiceBlobProvider" android:name=".provider.ApgServiceBlobProvider"
android:authorities="org.thialfihar.android.apg.provider.apgserviceblobprovider" android:authorities="org.thialfihar.android.apg.provider.apgserviceblobprovider"
android:permission="org.thialfihar.android.apg.permission.STORE_BLOBS" /> android:permission="org.thialfihar.android.apg.permission.STORE_BLOBS" />
</application> </application>

202
COPYING Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@ -14,7 +14,7 @@
limitations under the License. limitations under the License.
--> -->
<org.thialfihar.android.apg.ui.widget.SectionView <org.apg.ui.widget.SectionView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -63,4 +63,4 @@
android:paddingBottom="6dip" android:paddingBottom="6dip"
android:orientation="vertical"/> android:orientation="vertical"/>
</org.thialfihar.android.apg.ui.widget.SectionView> </org.apg.ui.widget.SectionView>

View File

@ -14,7 +14,7 @@
limitations under the License. limitations under the License.
--> -->
<org.thialfihar.android.apg.ui.widget.UserIdEditor <org.apg.ui.widget.UserIdEditor
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -112,4 +112,4 @@
</LinearLayout> </LinearLayout>
</org.thialfihar.android.apg.ui.widget.UserIdEditor> </org.apg.ui.widget.UserIdEditor>

View File

@ -14,7 +14,7 @@
limitations under the License. limitations under the License.
--> -->
<org.thialfihar.android.apg.ui.widget.KeyServerEditor <org.apg.ui.widget.KeyServerEditor
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -49,4 +49,4 @@
android:layout_height="1dip" android:layout_height="1dip"
android:background="?android:attr/listDivider"/> android:background="?android:attr/listDivider"/>
</org.thialfihar.android.apg.ui.widget.KeyServerEditor> </org.apg.ui.widget.KeyServerEditor>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org> <!--
Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -15,6 +16,7 @@
--> -->
<resources> <resources>
<style name="MinusButton"> <style name="MinusButton">
<item name="android:background">@drawable/btn_circle</item> <item name="android:background">@drawable/btn_circle</item>
<item name="android:src">@drawable/ic_btn_round_minus</item> <item name="android:src">@drawable/ic_btn_round_minus</item>
@ -24,4 +26,5 @@
<item name="android:background">@drawable/btn_circle</item> <item name="android:background">@drawable/btn_circle</item>
<item name="android:src">@drawable/ic_btn_round_plus</item> <item name="android:src">@drawable/ic_btn_round_plus</item>
</style> </style>
</resources>
</resources>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org> <!--
Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,71 +15,56 @@
limitations under the License. limitations under the License.
--> -->
<PreferenceScreen <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="@string/section_general">
<PreferenceCategory android:title="@string/section_general" >
<ListPreference <ListPreference
android:key="language" android:dialogTitle="@string/label_language"
android:title="@string/label_language"
android:entries="@array/language_entries" android:entries="@array/language_entries"
android:entryValues="@array/language_values" android:entryValues="@array/language_values"
android:dialogTitle="@string/label_language" /> android:key="language"
android:title="@string/label_language" />
<org.thialfihar.android.apg.ui.widget.IntegerListPreference <org.apg.ui.widget.IntegerListPreference
android:persistent="false"
android:key="passPhraseCacheTtl"
android:entries="@array/pass_phrase_cache_ttl_entries" android:entries="@array/pass_phrase_cache_ttl_entries"
android:entryValues="@array/pass_phrase_cache_ttl_values" android:entryValues="@array/pass_phrase_cache_ttl_values"
android:key="passPhraseCacheTtl"
android:persistent="false"
android:title="@string/label_passPhraseCacheTtl" /> android:title="@string/label_passPhraseCacheTtl" />
<PreferenceScreen <PreferenceScreen
android:persistent="false"
android:key="keyServers" android:key="keyServers"
android:persistent="false"
android:title="@string/label_keyServers" /> android:title="@string/label_keyServers" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/section_defaults" >
<PreferenceCategory <org.apg.ui.widget.IntegerListPreference
android:title="@string/section_defaults">
<org.thialfihar.android.apg.ui.widget.IntegerListPreference
android:persistent="false"
android:key="defaultEncryptionAlgorithm" android:key="defaultEncryptionAlgorithm"
android:persistent="false"
android:title="@string/label_encryptionAlgorithm" /> android:title="@string/label_encryptionAlgorithm" />
<org.apg.ui.widget.IntegerListPreference
<org.thialfihar.android.apg.ui.widget.IntegerListPreference
android:persistent="false"
android:key="defaultHashAlgorithm" android:key="defaultHashAlgorithm"
android:persistent="false"
android:title="@string/label_hashAlgorithm" /> android:title="@string/label_hashAlgorithm" />
<org.apg.ui.widget.IntegerListPreference
<org.thialfihar.android.apg.ui.widget.IntegerListPreference
android:persistent="false"
android:key="defaultMessageCompression" android:key="defaultMessageCompression"
android:title="@string/label_messageCompression" />
<org.thialfihar.android.apg.ui.widget.IntegerListPreference
android:persistent="false" android:persistent="false"
android:title="@string/label_messageCompression" />
<org.apg.ui.widget.IntegerListPreference
android:key="defaultFileCompression" android:key="defaultFileCompression"
android:persistent="false"
android:title="@string/label_fileCompression" /> android:title="@string/label_fileCompression" />
<CheckBoxPreference <CheckBoxPreference
android:persistent="false"
android:key="defaultAsciiArmour" android:key="defaultAsciiArmour"
android:title="@string/label_asciiArmour" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/section_advanced">
<CheckBoxPreference
android:persistent="false" android:persistent="false"
android:title="@string/label_asciiArmour" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/section_advanced" >
<CheckBoxPreference
android:key="forceV3Signatures" android:key="forceV3Signatures"
android:persistent="false"
android:title="@string/label_forceV3Signature" /> android:title="@string/label_forceV3Signature" />
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org> <!--
Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,8 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<searchable <searchable xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android" android:hint="@string/hint_publicKeys"
android:label="@string/app_name" android:label="@string/app_name" >
android:hint="@string/hint_publicKeys">
</searchable> </searchable>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org> <!--
Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,8 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<searchable <searchable xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android" android:hint="@string/hint_secretKeys"
android:label="@string/app_name" android:label="@string/app_name" >
android:hint="@string/hint_secretKeys">
</searchable> </searchable>

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -11,9 +11,10 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import org.thialfihar.android.apg.provider.KeyRings; import org.apg.provider.KeyRings;
import org.thialfihar.android.apg.provider.Keys; import org.apg.provider.Keys;
import org.thialfihar.android.apg.provider.UserIds; import org.apg.provider.UserIds;
import org.apg.IApgService;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Intent; import android.content.Intent;
@ -31,28 +32,22 @@ public class ApgService extends Service {
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
if( LOCAL_LOGD ) Log.d(TAG, "bound"); if (LOCAL_LOGD)
Log.d(TAG, "bound");
return mBinder; return mBinder;
} }
/** error status */ /** error status */
private static enum error { private static enum error {
ARGUMENTS_MISSING, ARGUMENTS_MISSING, APG_FAILURE, NO_MATCHING_SECRET_KEY, PRIVATE_KEY_PASSPHRASE_WRONG, PRIVATE_KEY_PASSPHRASE_MISSING;
APG_FAILURE,
NO_MATCHING_SECRET_KEY,
PRIVATE_KEY_PASSPHRASE_WRONG,
PRIVATE_KEY_PASSPHRASE_MISSING;
public int shiftedOrdinal() { public int shiftedOrdinal() {
return ordinal() + 100; return ordinal() + 100;
} }
} }
private static enum call { private static enum call {
encrypt_with_passphrase, encrypt_with_passphrase, encrypt_with_public_key, decrypt, get_keys
encrypt_with_public_key,
decrypt,
get_keys
} }
/** all arguments that can be passed by calling application */ /** all arguments that can be passed by calling application */
@ -139,42 +134,49 @@ public class ApgService extends Service {
private static final HashMap<String, Method> FUNCTIONS_DEFAULTS_METHODS = new HashMap<String, Method>(); private static final HashMap<String, Method> FUNCTIONS_DEFAULTS_METHODS = new HashMap<String, Method>();
static { static {
try { try {
FUNCTIONS_DEFAULTS_METHODS.put("getDefaultEncryptionAlgorithm", Preferences.class.getMethod("getDefaultEncryptionAlgorithm")); FUNCTIONS_DEFAULTS_METHODS.put("getDefaultEncryptionAlgorithm",
FUNCTIONS_DEFAULTS_METHODS.put("getDefaultHashAlgorithm", Preferences.class.getMethod("getDefaultHashAlgorithm")); Preferences.class.getMethod("getDefaultEncryptionAlgorithm"));
FUNCTIONS_DEFAULTS_METHODS.put("getDefaultAsciiArmour", Preferences.class.getMethod("getDefaultAsciiArmour")); FUNCTIONS_DEFAULTS_METHODS.put("getDefaultHashAlgorithm",
FUNCTIONS_DEFAULTS_METHODS.put("getForceV3Signatures", Preferences.class.getMethod("getForceV3Signatures")); Preferences.class.getMethod("getDefaultHashAlgorithm"));
FUNCTIONS_DEFAULTS_METHODS.put("getDefaultMessageCompression", Preferences.class.getMethod("getDefaultMessageCompression")); FUNCTIONS_DEFAULTS_METHODS.put("getDefaultAsciiArmour",
Preferences.class.getMethod("getDefaultAsciiArmour"));
FUNCTIONS_DEFAULTS_METHODS.put("getForceV3Signatures",
Preferences.class.getMethod("getForceV3Signatures"));
FUNCTIONS_DEFAULTS_METHODS.put("getDefaultMessageCompression",
Preferences.class.getMethod("getDefaultMessageCompression"));
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Function method exception: " + e.getMessage()); Log.e(TAG, "Function method exception: " + e.getMessage());
} }
} }
private static void writeToOutputStream(InputStream is, OutputStream os) throws IOException { private static void writeToOutputStream(InputStream is, OutputStream os) throws IOException {
byte[] buffer = new byte[8]; byte[] buffer = new byte[8];
int len = 0; int len = 0;
while( (len = is.read(buffer)) != -1) { while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len); os.write(buffer, 0, len);
} }
} }
private static Cursor getKeyEntries(HashMap<String, Object> pParams) { private static Cursor getKeyEntries(HashMap<String, Object> pParams) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + Keys.TABLE_NAME qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + "("
+ "." + Keys.KEY_RING_ID + " AND " + Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" + ") " + " INNER JOIN " + UserIds.TABLE_NAME + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + Keys.TABLE_NAME + "."
+ " ON " + "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " + UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " + UserIds.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " + Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY
+ UserIds.RANK + " = '0') "); + " = '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') ");
String orderBy = pParams.containsKey("order_by") ? (String) pParams.get("order_by") : UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC"; String orderBy = pParams.containsKey("order_by") ? (String) pParams.get("order_by")
: UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC";
String typeVal[] = null; String typeVal[] = null;
String typeWhere = null; String typeWhere = null;
if (pParams.containsKey("key_type")) { if (pParams.containsKey("key_type")) {
typeWhere = KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?"; typeWhere = KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?";
typeVal = new String[] { typeVal = new String[] { "" + pParams.get("key_type") };
"" + pParams.get("key_type")
};
} }
return qb.query(Apg.getDatabase().db(), (String[]) pParams.get("columns"), typeWhere, typeVal, null, null, orderBy); return qb.query(Apg.getDatabase().db(), (String[]) pParams.get("columns"), typeWhere,
typeVal, null, null, orderBy);
} }
/** /**
@ -197,40 +199,43 @@ public class ApgService extends Service {
return 0; return 0;
} }
} }
/** /**
* maps fingerprints or user ids of keys to master keys in database * maps fingerprints or user ids of keys to master keys in database
* *
* @param search_keys * @param search_keys
* a list of keys (fingerprints or user ids) to look for in * a list of keys (fingerprints or user ids) to look for in database
* database
* @return an array of master keys * @return an array of master keys
*/ */
private static long[] getMasterKey(ArrayList<String> pSearchKeys, Bundle pReturn) { private static long[] getMasterKey(ArrayList<String> pSearchKeys, Bundle pReturn) {
HashMap<String, Object> qParams = new HashMap<String, Object>(); HashMap<String, Object> qParams = new HashMap<String, Object>();
qParams.put("columns", new String[] { qParams.put("columns", new String[] { KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 0
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 0
UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 1 UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 1
}); });
qParams.put("key_type", Id.database.type_public); qParams.put("key_type", Id.database.type_public);
Cursor mCursor = getKeyEntries(qParams); Cursor mCursor = getKeyEntries(qParams);
if( LOCAL_LOGV ) Log.v(TAG, "going through installed user keys"); if (LOCAL_LOGV)
Log.v(TAG, "going through installed user keys");
ArrayList<Long> masterKeys = new ArrayList<Long>(); ArrayList<Long> masterKeys = new ArrayList<Long>();
while (mCursor.moveToNext()) { while (mCursor.moveToNext()) {
long curMkey = mCursor.getLong(0); long curMkey = mCursor.getLong(0);
String curUser = mCursor.getString(1); String curUser = mCursor.getString(1);
String curFprint = Apg.getSmallFingerPrint(curMkey); String curFprint = Apg.getSmallFingerPrint(curMkey);
if( LOCAL_LOGV ) Log.v(TAG, "current user: " + curUser + " (" + curFprint + ")"); if (LOCAL_LOGV)
Log.v(TAG, "current user: " + curUser + " (" + curFprint + ")");
if (pSearchKeys.contains(curFprint) || pSearchKeys.contains(curUser)) { if (pSearchKeys.contains(curFprint) || pSearchKeys.contains(curUser)) {
if( LOCAL_LOGV ) Log.v(TAG, "master key found for: " + curFprint); if (LOCAL_LOGV)
Log.v(TAG, "master key found for: " + curFprint);
masterKeys.add(curMkey); masterKeys.add(curMkey);
pSearchKeys.remove(curFprint); pSearchKeys.remove(curFprint);
} else { } else {
if( LOCAL_LOGV ) Log.v(TAG, "Installed key " + curFprint + " is not in the list of public keys to encrypt with"); if (LOCAL_LOGV)
Log.v(TAG, "Installed key " + curFprint
+ " is not in the list of public keys to encrypt with");
} }
} }
mCursor.close(); mCursor.close();
@ -243,12 +248,14 @@ public class ApgService extends Service {
if (i == 0) { if (i == 0) {
Log.w(TAG, "Found not one public key"); Log.w(TAG, "Found not one public key");
pReturn.getStringArrayList(ret.WARNINGS.name()).add("Searched for public key(s) but found not one"); pReturn.getStringArrayList(ret.WARNINGS.name()).add(
"Searched for public key(s) but found not one");
} }
for (String key : pSearchKeys) { for (String key : pSearchKeys) {
Log.w(TAG, "Searched for key " + key + " but cannot find it in APG"); Log.w(TAG, "Searched for key " + key + " but cannot find it in APG");
pReturn.getStringArrayList(ret.WARNINGS.name()).add("Searched for key " + key + " but cannot find it in APG"); pReturn.getStringArrayList(ret.WARNINGS.name()).add(
"Searched for key " + key + " but cannot find it in APG");
} }
return masterKeyLongs; return masterKeyLongs;
@ -269,18 +276,27 @@ public class ApgService extends Service {
while (iter.hasNext()) { while (iter.hasNext()) {
arg currentArg = iter.next(); arg currentArg = iter.next();
String currentKey = currentArg.name(); String currentKey = currentArg.name();
if (!pArgs.containsKey(currentKey) && FUNCTIONS_OPTIONAL_ARGS.get(pCall).contains(currentArg)) { if (!pArgs.containsKey(currentKey)
&& FUNCTIONS_OPTIONAL_ARGS.get(pCall).contains(currentArg)) {
String currentFunctionName = FUNCTIONS_DEFAULTS.get(currentArg); String currentFunctionName = FUNCTIONS_DEFAULTS.get(currentArg);
try { try {
Class<?> returnType = FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName).getReturnType(); Class<?> returnType = FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName)
.getReturnType();
if (returnType == String.class) { if (returnType == String.class) {
pArgs.putString(currentKey, (String) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName).invoke(preferences)); pArgs.putString(currentKey,
(String) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName)
.invoke(preferences));
} else if (returnType == boolean.class) { } else if (returnType == boolean.class) {
pArgs.putBoolean(currentKey, (Boolean) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName).invoke(preferences)); pArgs.putBoolean(currentKey,
(Boolean) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName)
.invoke(preferences));
} else if (returnType == int.class) { } else if (returnType == int.class) {
pArgs.putInt(currentKey, (Integer) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName).invoke(preferences)); pArgs.putInt(currentKey,
(Integer) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName)
.invoke(preferences));
} else { } else {
Log.e(TAG, "Unknown return type " + returnType.toString() + " for default option"); Log.e(TAG, "Unknown return type " + returnType.toString()
+ " for default option");
} }
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Exception in add_default_arguments " + e.getMessage()); Log.e(TAG, "Exception in add_default_arguments " + e.getMessage());
@ -320,19 +336,21 @@ public class ApgService extends Service {
while (iter.hasNext()) { while (iter.hasNext()) {
String curArg = iter.next().name(); String curArg = iter.next().name();
if (!pArgs.containsKey(curArg)) { if (!pArgs.containsKey(curArg)) {
pReturn.getStringArrayList(ret.ERRORS.name()).add("Argument missing: " + curArg); pReturn.getStringArrayList(ret.ERRORS.name())
.add("Argument missing: " + curArg);
} }
} }
} }
if(pFunction.equals(call.encrypt_with_passphrase.name()) || if (pFunction.equals(call.encrypt_with_passphrase.name())
pFunction.equals(call.encrypt_with_public_key.name()) || || pFunction.equals(call.encrypt_with_public_key.name())
pFunction.equals(call.decrypt.name())) { || pFunction.equals(call.decrypt.name())) {
// check that either MESSAGE or BLOB are there // check that either MESSAGE or BLOB are there
if( !pArgs.containsKey(arg.MESSAGE.name()) && !pArgs.containsKey(arg.BLOB.name())) { if (!pArgs.containsKey(arg.MESSAGE.name()) && !pArgs.containsKey(arg.BLOB.name())) {
pReturn.getStringArrayList(ret.ERRORS.name()).add("Arguments missing: Neither MESSAGE nor BLOG found"); pReturn.getStringArrayList(ret.ERRORS.name()).add(
"Arguments missing: Neither MESSAGE nor BLOG found");
} }
} }
} }
@ -363,7 +381,8 @@ public class ApgService extends Service {
try { try {
arg curArg = arg.valueOf(curKey); arg curArg = arg.valueOf(curKey);
if (!allArgs.contains(curArg)) { if (!allArgs.contains(curArg)) {
pReturn.getStringArrayList(ret.WARNINGS.name()).add("Unknown argument: " + curKey); pReturn.getStringArrayList(ret.WARNINGS.name()).add(
"Unknown argument: " + curKey);
unknownArgs.add(curKey); unknownArgs.add(curKey);
} }
} catch (Exception e) { } catch (Exception e) {
@ -386,23 +405,28 @@ public class ApgService extends Service {
/* add default arguments if missing */ /* add default arguments if missing */
addDefaultArguments(pCall, pArgs); addDefaultArguments(pCall, pArgs);
if( LOCAL_LOGV ) Log.v(TAG, "add_default_arguments"); if (LOCAL_LOGV)
Log.v(TAG, "add_default_arguments");
/* check for required arguments */ /* check for required arguments */
checkForRequiredArgs(pCall, pArgs, pReturn); checkForRequiredArgs(pCall, pArgs, pReturn);
if( LOCAL_LOGV ) Log.v(TAG, "check_required_args"); if (LOCAL_LOGV)
Log.v(TAG, "check_required_args");
/* check for unknown arguments and add to warning if found */ /* check for unknown arguments and add to warning if found */
checkForUnknownArgs(pCall, pArgs, pReturn); checkForUnknownArgs(pCall, pArgs, pReturn);
if( LOCAL_LOGV ) Log.v(TAG, "check_unknown_args"); if (LOCAL_LOGV)
Log.v(TAG, "check_unknown_args");
/* return if errors happened */ /* return if errors happened */
if (pReturn.getStringArrayList(ret.ERRORS.name()).size() != 0) { if (pReturn.getStringArrayList(ret.ERRORS.name()).size() != 0) {
if( LOCAL_LOGV ) Log.v(TAG, "Errors after preparing, not executing "+pCall); if (LOCAL_LOGV)
Log.v(TAG, "Errors after preparing, not executing " + pCall);
pReturn.putInt(ret.ERROR.name(), error.ARGUMENTS_MISSING.shiftedOrdinal()); pReturn.putInt(ret.ERROR.name(), error.ARGUMENTS_MISSING.shiftedOrdinal());
return false; return false;
} }
if( LOCAL_LOGV ) Log.v(TAG, "error return"); if (LOCAL_LOGV)
Log.v(TAG, "error return");
return true; return true;
} }
@ -414,7 +438,8 @@ public class ApgService extends Service {
if (pArgs.containsKey(arg.PUBLIC_KEYS.name())) { if (pArgs.containsKey(arg.PUBLIC_KEYS.name())) {
ArrayList<String> list = pArgs.getStringArrayList(arg.PUBLIC_KEYS.name()); ArrayList<String> list = pArgs.getStringArrayList(arg.PUBLIC_KEYS.name());
ArrayList<String> pubKeys = new ArrayList<String>(); ArrayList<String> pubKeys = new ArrayList<String>();
if( LOCAL_LOGV ) Log.v(TAG, "Long size: " + list.size()); if (LOCAL_LOGV)
Log.v(TAG, "Long size: " + list.size());
Iterator<String> iter = list.iterator(); Iterator<String> iter = list.iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
pubKeys.add(iter.next()); pubKeys.add(iter.next());
@ -423,7 +448,7 @@ public class ApgService extends Service {
} }
InputStream inStream = null; InputStream inStream = null;
if(isBlob) { if (isBlob) {
ContentResolver cr = getContentResolver(); ContentResolver cr = getContentResolver();
try { try {
inStream = cr.openInputStream(Uri.parse(pArgs.getString(arg.BLOB.name()))); inStream = cr.openInputStream(Uri.parse(pArgs.getString(arg.BLOB.name())));
@ -436,14 +461,16 @@ public class ApgService extends Service {
InputData in = new InputData(inStream, 0); // XXX Size second param? InputData in = new InputData(inStream, 0); // XXX Size second param?
OutputStream out = new ByteArrayOutputStream(); OutputStream out = new ByteArrayOutputStream();
if( LOCAL_LOGV ) Log.v(TAG, "About to encrypt"); if (LOCAL_LOGV)
Log.v(TAG, "About to encrypt");
try { try {
Apg.encrypt(getBaseContext(), // context Apg.encrypt(getBaseContext(), // context
in, // input stream in, // input stream
out, // output stream out, // output stream
pArgs.getBoolean(arg.ARMORED_OUTPUT.name()), // ARMORED_OUTPUT pArgs.getBoolean(arg.ARMORED_OUTPUT.name()), // ARMORED_OUTPUT
pubMasterKeys, // encryption keys pubMasterKeys, // encryption keys
getMasterKey(pArgs.getString(arg.SIGNATURE_KEY.name()), pReturn), // signature key getMasterKey(pArgs.getString(arg.SIGNATURE_KEY.name()), pReturn), // signature
// key
pArgs.getString(arg.PRIVATE_KEY_PASSPHRASE.name()), // signature passphrase pArgs.getString(arg.PRIVATE_KEY_PASSPHRASE.name()), // signature passphrase
null, // progress null, // progress
pArgs.getInt(arg.ENCRYPTION_ALGORYTHM.name()), // encryption pArgs.getInt(arg.ENCRYPTION_ALGORYTHM.name()), // encryption
@ -451,27 +478,38 @@ public class ApgService extends Service {
pArgs.getInt(arg.COMPRESSION.name()), // compression pArgs.getInt(arg.COMPRESSION.name()), // compression
pArgs.getBoolean(arg.FORCE_V3_SIGNATURE.name()), // mPreferences.getForceV3Signatures(), pArgs.getBoolean(arg.FORCE_V3_SIGNATURE.name()), // mPreferences.getForceV3Signatures(),
pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) // passPhrase pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) // passPhrase
); );
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Exception in encrypt"); Log.e(TAG, "Exception in encrypt");
String msg = e.getMessage(); String msg = e.getMessage();
if (msg.equals(getBaseContext().getString(R.string.error_noSignaturePassPhrase))) { if (msg.equals(getBaseContext().getString(R.string.error_noSignaturePassPhrase))) {
pReturn.getStringArrayList(ret.ERRORS.name()).add("Cannot encrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name() + " missing): " + msg); pReturn.getStringArrayList(ret.ERRORS.name()).add(
pReturn.putInt(ret.ERROR.name(), error.PRIVATE_KEY_PASSPHRASE_MISSING.shiftedOrdinal()); "Cannot encrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name() + " missing): "
} else if (msg.equals(getBaseContext().getString(R.string.error_couldNotExtractPrivateKey))) { + msg);
pReturn.getStringArrayList(ret.ERRORS.name()).add("Cannot encrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name() + " probably wrong): " + msg); pReturn.putInt(ret.ERROR.name(),
pReturn.putInt(ret.ERROR.name(), error.PRIVATE_KEY_PASSPHRASE_WRONG.shiftedOrdinal()); error.PRIVATE_KEY_PASSPHRASE_MISSING.shiftedOrdinal());
} else if (msg.equals(getBaseContext().getString(
R.string.error_couldNotExtractPrivateKey))) {
pReturn.getStringArrayList(ret.ERRORS.name()).add(
"Cannot encrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name()
+ " probably wrong): " + msg);
pReturn.putInt(ret.ERROR.name(),
error.PRIVATE_KEY_PASSPHRASE_WRONG.shiftedOrdinal());
} else { } else {
pReturn.getStringArrayList(ret.ERRORS.name()).add("Internal failure (" + e.getClass() + ") in APG when encrypting: " + e.getMessage()); pReturn.getStringArrayList(ret.ERRORS.name()).add(
"Internal failure (" + e.getClass() + ") in APG when encrypting: "
+ e.getMessage());
pReturn.putInt(ret.ERROR.name(), error.APG_FAILURE.shiftedOrdinal()); pReturn.putInt(ret.ERROR.name(), error.APG_FAILURE.shiftedOrdinal());
} }
return false; return false;
} }
if( LOCAL_LOGV ) Log.v(TAG, "Encrypted"); if (LOCAL_LOGV)
if(isBlob) { Log.v(TAG, "Encrypted");
if (isBlob) {
ContentResolver cr = getContentResolver(); ContentResolver cr = getContentResolver();
try { try {
OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB.name()))); OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB
.name())));
writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()), outStream); writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()), outStream);
outStream.close(); outStream.close();
} catch (Exception e) { } catch (Exception e) {
@ -501,7 +539,8 @@ public class ApgService extends Service {
ArrayList<String> fPrints = new ArrayList<String>(); ArrayList<String> fPrints = new ArrayList<String>();
ArrayList<String> ids = new ArrayList<String>(); ArrayList<String> ids = new ArrayList<String>();
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
if( LOCAL_LOGV ) Log.v(TAG, "adding key "+Apg.getSmallFingerPrint(cursor.getLong(0))); if (LOCAL_LOGV)
Log.v(TAG, "adding key " + Apg.getSmallFingerPrint(cursor.getLong(0)));
fPrints.add(Apg.getSmallFingerPrint(cursor.getLong(0))); fPrints.add(Apg.getSmallFingerPrint(cursor.getLong(0)));
ids.add(cursor.getString(1)); ids.add(cursor.getString(1));
} }
@ -533,14 +572,15 @@ public class ApgService extends Service {
if (!prepareArgs("decrypt", pArgs, pReturn)) { if (!prepareArgs("decrypt", pArgs, pReturn)) {
return false; return false;
} }
boolean isBlob = pArgs.containsKey(arg.BLOB.name()); boolean isBlob = pArgs.containsKey(arg.BLOB.name());
String passphrase = pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null ? pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) : pArgs String passphrase = pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null ? pArgs
.getString(arg.SYMMETRIC_PASSPHRASE.name()) : pArgs
.getString(arg.PRIVATE_KEY_PASSPHRASE.name()); .getString(arg.PRIVATE_KEY_PASSPHRASE.name());
InputStream inStream = null; InputStream inStream = null;
if(isBlob) { if (isBlob) {
ContentResolver cr = getContentResolver(); ContentResolver cr = getContentResolver();
try { try {
inStream = cr.openInputStream(Uri.parse(pArgs.getString(arg.BLOB.name()))); inStream = cr.openInputStream(Uri.parse(pArgs.getString(arg.BLOB.name())));
@ -550,14 +590,15 @@ public class ApgService extends Service {
} else { } else {
inStream = new ByteArrayInputStream(pArgs.getString(arg.MESSAGE.name()).getBytes()); inStream = new ByteArrayInputStream(pArgs.getString(arg.MESSAGE.name()).getBytes());
} }
InputData in = new InputData(inStream, 0); // XXX what size in second parameter? InputData in = new InputData(inStream, 0); // XXX what size in second parameter?
OutputStream out = new ByteArrayOutputStream(); OutputStream out = new ByteArrayOutputStream();
if( LOCAL_LOGV ) Log.v(TAG, "About to decrypt"); if (LOCAL_LOGV)
Log.v(TAG, "About to decrypt");
try { try {
Apg.decrypt(getBaseContext(), in, out, passphrase, null, // progress Apg.decrypt(getBaseContext(), in, out, passphrase, null, // progress
pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null // symmetric pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null // symmetric
); );
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Exception in decrypt"); Log.e(TAG, "Exception in decrypt");
String msg = e.getMessage(); String msg = e.getMessage();
@ -565,25 +606,33 @@ public class ApgService extends Service {
pReturn.getStringArrayList(ret.ERRORS.name()).add("Cannot decrypt: " + msg); pReturn.getStringArrayList(ret.ERRORS.name()).add("Cannot decrypt: " + msg);
pReturn.putInt(ret.ERROR.name(), error.NO_MATCHING_SECRET_KEY.shiftedOrdinal()); pReturn.putInt(ret.ERROR.name(), error.NO_MATCHING_SECRET_KEY.shiftedOrdinal());
} else if (msg.equals(getBaseContext().getString(R.string.error_wrongPassPhrase))) { } else if (msg.equals(getBaseContext().getString(R.string.error_wrongPassPhrase))) {
pReturn.getStringArrayList(ret.ERRORS.name()).add("Cannot decrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name() + " wrong/missing): " + msg); pReturn.getStringArrayList(ret.ERRORS.name()).add(
pReturn.putInt(ret.ERROR.name(), error.PRIVATE_KEY_PASSPHRASE_WRONG.shiftedOrdinal()); "Cannot decrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name()
+ " wrong/missing): " + msg);
pReturn.putInt(ret.ERROR.name(),
error.PRIVATE_KEY_PASSPHRASE_WRONG.shiftedOrdinal());
} else { } else {
pReturn.getStringArrayList(ret.ERRORS.name()).add("Internal failure (" + e.getClass() + ") in APG when decrypting: " + msg); pReturn.getStringArrayList(ret.ERRORS.name()).add(
"Internal failure (" + e.getClass() + ") in APG when decrypting: "
+ msg);
pReturn.putInt(ret.ERROR.name(), error.APG_FAILURE.shiftedOrdinal()); pReturn.putInt(ret.ERROR.name(), error.APG_FAILURE.shiftedOrdinal());
} }
return false; return false;
} }
if( LOCAL_LOGV ) Log.v(TAG, "... decrypted"); if (LOCAL_LOGV)
Log.v(TAG, "... decrypted");
if(isBlob) { if (isBlob) {
ContentResolver cr = getContentResolver(); ContentResolver cr = getContentResolver();
try { try {
OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB.name()))); OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB
writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()), outStream); .name())));
writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()),
outStream);
outStream.close(); outStream.close();
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "... exception on writing blob", e); Log.e(TAG, "... exception on writing blob", e);
} }
} else { } else {
pReturn.putString(ret.RESULT.name(), out.toString()); pReturn.putString(ret.RESULT.name(), out.toString());
} }

View File

@ -14,12 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg;
import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.apg.R;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
@ -38,7 +39,7 @@ public class AskForSecretKeyPassPhrase {
} }
public static Dialog createDialog(Activity context, long secretKeyId, public static Dialog createDialog(Activity context, long secretKeyId,
PassPhraseCallbackInterface callback) { PassPhraseCallbackInterface callback) {
AlertDialog.Builder alert = new AlertDialog.Builder(context); AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setTitle(R.string.title_authentication); alert.setTitle(R.string.title_authentication);
@ -66,8 +67,8 @@ public class AskForSecretKeyPassPhrase {
alert.setMessage(context.getString(R.string.passPhraseFor, userId)); alert.setMessage(context.getString(R.string.passPhraseFor, userId));
} }
LayoutInflater inflater = LayoutInflater inflater = (LayoutInflater) context
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.pass_phrase, null); View view = inflater.inflate(R.layout.pass_phrase, null);
final EditText input = (EditText) view.findViewById(R.id.passPhrase); final EditText input = (EditText) view.findViewById(R.id.passPhrase);
final EditText inputNotUsed = (EditText) view.findViewById(R.id.passPhraseAgain); final EditText inputNotUsed = (EditText) view.findViewById(R.id.passPhraseAgain);
@ -76,42 +77,38 @@ public class AskForSecretKeyPassPhrase {
alert.setView(view); alert.setView(view);
final PassPhraseCallbackInterface cb = callback; final PassPhraseCallbackInterface cb = callback;
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) { activity.removeDialog(Id.dialog.pass_phrase);
activity.removeDialog(Id.dialog.pass_phrase); String passPhrase = "" + input.getText();
String passPhrase = "" + input.getText(); long keyId;
long keyId; if (secretKey != null) {
if (secretKey != null) { try {
try { PGPPrivateKey testKey = secretKey.extractPrivateKey(
PGPPrivateKey testKey = secretKey.extractPrivateKey(passPhrase.toCharArray(), passPhrase.toCharArray(), new BouncyCastleProvider());
new BouncyCastleProvider()); if (testKey == null) {
if (testKey == null) { Toast.makeText(activity, R.string.error_couldNotExtractPrivateKey,
Toast.makeText(activity, Toast.LENGTH_SHORT).show();
R.string.error_couldNotExtractPrivateKey, return;
Toast.LENGTH_SHORT).show();
return;
}
} catch (PGPException e) {
Toast.makeText(activity,
R.string.wrongPassPhrase,
Toast.LENGTH_SHORT).show();
return;
}
keyId = secretKey.getKeyID();
} else {
keyId = Id.key.symmetric;
} }
cb.passPhraseCallback(keyId, passPhrase); } catch (PGPException e) {
Toast.makeText(activity, R.string.wrongPassPhrase, Toast.LENGTH_SHORT)
.show();
return;
} }
}); keyId = secretKey.getKeyID();
} else {
keyId = Id.key.symmetric;
}
cb.passPhraseCallback(keyId, passPhrase);
}
});
alert.setNegativeButton(android.R.string.cancel, alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) {
public void onClick(DialogInterface dialog, int id) { activity.removeDialog(Id.dialog.pass_phrase);
activity.removeDialog(Id.dialog.pass_phrase); }
} });
});
return alert.create(); return alert.create();
} }

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
public class CachedPassPhrase { public class CachedPassPhrase {
public final long timestamp; public final long timestamp;
@ -11,14 +11,14 @@ public class CachedPassPhrase {
} }
@Override @Override
public int hashCode() { public int hashCode() {
int hc1 = (int)(this.timestamp & 0xffffffff); int hc1 = (int) (this.timestamp & 0xffffffff);
int hc2 = (this.passPhrase == null ? 0 : this.passPhrase.hashCode()); int hc2 = (this.passPhrase == null ? 0 : this.passPhrase.hashCode());
return (hc1 + hc2) * hc2 + hc1; return (hc1 + hc2) * hc2 + hc1;
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (!(other instanceof CachedPassPhrase)) { if (!(other instanceof CachedPassPhrase)) {
return false; return false;
} }
@ -42,7 +42,7 @@ public class CachedPassPhrase {
} }
@Override @Override
public String toString() { public String toString() {
return "(" + timestamp + ", *******)"; return "(" + timestamp + ", *******)";
} }
} }

View File

@ -14,14 +14,14 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg;
import android.os.Environment; import android.os.Environment;
public final class Constants { public final class Constants {
public static final String tag = "APG"; public static final String tag = "APG";
public static final class path { public static final class path {
public static final String app_dir = Environment.getExternalStorageDirectory() + "/APG"; public static final String app_dir = Environment.getExternalStorageDirectory() + "/APG";
} }

View File

@ -0,0 +1,81 @@
package org.apg;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apg.Apg.GeneralException;
import org.apg.R;
import android.content.Context;
import android.os.Environment;
public class DataDestination {
private String mStreamFilename;
private String mFilename;
private int mMode = Id.mode.undefined;
public DataDestination() {
}
public void setMode(int mode) {
mMode = mode;
}
public void setFilename(String filename) {
mFilename = filename;
}
public String getStreamFilename() {
return mStreamFilename;
}
public OutputStream getOutputStream(Context context) throws Apg.GeneralException,
FileNotFoundException, IOException {
OutputStream out = null;
mStreamFilename = null;
switch (mMode) {
case Id.mode.stream: {
try {
while (true) {
mStreamFilename = Apg.generateRandomString(32);
if (mStreamFilename == null) {
throw new Apg.GeneralException("couldn't generate random file name");
}
context.openFileInput(mStreamFilename).close();
}
} catch (FileNotFoundException e) {
// found a name that isn't used yet
}
out = context.openFileOutput(mStreamFilename, Context.MODE_PRIVATE);
break;
}
case Id.mode.byte_array: {
out = new ByteArrayOutputStream();
break;
}
case Id.mode.file: {
if (mFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
throw new GeneralException(
context.getString(R.string.error_externalStorageNotReady));
}
}
out = new FileOutputStream(mFilename);
break;
}
default: {
break;
}
}
return out;
}
}

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
@ -7,7 +7,8 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import org.thialfihar.android.apg.Apg.GeneralException; import org.apg.Apg.GeneralException;
import org.apg.R;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
@ -56,8 +57,8 @@ public class DataSource {
return mData != null || mContentUri != null; return mData != null || mContentUri != null;
} }
public InputData getInputData(Context context, boolean withSize) public InputData getInputData(Context context, boolean withSize) throws GeneralException,
throws GeneralException, FileNotFoundException, IOException { FileNotFoundException, IOException {
InputStream in = null; InputStream in = null;
long size = 0; long size = 0;
@ -67,7 +68,8 @@ public class DataSource {
String path = Uri.decode(mContentUri.toString().substring(7)); String path = Uri.decode(mContentUri.toString().substring(7));
if (path.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) { if (path.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
throw new GeneralException(context.getString(R.string.error_externalStorageNotReady)); throw new GeneralException(
context.getString(R.string.error_externalStorageNotReady));
} }
} }
in = new FileInputStream(path); in = new FileInputStream(path);

View File

@ -14,7 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg;
import org.apg.R;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
@ -41,18 +43,17 @@ public class FileDialog {
public static interface OnClickListener { public static interface OnClickListener {
public void onCancelClick(); public void onCancelClick();
public void onOkClick(String filename, boolean checkbox); public void onOkClick(String filename, boolean checkbox);
} }
public static AlertDialog build(Activity activity, String title, String message, public static AlertDialog build(Activity activity, String title, String message,
String defaultFile, OnClickListener onClickListener, String defaultFile, OnClickListener onClickListener, String fileManagerTitle,
String fileManagerTitle, String fileManagerButton, String fileManagerButton, String checkboxText, int requestCode) {
String checkboxText,
int requestCode) {
// TODO: fileManagerTitle and fileManagerButton are deprecated, no use for them right now, // 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 // but maybe the Intent now used will someday support them again, so leaving them in
LayoutInflater inflater = LayoutInflater inflater = (LayoutInflater) activity
(LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
AlertDialog.Builder alert = new AlertDialog.Builder(activity); AlertDialog.Builder alert = new AlertDialog.Builder(activity);
alert.setTitle(title); alert.setTitle(title);
@ -87,22 +88,20 @@ public class FileDialog {
final OnClickListener clickListener = onClickListener; final OnClickListener clickListener = onClickListener;
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) {
boolean checked = false; boolean checked = false;
if (mCheckBox.isEnabled()) { if (mCheckBox.isEnabled()) {
checked = mCheckBox.isChecked(); checked = mCheckBox.isChecked();
} }
clickListener.onOkClick(mFilename.getText().toString(), clickListener.onOkClick(mFilename.getText().toString(), checked);
checked); }
} });
});
alert.setNegativeButton(android.R.string.cancel, alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) {
public void onClick(DialogInterface dialog, int id) { clickListener.onCancelClick();
clickListener.onCancelClick(); }
} });
});
return alert.create(); return alert.create();
} }

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
@ -56,9 +56,15 @@ public class HkpKeyServer extends KeyServer {
private short mPort = 11371; private short mPort = 11371;
// example: // example:
// pub 2048R/<a href="/pks/lookup?op=get&search=0x887DF4BE9F5C9090">9F5C9090</a> 2009-08-17 <a href="/pks/lookup?op=vindex&search=0x887DF4BE9F5C9090">Jörg Runge &lt;joerg@joergrunge.de&gt;</a> // pub 2048R/<a href="/pks/lookup?op=get&search=0x887DF4BE9F5C9090">9F5C9090</a> 2009-08-17 <a
public static Pattern PUB_KEY_LINE = Pattern.compile("pub +([0-9]+)([a-z]+)/.*?0x([0-9a-z]+).*? +([0-9-]+) +(.+)[\n\r]+((?: +.+[\n\r]+)*)", Pattern.CASE_INSENSITIVE); // href="/pks/lookup?op=vindex&search=0x887DF4BE9F5C9090">Jörg Runge
public static Pattern USER_ID_LINE = Pattern.compile("^ +(.+)$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); // &lt;joerg@joergrunge.de&gt;</a>
public static Pattern PUB_KEY_LINE = Pattern
.compile(
"pub +([0-9]+)([a-z]+)/.*?0x([0-9a-z]+).*? +([0-9-]+) +(.+)[\n\r]+((?: +.+[\n\r]+)*)",
Pattern.CASE_INSENSITIVE);
public static Pattern USER_ID_LINE = Pattern.compile("^ +(.+)$", Pattern.MULTILINE
| Pattern.CASE_INSENSITIVE);
public HkpKeyServer(String host) { public HkpKeyServer(String host) {
mHost = host; mHost = host;
@ -119,7 +125,8 @@ public class HkpKeyServer extends KeyServer {
// TODO: replace this with httpclient // TODO: replace this with httpclient
@Override @Override
List<KeyInfo> search(String query) throws QueryException, TooManyResponses, InsufficientQuery { public List<KeyInfo> search(String query) throws QueryException, TooManyResponses,
InsufficientQuery {
Vector<KeyInfo> results = new Vector<KeyInfo>(); Vector<KeyInfo> results = new Vector<KeyInfo>();
if (query.length() < 3) { if (query.length() < 3) {
@ -160,7 +167,8 @@ public class HkpKeyServer extends KeyServer {
info.keyId = Apg.keyFromHex(matcher.group(3)); info.keyId = Apg.keyFromHex(matcher.group(3));
info.fingerPrint = Apg.getSmallFingerPrint(info.keyId); info.fingerPrint = Apg.getSmallFingerPrint(info.keyId);
String chunks[] = matcher.group(4).split("-"); String chunks[] = matcher.group(4).split("-");
info.date = new GregorianCalendar(Integer.parseInt(chunks[0]), Integer.parseInt(chunks[1]), Integer.parseInt(chunks[2])).getTime(); info.date = new GregorianCalendar(Integer.parseInt(chunks[0]),
Integer.parseInt(chunks[1]), Integer.parseInt(chunks[2])).getTime();
info.userIds = new Vector<String>(); info.userIds = new Vector<String>();
if (matcher.group(5).startsWith("*** KEY")) { if (matcher.group(5).startsWith("*** KEY")) {
info.revoked = matcher.group(5); info.revoked = matcher.group(5);
@ -184,16 +192,17 @@ public class HkpKeyServer extends KeyServer {
} }
@Override @Override
String get(long keyId) throws QueryException { public String get(long keyId) throws QueryException {
HttpClient client = new DefaultHttpClient(); HttpClient client = new DefaultHttpClient();
try { try {
HttpGet get = new HttpGet("http://" + mHost + ":" + mPort + "/pks/lookup?op=get&search=0x" + Apg.keyToHex(keyId)); HttpGet get = new HttpGet("http://" + mHost + ":" + mPort
+ "/pks/lookup?op=get&search=0x" + Apg.keyToHex(keyId));
HttpResponse response = client.execute(get); HttpResponse response = client.execute(get);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new QueryException("not found"); throw new QueryException("not found");
} }
HttpEntity entity = response.getEntity(); HttpEntity entity = response.getEntity();
InputStream is = entity.getContent(); InputStream is = entity.getContent();
String data = readAll(is, EntityUtils.getContentCharSet(entity)); String data = readAll(is, EntityUtils.getContentCharSet(entity));
@ -206,7 +215,7 @@ public class HkpKeyServer extends KeyServer {
} finally { } finally {
client.getConnectionManager().shutdown(); client.getConnectionManager().shutdown();
} }
return null; return null;
} }
@ -215,11 +224,11 @@ public class HkpKeyServer extends KeyServer {
HttpClient client = new DefaultHttpClient(); HttpClient client = new DefaultHttpClient();
try { try {
HttpPost post = new HttpPost("http://" + mHost + ":" + mPort + "/pks/add"); HttpPost post = new HttpPost("http://" + mHost + ":" + mPort + "/pks/add");
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2); List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
nameValuePairs.add(new BasicNameValuePair("keytext", armouredText)); nameValuePairs.add(new BasicNameValuePair("keytext", armouredText));
post.setEntity(new UrlEncodedFormEntity(nameValuePairs)); post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = client.execute(post); HttpResponse response = client.execute(post);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new AddKeyException(); throw new AddKeyException();

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
interface IApgService { interface IApgService {

View File

@ -14,14 +14,14 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg;
import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.spongycastle.bcpg.CompressionAlgorithmTags;
public final class Id { public final class Id {
public static final String TAG = "APG"; public static final String TAG = "APG";
public static final class menu { public static final class menu {
public static final int export = 0x21070001; public static final int export = 0x21070001;
public static final int delete = 0x21070002; public static final int delete = 0x21070002;

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
import java.io.InputStream; import java.io.InputStream;
@ -6,7 +6,7 @@ public class InputData {
private PositionAwareInputStream mInputStream; private PositionAwareInputStream mInputStream;
private long mSize; private long mSize;
InputData(InputStream inputStream, long size) { public InputData(InputStream inputStream, long size) {
mInputStream = new PositionAwareInputStream(inputStream); mInputStream = new PositionAwareInputStream(inputStream);
mSize = size; mSize = size;
} }

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
@ -8,35 +8,39 @@ import java.util.Vector;
public abstract class KeyServer { public abstract class KeyServer {
static public class QueryException extends Exception { static public class QueryException extends Exception {
private static final long serialVersionUID = 2703768928624654512L; private static final long serialVersionUID = 2703768928624654512L;
public QueryException(String message) { public QueryException(String message) {
super(message); super(message);
} }
} }
static public class TooManyResponses extends Exception { static public class TooManyResponses extends Exception {
private static final long serialVersionUID = 2703768928624654513L; private static final long serialVersionUID = 2703768928624654513L;
} }
static public class InsufficientQuery extends Exception { static public class InsufficientQuery extends Exception {
private static final long serialVersionUID = 2703768928624654514L; private static final long serialVersionUID = 2703768928624654514L;
} }
static public class AddKeyException extends Exception { static public class AddKeyException extends Exception {
private static final long serialVersionUID = -507574859137295530L; private static final long serialVersionUID = -507574859137295530L;
} }
static public class KeyInfo implements Serializable { static public class KeyInfo implements Serializable {
private static final long serialVersionUID = -7797972113284992662L; private static final long serialVersionUID = -7797972113284992662L;
Vector<String> userIds; public Vector<String> userIds;
String revoked; public String revoked;
Date date; public Date date;
String fingerPrint; public String fingerPrint;
long keyId; public long keyId;
int size; public int size;
String algorithm; public String algorithm;
} }
abstract List<KeyInfo> search(String query) throws QueryException, TooManyResponses, InsufficientQuery; abstract List<KeyInfo> search(String query) throws QueryException, TooManyResponses,
InsufficientQuery;
abstract String get(long keyId) throws QueryException; abstract String get(long keyId) throws QueryException;
abstract void add(String armouredText) throws AddKeyException; abstract void add(String armouredText) throws AddKeyException;
} }

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
public class PausableThread extends Thread { public class PausableThread extends Thread {
private boolean mPaused = false; private boolean mPaused = false;
@ -22,8 +22,8 @@ public class PausableThread extends Thread {
public void unpause() { public void unpause() {
synchronized (this) { synchronized (this) {
mPaused = false; mPaused = false;
notify(); notify();
} }
} }

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
import org.spongycastle.bcpg.HashAlgorithmTags; import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData; import org.spongycastle.openpgp.PGPEncryptedData;
@ -15,17 +15,15 @@ public class Preferences {
public static synchronized Preferences getPreferences(Context context) { public static synchronized Preferences getPreferences(Context context) {
return getPreferences(context, false); return getPreferences(context, false);
} }
public static synchronized Preferences getPreferences(Context context, boolean force_new) public static synchronized Preferences getPreferences(Context context, boolean force_new) {
{
if (mPreferences == null || force_new) { if (mPreferences == null || force_new) {
mPreferences = new Preferences(context); mPreferences = new Preferences(context);
} }
return mPreferences; return mPreferences;
} }
private Preferences(Context context) private Preferences(Context context) {
{
mSharedPreferences = context.getSharedPreferences("APG.main", Context.MODE_PRIVATE); mSharedPreferences = context.getSharedPreferences("APG.main", Context.MODE_PRIVATE);
} }
@ -57,7 +55,7 @@ public class Preferences {
public int getDefaultEncryptionAlgorithm() { public int getDefaultEncryptionAlgorithm() {
return mSharedPreferences.getInt(Constants.pref.default_encryption_algorithm, return mSharedPreferences.getInt(Constants.pref.default_encryption_algorithm,
PGPEncryptedData.AES_256); PGPEncryptedData.AES_256);
} }
public void setDefaultEncryptionAlgorithm(int value) { public void setDefaultEncryptionAlgorithm(int value) {
@ -68,7 +66,7 @@ public class Preferences {
public int getDefaultHashAlgorithm() { public int getDefaultHashAlgorithm() {
return mSharedPreferences.getInt(Constants.pref.default_hash_algorithm, return mSharedPreferences.getInt(Constants.pref.default_hash_algorithm,
HashAlgorithmTags.SHA256); HashAlgorithmTags.SHA256);
} }
public void setDefaultHashAlgorithm(int value) { public void setDefaultHashAlgorithm(int value) {
@ -79,7 +77,7 @@ public class Preferences {
public int getDefaultMessageCompression() { public int getDefaultMessageCompression() {
return mSharedPreferences.getInt(Constants.pref.default_message_compression, return mSharedPreferences.getInt(Constants.pref.default_message_compression,
Id.choice.compression.zlib); Id.choice.compression.zlib);
} }
public void setDefaultMessageCompression(int value) { public void setDefaultMessageCompression(int value) {
@ -90,7 +88,7 @@ public class Preferences {
public int getDefaultFileCompression() { public int getDefaultFileCompression() {
return mSharedPreferences.getInt(Constants.pref.default_file_compression, return mSharedPreferences.getInt(Constants.pref.default_file_compression,
Id.choice.compression.none); Id.choice.compression.none);
} }
public void setDefaultFileCompression(int value) { public void setDefaultFileCompression(int value) {
@ -120,8 +118,7 @@ public class Preferences {
} }
public boolean hasSeenChangeLog(String version) { public boolean hasSeenChangeLog(String version) {
return mSharedPreferences.getBoolean(Constants.pref.has_seen_change_log + version, return mSharedPreferences.getBoolean(Constants.pref.has_seen_change_log + version, false);
false);
} }
public void setHasSeenChangeLog(String version, boolean value) { public void setHasSeenChangeLog(String version, boolean value) {
@ -142,7 +139,7 @@ public class Preferences {
public String[] getKeyServers() { public String[] getKeyServers() {
String rawData = mSharedPreferences.getString(Constants.pref.key_servers, String rawData = mSharedPreferences.getString(Constants.pref.key_servers,
Constants.defaults.key_servers); Constants.defaults.key_servers);
Vector<String> servers = new Vector<String>(); Vector<String> servers = new Vector<String>();
String chunks[] = rawData.split(","); String chunks[] = rawData.split(",");
for (int i = 0; i < chunks.length; ++i) { for (int i = 0; i < chunks.length; ++i) {

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg;
import java.math.BigInteger; import java.math.BigInteger;

View File

@ -14,10 +14,12 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg;
public interface ProgressDialogUpdater { public interface ProgressDialogUpdater {
void setProgress(String message, int current, int total); void setProgress(String message, int current, int total);
void setProgress(int resourceId, int current, int total); void setProgress(int resourceId, int current, int total);
void setProgress(int current, int total); void setProgress(int current, int total);
} }

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg;
import android.content.Intent; import android.content.Intent;
import android.os.Binder; import android.os.Binder;

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.provider; package org.apg.provider;
import android.provider.BaseColumns; import android.provider.BaseColumns;

View File

@ -1,6 +1,6 @@
package org.thialfihar.android.apg.provider; package org.apg.provider;
import org.thialfihar.android.apg.ApgService; import org.apg.ApgService;
import android.content.ContentUris; import android.content.ContentUris;
import android.content.ContentValues; import android.content.ContentValues;

View File

@ -1,7 +1,7 @@
package org.thialfihar.android.apg.provider; package org.apg.provider;
import org.thialfihar.android.apg.ApgService; import org.apg.ApgService;
import org.thialfihar.android.apg.Constants; import org.apg.Constants;
import android.content.ContentProvider; import android.content.ContentProvider;
import android.content.ContentValues; import android.content.ContentValues;

View File

@ -14,13 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.provider; package org.apg.provider;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.util.HashMap; import java.util.HashMap;
import org.thialfihar.android.apg.Id; import org.apg.Id;
import android.content.ContentProvider; import android.content.ContentProvider;
import android.content.ContentValues; import android.content.ContentValues;

View File

@ -1,13 +1,13 @@
package org.thialfihar.android.apg.provider; package org.apg.provider;
import org.apg.Apg;
import org.apg.Id;
import org.apg.util.IterableIterator;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.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.ContentValues;
import android.content.Context; import android.content.Context;

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.provider; package org.apg.provider;
import android.provider.BaseColumns; import android.provider.BaseColumns;

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.provider; package org.apg.provider;
import android.provider.BaseColumns; import android.provider.BaseColumns;

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.provider; package org.apg.provider;
import android.provider.BaseColumns; import android.provider.BaseColumns;

View File

@ -0,0 +1,461 @@
/*
* 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.apg.ui;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Locale;
import org.apg.Apg;
import org.apg.AskForSecretKeyPassPhrase;
import org.apg.Constants;
import org.apg.Id;
import org.apg.PausableThread;
import org.apg.Preferences;
import org.apg.ProgressDialogUpdater;
import org.apg.Service;
import org.apg.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class BaseActivity extends Activity implements Runnable, ProgressDialogUpdater,
AskForSecretKeyPassPhrase.PassPhraseCallbackInterface {
private ProgressDialog mProgressDialog = null;
private PausableThread mRunningThread = null;
private Thread mDeletingThread = null;
private long mSecretKeyId = 0;
private String mDeleteFile = null;
protected Preferences mPreferences;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
handlerCallback(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPreferences = Preferences.getPreferences(this);
setLanguage(this, mPreferences.getLanguage());
Apg.initialize(this);
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
}
}
startCacheService(this, mPreferences);
}
public static void startCacheService(Activity activity, Preferences preferences) {
Intent intent = new Intent(activity, Service.class);
intent.putExtra(Service.EXTRA_TTL, preferences.getPassPhraseCacheTtl());
activity.startService(intent);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences).setIcon(
android.R.drawable.ic_menu_preferences);
menu.add(0, Id.menu.option.about, 1, R.string.menu_about).setIcon(
android.R.drawable.ic_menu_info_details);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case Id.menu.option.about: {
showDialog(Id.dialog.about);
return true;
}
case Id.menu.option.preferences: {
startActivity(new Intent(this, PreferencesActivity.class));
return true;
}
case Id.menu.option.search: {
startSearch("", false, null, false);
return true;
}
default: {
break;
}
}
return false;
}
@Override
protected Dialog onCreateDialog(int id) {
// in case it is a progress dialog
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false);
switch (id) {
case Id.dialog.encrypting: {
mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
return mProgressDialog;
}
case Id.dialog.decrypting: {
mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
return mProgressDialog;
}
case Id.dialog.saving: {
mProgressDialog.setMessage(this.getString(R.string.progress_saving));
return mProgressDialog;
}
case Id.dialog.importing: {
mProgressDialog.setMessage(this.getString(R.string.progress_importing));
return mProgressDialog;
}
case Id.dialog.exporting: {
mProgressDialog.setMessage(this.getString(R.string.progress_exporting));
return mProgressDialog;
}
case Id.dialog.deleting: {
mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
return mProgressDialog;
}
case Id.dialog.querying: {
mProgressDialog.setMessage(this.getString(R.string.progress_querying));
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setCancelable(false);
return mProgressDialog;
}
case Id.dialog.signing: {
mProgressDialog.setMessage(this.getString(R.string.progress_signing));
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setCancelable(false);
return mProgressDialog;
}
default: {
break;
}
}
mProgressDialog = null;
switch (id) {
case Id.dialog.about: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("About " + Apg.getFullVersion(this));
LayoutInflater inflater = (LayoutInflater) this
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.info, null);
TextView message = (TextView) layout.findViewById(R.id.message);
message.setText("This is an attempt to bring OpenPGP to Android. "
+ "It is far from complete, but more features are planned (see website).\n\n"
+ "Feel free to send bug reports, suggestions, feature requests, feedback, "
+ "photographs.\n\n" + "mail: thi@thialfihar.org\n"
+ "site: http://apg.thialfihar.org\n\n"
+ "This software is provided \"as is\", without warranty of any kind.");
alert.setView(layout);
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
BaseActivity.this.removeDialog(Id.dialog.about);
}
});
return alert.create();
}
case Id.dialog.pass_phrase: {
return AskForSecretKeyPassPhrase.createDialog(this, getSecretKeyId(), this);
}
case Id.dialog.pass_phrases_do_not_match: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setIcon(android.R.drawable.ic_dialog_alert);
alert.setTitle(R.string.error);
alert.setMessage(R.string.passPhrasesDoNotMatch);
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.pass_phrases_do_not_match);
}
});
alert.setCancelable(false);
return alert.create();
}
case Id.dialog.no_pass_phrase: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setIcon(android.R.drawable.ic_dialog_alert);
alert.setTitle(R.string.error);
alert.setMessage(R.string.passPhraseMustNotBeEmpty);
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.no_pass_phrase);
}
});
alert.setCancelable(false);
return alert.create();
}
case Id.dialog.delete_file: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setIcon(android.R.drawable.ic_dialog_alert);
alert.setTitle(R.string.warning);
alert.setMessage(this.getString(R.string.fileDeleteConfirmation, getDeleteFile()));
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.delete_file);
final File file = new File(getDeleteFile());
showDialog(Id.dialog.deleting);
mDeletingThread = new Thread(new Runnable() {
public void run() {
Bundle data = new Bundle();
data.putInt(Constants.extras.status, Id.message.delete_done);
try {
Apg.deleteFileSecurely(BaseActivity.this, file, BaseActivity.this);
} catch (FileNotFoundException e) {
data.putString(Apg.EXTRA_ERROR, BaseActivity.this.getString(
R.string.error_fileNotFound, file));
} catch (IOException e) {
data.putString(Apg.EXTRA_ERROR, BaseActivity.this.getString(
R.string.error_fileDeleteFailed, file));
}
Message msg = new Message();
msg.setData(data);
sendMessage(msg);
}
});
mDeletingThread.start();
}
});
alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.delete_file);
}
});
alert.setCancelable(true);
return alert.create();
}
default: {
break;
}
}
return super.onCreateDialog(id);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case Id.request.secret_keys: {
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
setSecretKeyId(bundle.getLong(Apg.EXTRA_KEY_ID));
} else {
setSecretKeyId(Id.key.none);
}
break;
}
default: {
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
public void setProgress(int resourceId, int progress, int max) {
setProgress(getString(resourceId), progress, max);
}
public void setProgress(int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt(Constants.extras.status, Id.message.progress_update);
data.putInt(Constants.extras.progress, progress);
data.putInt(Constants.extras.progress_max, max);
msg.setData(data);
mHandler.sendMessage(msg);
}
public void setProgress(String message, int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt(Constants.extras.status, Id.message.progress_update);
data.putString(Constants.extras.message, message);
data.putInt(Constants.extras.progress, progress);
data.putInt(Constants.extras.progress_max, max);
msg.setData(data);
mHandler.sendMessage(msg);
}
public void handlerCallback(Message msg) {
Bundle data = msg.getData();
if (data == null) {
return;
}
int type = data.getInt(Constants.extras.status);
switch (type) {
case Id.message.progress_update: {
String message = data.getString(Constants.extras.message);
if (mProgressDialog != null) {
if (message != null) {
mProgressDialog.setMessage(message);
}
mProgressDialog.setMax(data.getInt(Constants.extras.progress_max));
mProgressDialog.setProgress(data.getInt(Constants.extras.progress));
}
break;
}
case Id.message.delete_done: {
mProgressDialog = null;
deleteDoneCallback(msg);
break;
}
case Id.message.import_done: // intentionally no break
case Id.message.export_done: // intentionally no break
case Id.message.query_done: // intentionally no break
case Id.message.done: {
mProgressDialog = null;
doneCallback(msg);
break;
}
default: {
break;
}
}
}
public void doneCallback(Message msg) {
}
public void deleteDoneCallback(Message msg) {
removeDialog(Id.dialog.deleting);
mDeletingThread = null;
Bundle data = msg.getData();
String error = data.getString(Apg.EXTRA_ERROR);
String message;
if (error != null) {
message = getString(R.string.errorMessage, error);
} else {
message = getString(R.string.fileDeleteSuccessful);
}
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
public void passPhraseCallback(long keyId, String passPhrase) {
Apg.setCachedPassPhrase(keyId, passPhrase);
}
public void sendMessage(Message msg) {
mHandler.sendMessage(msg);
}
public PausableThread getRunningThread() {
return mRunningThread;
}
public void startThread() {
mRunningThread = new PausableThread(this);
mRunningThread.start();
}
public void run() {
}
public void setSecretKeyId(long id) {
mSecretKeyId = id;
}
public long getSecretKeyId() {
return mSecretKeyId;
}
protected void setDeleteFile(String deleteFile) {
mDeleteFile = deleteFile;
}
protected String getDeleteFile() {
return mDeleteFile;
}
public static void setLanguage(Context context, String language) {
Locale locale;
if (language == null || language.equals("")) {
locale = Locale.getDefault();
} else {
locale = new Locale(language);
}
Configuration config = new Configuration();
config.locale = locale;
context.getResources().updateConfiguration(config,
context.getResources().getDisplayMetrics());
}
}

View File

@ -14,13 +14,22 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.DataDestination;
import org.apg.DataSource;
import org.apg.FileDialog;
import org.apg.Id;
import org.apg.InputData;
import org.apg.PausableThread;
import org.apg.provider.DataProvider;
import org.apg.util.Compatibility;
import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.thialfihar.android.apg.provider.DataProvider; import org.apg.R;
import org.thialfihar.android.apg.utils.Compatibility;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;

View File

@ -14,15 +14,19 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.Id;
import org.apg.provider.Database;
import org.apg.ui.widget.KeyEditor;
import org.apg.ui.widget.SectionView;
import org.apg.util.IterableIterator;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.provider.Database; import org.apg.R;
import org.thialfihar.android.apg.ui.widget.KeyEditor;
import org.thialfihar.android.apg.ui.widget.SectionView;
import org.thialfihar.android.apg.utils.IterableIterator;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
@ -104,8 +108,7 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
mSaveButton.setOnClickListener(this); mSaveButton.setOnClickListener(this);
mDiscardButton.setOnClickListener(this); mDiscardButton.setOnClickListener(this);
LayoutInflater inflater = LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
(LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout container = (LinearLayout) findViewById(R.id.container); LinearLayout container = (LinearLayout) findViewById(R.id.container);
mUserIds = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); mUserIds = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
@ -124,8 +127,9 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
updatePassPhraseButtonText(); updatePassPhraseButtonText();
Toast.makeText(this, getString(R.string.warningMessage, getString(R.string.keyEditingIsBeta)), Toast.makeText(this,
Toast.LENGTH_LONG).show(); getString(R.string.warningMessage, getString(R.string.keyEditingIsBeta)),
Toast.LENGTH_LONG).show();
} }
private long getMasterKeyId() { private long getMasterKeyId() {
@ -136,75 +140,72 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
} }
public boolean havePassPhrase() { public boolean havePassPhrase() {
return (!mCurrentPassPhrase.equals("")) || return (!mCurrentPassPhrase.equals(""))
(mNewPassPhrase != null && !mNewPassPhrase.equals("")); || (mNewPassPhrase != null && !mNewPassPhrase.equals(""));
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences) menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences).setIcon(
.setIcon(android.R.drawable.ic_menu_preferences); android.R.drawable.ic_menu_preferences);
menu.add(0, Id.menu.option.about, 1, R.string.menu_about) menu.add(0, Id.menu.option.about, 1, R.string.menu_about).setIcon(
.setIcon(android.R.drawable.ic_menu_info_details); android.R.drawable.ic_menu_info_details);
return true; return true;
} }
@Override @Override
protected Dialog onCreateDialog(int id) { protected Dialog onCreateDialog(int id) {
switch (id) { switch (id) {
case Id.dialog.new_pass_phrase: { case Id.dialog.new_pass_phrase: {
AlertDialog.Builder alert = new AlertDialog.Builder(this); AlertDialog.Builder alert = new AlertDialog.Builder(this);
if (havePassPhrase()) { if (havePassPhrase()) {
alert.setTitle(R.string.title_changePassPhrase); alert.setTitle(R.string.title_changePassPhrase);
} else { } else {
alert.setTitle(R.string.title_setPassPhrase); alert.setTitle(R.string.title_setPassPhrase);
}
alert.setMessage(R.string.enterPassPhraseTwice);
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.pass_phrase, null);
final EditText input1 = (EditText) view.findViewById(R.id.passPhrase);
final EditText input2 = (EditText) view.findViewById(R.id.passPhraseAgain);
alert.setView(view);
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.new_pass_phrase);
String passPhrase1 = "" + input1.getText();
String passPhrase2 = "" + input2.getText();
if (!passPhrase1.equals(passPhrase2)) {
showDialog(Id.dialog.pass_phrases_do_not_match);
return;
}
if (passPhrase1.equals("")) {
showDialog(Id.dialog.no_pass_phrase);
return;
}
mNewPassPhrase = passPhrase1;
updatePassPhraseButtonText();
} }
alert.setMessage(R.string.enterPassPhraseTwice); });
LayoutInflater inflater = alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
(LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); public void onClick(DialogInterface dialog, int id) {
View view = inflater.inflate(R.layout.pass_phrase, null); removeDialog(Id.dialog.new_pass_phrase);
final EditText input1 = (EditText) view.findViewById(R.id.passPhrase); }
final EditText input2 = (EditText) view.findViewById(R.id.passPhraseAgain); });
alert.setView(view); return alert.create();
}
alert.setPositiveButton(android.R.string.ok, default: {
new DialogInterface.OnClickListener() { return super.onCreateDialog(id);
public void onClick(DialogInterface dialog, int id) { }
removeDialog(Id.dialog.new_pass_phrase);
String passPhrase1 = "" + input1.getText();
String passPhrase2 = "" + input2.getText();
if (!passPhrase1.equals(passPhrase2)) {
showDialog(Id.dialog.pass_phrases_do_not_match);
return;
}
if (passPhrase1.equals("")) {
showDialog(Id.dialog.no_pass_phrase);
return;
}
mNewPassPhrase = passPhrase1;
updatePassPhraseButtonText();
}
});
alert.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.new_pass_phrase);
}
});
return alert.create();
}
default: {
return super.onCreateDialog(id);
}
} }
} }
@ -275,8 +276,8 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
String error = data.getString(Apg.EXTRA_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, 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);
@ -285,7 +286,7 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
} }
private void updatePassPhraseButtonText() { private void updatePassPhraseButtonText() {
mChangePassPhrase.setText( mChangePassPhrase.setText(havePassPhrase() ? R.string.btn_changePassPhrase
havePassPhrase() ? R.string.btn_changePassPhrase : R.string.btn_setPassPhrase); : R.string.btn_setPassPhrase);
} }
} }

View File

@ -14,16 +14,24 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.DataDestination;
import org.apg.DataSource;
import org.apg.FileDialog;
import org.apg.Id;
import org.apg.InputData;
import org.apg.provider.DataProvider;
import org.apg.util.Choice;
import org.apg.util.Compatibility;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.provider.DataProvider; import org.apg.R;
import org.thialfihar.android.apg.utils.Choice;
import org.thialfihar.android.apg.utils.Compatibility;
import android.app.Dialog; import android.app.Dialog;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
@ -121,9 +129,9 @@ public class EncryptActivity extends BaseActivity {
mSourcePrevious.setOnClickListener(new OnClickListener() { mSourcePrevious.setOnClickListener(new OnClickListener() {
public void onClick(View v) { public void onClick(View v) {
mSource.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, mSource.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
R.anim.push_right_in)); R.anim.push_right_in));
mSource.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, mSource.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
R.anim.push_right_out)); R.anim.push_right_out));
mSource.showPrevious(); mSource.showPrevious();
updateSource(); updateSource();
} }
@ -133,9 +141,9 @@ public class EncryptActivity extends BaseActivity {
OnClickListener nextSourceClickListener = new OnClickListener() { OnClickListener nextSourceClickListener = new OnClickListener() {
public void onClick(View v) { public void onClick(View v) {
mSource.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, mSource.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
R.anim.push_left_in)); R.anim.push_left_in));
mSource.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, mSource.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
R.anim.push_left_out)); R.anim.push_left_out));
mSource.showNext(); mSource.showNext();
updateSource(); updateSource();
} }
@ -154,9 +162,9 @@ public class EncryptActivity extends BaseActivity {
mModePrevious.setOnClickListener(new OnClickListener() { mModePrevious.setOnClickListener(new OnClickListener() {
public void onClick(View v) { public void onClick(View v) {
mMode.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, mMode.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
R.anim.push_right_in)); R.anim.push_right_in));
mMode.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, mMode.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
R.anim.push_right_out)); R.anim.push_right_out));
mMode.showPrevious(); mMode.showPrevious();
updateMode(); updateMode();
} }
@ -165,9 +173,9 @@ public class EncryptActivity extends BaseActivity {
OnClickListener nextModeClickListener = new OnClickListener() { OnClickListener nextModeClickListener = new OnClickListener() {
public void onClick(View v) { public void onClick(View v) {
mMode.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, mMode.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
R.anim.push_left_in)); R.anim.push_left_in));
mMode.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this, mMode.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
R.anim.push_left_out)); R.anim.push_left_out));
mMode.showNext(); mMode.showNext();
updateMode(); updateMode();
} }
@ -205,14 +213,14 @@ public class EncryptActivity extends BaseActivity {
mFileCompression = (Spinner) findViewById(R.id.fileCompression); mFileCompression = (Spinner) findViewById(R.id.fileCompression);
Choice[] choices = new Choice[] { Choice[] choices = new Choice[] {
new Choice(Id.choice.compression.none, getString(R.string.choice_none) + new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " ("
" (" + getString(R.string.fast) + ")"), + getString(R.string.fast) + ")"),
new Choice(Id.choice.compression.zip, "ZIP (" + getString(R.string.fast) + ")"), new Choice(Id.choice.compression.zip, "ZIP (" + getString(R.string.fast) + ")"),
new Choice(Id.choice.compression.zlib, "ZLIB (" + getString(R.string.fast) + ")"), new Choice(Id.choice.compression.zlib, "ZLIB (" + getString(R.string.fast) + ")"),
new Choice(Id.choice.compression.bzip2, "BZIP2 (" + getString(R.string.very_slow) + ")"), new Choice(Id.choice.compression.bzip2, "BZIP2 (" + getString(R.string.very_slow)
}; + ")"), };
ArrayAdapter<Choice> adapter = ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(this,
new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices); android.R.layout.simple_spinner_item, choices);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mFileCompression.setAdapter(adapter); mFileCompression.setAdapter(adapter);
@ -265,18 +273,18 @@ public class EncryptActivity extends BaseActivity {
}); });
mIntent = getIntent(); mIntent = getIntent();
if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) || if (Apg.Intent.ENCRYPT.equals(mIntent.getAction())
Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction()) || || Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction())
Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction()) || || Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())
Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) { || Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) {
mContentUri = mIntent.getData(); mContentUri = mIntent.getData();
Bundle extras = mIntent.getExtras(); Bundle extras = mIntent.getExtras();
if (extras == null) { if (extras == null) {
extras = new Bundle(); extras = new Bundle();
} }
if (Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction()) || if (Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())
Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) { || Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) {
mReturnResult = true; mReturnResult = true;
} }
@ -341,9 +349,9 @@ public class EncryptActivity extends BaseActivity {
} }
} }
if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) || if (Apg.Intent.ENCRYPT.equals(mIntent.getAction())
Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction()) || || Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())
Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) { || Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) {
if (textData != null) { if (textData != null) {
mMessage.setText(textData); mMessage.setText(textData);
} }
@ -385,11 +393,9 @@ public class EncryptActivity extends BaseActivity {
updateButtons(); updateButtons();
if (mReturnResult && if (mReturnResult
(mMessage.getText().length() > 0 || mData != null || mContentUri != null) && && (mMessage.getText().length() > 0 || mData != null || mContentUri != null)
((mEncryptionKeyIds != null && && ((mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) || getSecretKeyId() != 0)) {
mEncryptionKeyIds.length > 0) ||
getSecretKeyId() != 0)) {
encryptClicked(); encryptClicked();
} }
} }
@ -420,39 +426,69 @@ public class EncryptActivity extends BaseActivity {
private void updateSource() { private void updateSource() {
switch (mSource.getCurrentView().getId()) { switch (mSource.getCurrentView().getId()) {
case R.id.sourceFile: { case R.id.sourceFile: {
mSourceLabel.setText(R.string.label_file); mSourceLabel.setText(R.string.label_file);
break; break;
} }
case R.id.sourceMessage: { case R.id.sourceMessage: {
mSourceLabel.setText(R.string.label_message); mSourceLabel.setText(R.string.label_message);
break; break;
} }
default: { default: {
break; break;
} }
} }
updateButtons(); updateButtons();
} }
private void updateButtons() { private void updateButtons() {
switch (mSource.getCurrentView().getId()) { switch (mSource.getCurrentView().getId()) {
case R.id.sourceFile: { case R.id.sourceFile: {
mEncryptToClipboardButton.setVisibility(View.INVISIBLE); mEncryptToClipboardButton.setVisibility(View.INVISIBLE);
mEncryptButton.setText(R.string.btn_encrypt); mEncryptButton.setText(R.string.btn_encrypt);
break; break;
} }
case R.id.sourceMessage: { case R.id.sourceMessage: {
mSourceLabel.setText(R.string.label_message); mSourceLabel.setText(R.string.label_message);
if (mReturnResult) {
mEncryptToClipboardButton.setVisibility(View.INVISIBLE);
} else {
mEncryptToClipboardButton.setVisibility(View.VISIBLE);
}
if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
if (mReturnResult) { if (mReturnResult) {
mEncryptToClipboardButton.setVisibility(View.INVISIBLE); mEncryptButton.setText(R.string.btn_encrypt);
} else { } else {
mEncryptToClipboardButton.setVisibility(View.VISIBLE); mEncryptButton.setText(R.string.btn_encryptAndEmail);
} }
if (mMode.getCurrentView().getId() == R.id.modeSymmetric) { mEncryptButton.setEnabled(true);
mEncryptToClipboardButton.setText(R.string.btn_encryptToClipboard);
mEncryptToClipboardButton.setEnabled(true);
} else {
if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
if (getSecretKeyId() == 0) {
if (mReturnResult) {
mEncryptButton.setText(R.string.btn_encrypt);
} else {
mEncryptButton.setText(R.string.btn_encryptAndEmail);
}
mEncryptButton.setEnabled(false);
mEncryptToClipboardButton.setText(R.string.btn_encryptToClipboard);
mEncryptToClipboardButton.setEnabled(false);
} else {
if (mReturnResult) {
mEncryptButton.setText(R.string.btn_sign);
} else {
mEncryptButton.setText(R.string.btn_signAndEmail);
}
mEncryptButton.setEnabled(true);
mEncryptToClipboardButton.setText(R.string.btn_signToClipboard);
mEncryptToClipboardButton.setEnabled(true);
}
} else {
if (mReturnResult) { if (mReturnResult) {
mEncryptButton.setText(R.string.btn_encrypt); mEncryptButton.setText(R.string.btn_encrypt);
} else { } else {
@ -461,62 +497,32 @@ public class EncryptActivity extends BaseActivity {
mEncryptButton.setEnabled(true); mEncryptButton.setEnabled(true);
mEncryptToClipboardButton.setText(R.string.btn_encryptToClipboard); mEncryptToClipboardButton.setText(R.string.btn_encryptToClipboard);
mEncryptToClipboardButton.setEnabled(true); mEncryptToClipboardButton.setEnabled(true);
} else {
if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
if (getSecretKeyId() == 0) {
if (mReturnResult) {
mEncryptButton.setText(R.string.btn_encrypt);
} else {
mEncryptButton.setText(R.string.btn_encryptAndEmail);
}
mEncryptButton.setEnabled(false);
mEncryptToClipboardButton.setText(R.string.btn_encryptToClipboard);
mEncryptToClipboardButton.setEnabled(false);
} else {
if (mReturnResult) {
mEncryptButton.setText(R.string.btn_sign);
} else {
mEncryptButton.setText(R.string.btn_signAndEmail);
}
mEncryptButton.setEnabled(true);
mEncryptToClipboardButton.setText(R.string.btn_signToClipboard);
mEncryptToClipboardButton.setEnabled(true);
}
} else {
if (mReturnResult) {
mEncryptButton.setText(R.string.btn_encrypt);
} else {
mEncryptButton.setText(R.string.btn_encryptAndEmail);
}
mEncryptButton.setEnabled(true);
mEncryptToClipboardButton.setText(R.string.btn_encryptToClipboard);
mEncryptToClipboardButton.setEnabled(true);
}
} }
break;
} }
break;
}
default: { default: {
break; break;
} }
} }
} }
private void updateMode() { private void updateMode() {
switch (mMode.getCurrentView().getId()) { switch (mMode.getCurrentView().getId()) {
case R.id.modeAsymmetric: { case R.id.modeAsymmetric: {
mModeLabel.setText(R.string.label_asymmetric); mModeLabel.setText(R.string.label_asymmetric);
break; break;
} }
case R.id.modeSymmetric: { case R.id.modeSymmetric: {
mModeLabel.setText(R.string.label_symmetric); mModeLabel.setText(R.string.label_symmetric);
break; break;
} }
default: { default: {
break; break;
} }
} }
updateButtons(); updateButtons();
} }
@ -550,9 +556,10 @@ public class EncryptActivity extends BaseActivity {
if (!mInputFilename.startsWith("content")) { if (!mInputFilename.startsWith("content")) {
File file = new File(mInputFilename); File file = new File(mInputFilename);
if (!file.exists() || !file.isFile()) { if (!file.exists() || !file.isFile()) {
Toast.makeText(this, getString(R.string.errorMessage, Toast.makeText(
getString(R.string.error_fileNotFound)), this,
Toast.LENGTH_SHORT).show(); getString(R.string.errorMessage, getString(R.string.error_fileNotFound)),
Toast.LENGTH_SHORT).show();
return; return;
} }
} }
@ -582,8 +589,8 @@ public class EncryptActivity extends BaseActivity {
} }
if (!encryptIt && getSecretKeyId() == 0) { if (!encryptIt && getSecretKeyId() == 0) {
Toast.makeText(this, R.string.selectEncryptionOrSignatureKey, Toast.makeText(this, R.string.selectEncryptionOrSignatureKey, Toast.LENGTH_SHORT)
Toast.LENGTH_SHORT).show(); .show();
return; return;
} }
@ -666,55 +673,49 @@ public class EncryptActivity extends BaseActivity {
} }
if (mGenerateSignature) { if (mGenerateSignature) {
Apg.generateSignature(this, in, out, useAsciiArmour, mDataSource.isBinary(), Apg.generateSignature(this, in, out, useAsciiArmour, mDataSource.isBinary(),
getSecretKeyId(), getSecretKeyId(), Apg.getCachedPassPhrase(getSecretKeyId()),
Apg.getCachedPassPhrase(getSecretKeyId()), mPreferences.getDefaultHashAlgorithm(),
mPreferences.getDefaultHashAlgorithm(), mPreferences.getForceV3Signatures(), this);
mPreferences.getForceV3Signatures(),
this);
} else if (signOnly) { } else if (signOnly) {
Apg.signText(this, in, out, getSecretKeyId(), Apg.signText(this, in, out, getSecretKeyId(),
Apg.getCachedPassPhrase(getSecretKeyId()), Apg.getCachedPassPhrase(getSecretKeyId()),
mPreferences.getDefaultHashAlgorithm(), mPreferences.getDefaultHashAlgorithm(),
mPreferences.getForceV3Signatures(), mPreferences.getForceV3Signatures(), this);
this);
} else { } else {
Apg.encrypt(this, in, out, useAsciiArmour, Apg.encrypt(this, in, out, useAsciiArmour, encryptionKeyIds, signatureKeyId,
encryptionKeyIds, signatureKeyId, Apg.getCachedPassPhrase(signatureKeyId), this,
Apg.getCachedPassPhrase(signatureKeyId), this, mPreferences.getDefaultEncryptionAlgorithm(),
mPreferences.getDefaultEncryptionAlgorithm(), mPreferences.getDefaultHashAlgorithm(), compressionId,
mPreferences.getDefaultHashAlgorithm(), mPreferences.getForceV3Signatures(), passPhrase);
compressionId,
mPreferences.getForceV3Signatures(),
passPhrase);
} }
out.close(); out.close();
if (mEncryptTarget != Id.target.file) { if (mEncryptTarget != Id.target.file) {
if(out instanceof ByteArrayOutputStream) { if (out instanceof ByteArrayOutputStream) {
if (useAsciiArmour) { if (useAsciiArmour) {
String extraData = new String(((ByteArrayOutputStream)out).toByteArray()); String extraData = new String(((ByteArrayOutputStream) out).toByteArray());
if (mGenerateSignature) { if (mGenerateSignature) {
data.putString(Apg.EXTRA_SIGNATURE_TEXT, extraData); data.putString(Apg.EXTRA_SIGNATURE_TEXT, extraData);
} else { } else {
data.putString(Apg.EXTRA_ENCRYPTED_MESSAGE, extraData); data.putString(Apg.EXTRA_ENCRYPTED_MESSAGE, extraData);
} }
} else { } else {
byte extraData[] = ((ByteArrayOutputStream)out).toByteArray(); byte extraData[] = ((ByteArrayOutputStream) out).toByteArray();
if (mGenerateSignature) { if (mGenerateSignature) {
data.putByteArray(Apg.EXTRA_SIGNATURE_DATA, extraData); data.putByteArray(Apg.EXTRA_SIGNATURE_DATA, extraData);
} else { } else {
data.putByteArray(Apg.EXTRA_ENCRYPTED_DATA, extraData); data.putByteArray(Apg.EXTRA_ENCRYPTED_DATA, extraData);
} }
} }
} else if(out instanceof FileOutputStream) { } else if (out instanceof FileOutputStream) {
String fileName = mDataDestination.getStreamFilename(); String fileName = mDataDestination.getStreamFilename();
String uri = "content://" + DataProvider.AUTHORITY + "/data/" + fileName; String uri = "content://" + DataProvider.AUTHORITY + "/data/" + fileName;
data.putString(Apg.EXTRA_RESULT_URI, uri); data.putString(Apg.EXTRA_RESULT_URI, uri);
} else { } else {
throw new Apg.GeneralException("No output-data found."); throw new Apg.GeneralException("No output-data found.");
} }
} }
} catch (IOException e) { } catch (IOException e) {
error = "" + e; error = "" + e;
@ -746,8 +747,8 @@ public class EncryptActivity extends BaseActivity {
} else if (mEncryptionKeyIds.length == 1) { } else if (mEncryptionKeyIds.length == 1) {
mSelectKeysButton.setText(R.string.oneKeySelected); mSelectKeysButton.setText(R.string.oneKeySelected);
} else { } else {
mSelectKeysButton.setText("" + mEncryptionKeyIds.length + " " + mSelectKeysButton.setText("" + mEncryptionKeyIds.length + " "
getResources().getString(R.string.nKeysSelected)); + getResources().getString(R.string.nKeysSelected));
} }
if (getSecretKeyId() == 0) { if (getSecretKeyId() == 0) {
@ -788,7 +789,7 @@ public class EncryptActivity extends BaseActivity {
keyIds.add(mEncryptionKeyIds[i]); keyIds.add(mEncryptionKeyIds[i]);
} }
} }
long [] initialKeyIds = null; long[] initialKeyIds = null;
if (keyIds.size() > 0) { if (keyIds.size() > 0) {
initialKeyIds = new long[keyIds.size()]; initialKeyIds = new long[keyIds.size()];
for (int i = 0; i < keyIds.size(); ++i) { for (int i = 0; i < keyIds.size(); ++i) {
@ -807,60 +808,60 @@ public class EncryptActivity extends BaseActivity {
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) { switch (requestCode) {
case Id.request.filename: { case Id.request.filename: {
if (resultCode == RESULT_OK && data != null) { if (resultCode == RESULT_OK && data != null) {
String filename = data.getDataString(); String filename = data.getDataString();
if (filename != null) { if (filename != null) {
// Get rid of URI prefix: // Get rid of URI prefix:
if (filename.startsWith("file://")) { if (filename.startsWith("file://")) {
filename = filename.substring(7); filename = filename.substring(7);
}
// replace %20 and so on
filename = Uri.decode(filename);
mFilename.setText(filename);
} }
// replace %20 and so on
filename = Uri.decode(filename);
mFilename.setText(filename);
} }
return;
} }
return;
}
case Id.request.output_filename: { case Id.request.output_filename: {
if (resultCode == RESULT_OK && data != null) { if (resultCode == RESULT_OK && data != null) {
String filename = data.getDataString(); String filename = data.getDataString();
if (filename != null) { if (filename != null) {
// Get rid of URI prefix: // Get rid of URI prefix:
if (filename.startsWith("file://")) { if (filename.startsWith("file://")) {
filename = filename.substring(7); filename = filename.substring(7);
}
// replace %20 and so on
filename = Uri.decode(filename);
FileDialog.setFilename(filename);
} }
} // replace %20 and so on
return; filename = Uri.decode(filename);
}
case Id.request.secret_keys: { FileDialog.setFilename(filename);
if (resultCode == RESULT_OK) {
super.onActivityResult(requestCode, resultCode, data);
} }
updateView();
break;
} }
return;
}
case Id.request.public_keys: { case Id.request.secret_keys: {
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras(); super.onActivityResult(requestCode, resultCode, data);
mEncryptionKeyIds = bundle.getLongArray(Apg.EXTRA_SELECTION);
}
updateView();
break;
} }
updateView();
break;
}
default: { case Id.request.public_keys: {
break; if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
mEncryptionKeyIds = bundle.getLongArray(Apg.EXTRA_SELECTION);
} }
updateView();
break;
}
default: {
break;
}
} }
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
@ -875,88 +876,82 @@ public class EncryptActivity extends BaseActivity {
Bundle data = msg.getData(); Bundle data = msg.getData();
String error = data.getString(Apg.EXTRA_ERROR); String error = data.getString(Apg.EXTRA_ERROR);
if (error != null) { if (error != null) {
Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show(); Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT)
.show();
return; return;
} }
switch (mEncryptTarget) { switch (mEncryptTarget) {
case Id.target.clipboard: { case Id.target.clipboard: {
String message = data.getString(Apg.EXTRA_ENCRYPTED_MESSAGE); String message = data.getString(Apg.EXTRA_ENCRYPTED_MESSAGE);
Compatibility.copyToClipboard(this, message); Compatibility.copyToClipboard(this, message);
Toast.makeText(this, R.string.encryptionToClipboardSuccessful, Toast.makeText(this, R.string.encryptionToClipboardSuccessful, Toast.LENGTH_SHORT)
Toast.LENGTH_SHORT).show(); .show();
break; break;
}
case Id.target.email: {
if (mReturnResult) {
Intent intent = new Intent();
intent.putExtras(data);
setResult(RESULT_OK, intent);
finish();
return;
} }
case Id.target.email: { String message = data.getString(Apg.EXTRA_ENCRYPTED_MESSAGE);
if (mReturnResult) { Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
Intent intent = new Intent(); emailIntent.setType("text/plain; charset=utf-8");
intent.putExtras(data); emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
setResult(RESULT_OK, intent); if (mSubject != null) {
finish(); emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, mSubject);
return;
}
String message = data.getString(Apg.EXTRA_ENCRYPTED_MESSAGE);
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("text/plain; charset=utf-8");
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
if (mSubject != null) {
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
mSubject);
}
if (mSendTo != null) {
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
new String[] { mSendTo });
}
EncryptActivity.this.
startActivity(Intent.createChooser(emailIntent,
getString(R.string.title_sendEmail)));
break;
} }
if (mSendTo != null) {
case Id.target.file: { emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] { mSendTo });
Toast.makeText(this, R.string.encryptionSuccessful, Toast.LENGTH_SHORT).show();
if (mDeleteAfter.isChecked()) {
setDeleteFile(mInputFilename);
showDialog(Id.dialog.delete_file);
}
break;
} }
EncryptActivity.this.startActivity(Intent.createChooser(emailIntent,
getString(R.string.title_sendEmail)));
break;
}
default: { case Id.target.file: {
// shouldn't happen Toast.makeText(this, R.string.encryptionSuccessful, Toast.LENGTH_SHORT).show();
break; if (mDeleteAfter.isChecked()) {
setDeleteFile(mInputFilename);
showDialog(Id.dialog.delete_file);
} }
break;
}
default: {
// shouldn't happen
break;
}
} }
} }
@Override @Override
protected Dialog onCreateDialog(int id) { protected Dialog onCreateDialog(int id) {
switch (id) { switch (id) {
case Id.dialog.output_filename: { case Id.dialog.output_filename: {
return FileDialog.build(this, getString(R.string.title_encryptToFile), return FileDialog.build(this, getString(R.string.title_encryptToFile),
getString(R.string.specifyFileToEncryptTo), getString(R.string.specifyFileToEncryptTo), mOutputFilename,
mOutputFilename, new FileDialog.OnClickListener() {
new FileDialog.OnClickListener() { public void onOkClick(String filename, boolean checked) {
public void onOkClick(String filename, boolean checked) { removeDialog(Id.dialog.output_filename);
removeDialog(Id.dialog.output_filename); mOutputFilename = filename;
mOutputFilename = filename; encryptStart();
encryptStart(); }
}
public void onCancelClick() { public void onCancelClick() {
removeDialog(Id.dialog.output_filename); removeDialog(Id.dialog.output_filename);
} }
}, }, getString(R.string.filemanager_titleSave),
getString(R.string.filemanager_titleSave), getString(R.string.filemanager_btnSave), null, Id.request.output_filename);
getString(R.string.filemanager_btnSave), }
null,
Id.request.output_filename);
}
default: { default: {
break; break;
} }
} }
return super.onCreateDialog(id); return super.onCreateDialog(id);

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg; package org.apg.ui;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@ -6,7 +6,10 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Vector; import java.util.Vector;
import org.thialfihar.android.apg.utils.Choice; import org.apg.Apg;
import org.apg.Id;
import org.apg.util.Choice;
import org.apg.R;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;

View File

@ -1,11 +1,16 @@
package org.thialfihar.android.apg; package org.apg.ui;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.HkpKeyServer;
import org.apg.Id;
import org.apg.KeyServer.QueryException;
import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.thialfihar.android.apg.KeyServer.QueryException; import org.apg.R;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;

View File

@ -14,14 +14,20 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.FileDialog;
import org.apg.Id;
import org.apg.InputData;
import org.apg.provider.KeyRings;
import org.apg.provider.Keys;
import org.apg.provider.UserIds;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.provider.KeyRings; import org.apg.R;
import org.thialfihar.android.apg.provider.Keys;
import org.thialfihar.android.apg.provider.UserIds;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;

View File

@ -14,13 +14,15 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import java.util.Vector; import java.util.Vector;
import org.thialfihar.android.apg.ui.widget.Editor; import org.apg.Apg;
import org.thialfihar.android.apg.ui.widget.Editor.EditorListener; import org.apg.ui.widget.Editor;
import org.thialfihar.android.apg.ui.widget.KeyServerEditor; import org.apg.ui.widget.KeyServerEditor;
import org.apg.ui.widget.Editor.EditorListener;
import org.apg.R;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;

View File

@ -1,12 +1,17 @@
package org.thialfihar.android.apg; package org.apg.ui;
import java.util.List; import java.util.List;
import java.util.Vector; import java.util.Vector;
import org.thialfihar.android.apg.KeyServer.InsufficientQuery; import org.apg.Apg;
import org.thialfihar.android.apg.KeyServer.KeyInfo; import org.apg.Constants;
import org.thialfihar.android.apg.KeyServer.QueryException; import org.apg.HkpKeyServer;
import org.thialfihar.android.apg.KeyServer.TooManyResponses; import org.apg.Id;
import org.apg.KeyServer.InsufficientQuery;
import org.apg.KeyServer.KeyInfo;
import org.apg.KeyServer.QueryException;
import org.apg.KeyServer.TooManyResponses;
import org.apg.R;
import android.app.Activity; import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;

View File

@ -14,11 +14,15 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import java.util.Vector; import java.util.Vector;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import org.apg.Apg;
import org.apg.Preferences;
import org.apg.R;
import android.app.ListActivity; import android.app.ListActivity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;

View File

@ -14,14 +14,20 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import java.security.Security; import java.security.Security;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apg.Apg;
import org.apg.Id;
import org.apg.Id.dialog;
import org.apg.Id.menu;
import org.apg.Id.menu.option;
import org.apg.provider.Accounts;
import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.thialfihar.android.apg.provider.Accounts; import org.apg.R;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;

View File

@ -14,11 +14,20 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.Id;
import org.apg.Preferences;
import org.apg.Constants.pref;
import org.apg.Id.choice;
import org.apg.Id.request;
import org.apg.Id.choice.compression;
import org.apg.ui.widget.IntegerListPreference;
import org.spongycastle.bcpg.HashAlgorithmTags; import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData; import org.spongycastle.openpgp.PGPEncryptedData;
import org.thialfihar.android.apg.ui.widget.IntegerListPreference; import org.apg.R;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;

View File

@ -14,9 +14,18 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.Id;
import org.apg.Constants.path;
import org.apg.Id.menu;
import org.apg.Id.request;
import org.apg.Id.type;
import org.apg.Id.menu.option;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.apg.R;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;

View File

@ -14,7 +14,19 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.AskForSecretKeyPassPhrase;
import org.apg.Constants;
import org.apg.Id;
import org.apg.Constants.path;
import org.apg.Id.dialog;
import org.apg.Id.menu;
import org.apg.Id.message;
import org.apg.Id.type;
import org.apg.Id.menu.option;
import org.apg.R;
import android.app.Dialog; import android.app.Dialog;
import android.content.Intent; import android.content.Intent;

View File

@ -14,10 +14,16 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import java.util.Vector; import java.util.Vector;
import org.apg.Apg;
import org.apg.Id;
import org.apg.Id.menu;
import org.apg.Id.menu.option;
import org.apg.R;
import android.app.SearchManager; import android.app.SearchManager;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;

View File

@ -14,13 +14,17 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import java.util.Date; import java.util.Date;
import org.thialfihar.android.apg.provider.KeyRings; import org.apg.Apg;
import org.thialfihar.android.apg.provider.Keys; import org.apg.Id;
import org.thialfihar.android.apg.provider.UserIds; import org.apg.Id.database;
import org.apg.provider.KeyRings;
import org.apg.provider.Keys;
import org.apg.provider.UserIds;
import org.apg.R;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;

View File

@ -14,7 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.Id;
import org.apg.Id.menu;
import org.apg.Id.menu.option;
import org.apg.R;
import android.app.SearchManager; import android.app.SearchManager;
import android.content.Intent; import android.content.Intent;

View File

@ -1,10 +1,14 @@
package org.thialfihar.android.apg; package org.apg.ui;
import java.util.Date; import java.util.Date;
import org.thialfihar.android.apg.provider.KeyRings; import org.apg.Apg;
import org.thialfihar.android.apg.provider.Keys; import org.apg.Id;
import org.thialfihar.android.apg.provider.UserIds; import org.apg.Id.database;
import org.apg.provider.KeyRings;
import org.apg.provider.Keys;
import org.apg.provider.UserIds;
import org.apg.R;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;

View File

@ -1,7 +1,14 @@
package org.thialfihar.android.apg; package org.apg.ui;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.HkpKeyServer;
import org.apg.Id;
import org.apg.Constants.extras;
import org.apg.Id.message;
import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.apg.R;
import android.os.Bundle; import android.os.Bundle;
import android.os.Message; import android.os.Message;

View File

@ -1,10 +1,19 @@
package org.thialfihar.android.apg; package org.apg.ui;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException; import java.security.NoSuchProviderException;
import java.security.SignatureException; import java.security.SignatureException;
import java.util.Iterator; import java.util.Iterator;
import org.apg.Apg;
import org.apg.Constants;
import org.apg.HkpKeyServer;
import org.apg.Id;
import org.apg.Constants.extras;
import org.apg.Id.dialog;
import org.apg.Id.message;
import org.apg.Id.request;
import org.apg.Id.return_value;
import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPPrivateKey;
@ -16,6 +25,7 @@ import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector; import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil; import org.spongycastle.openpgp.PGPUtil;
import org.apg.R;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.ui.widget; package org.apg.ui.widget;
public interface Editor { public interface Editor {
public interface EditorListener { public interface EditorListener {

View File

@ -14,7 +14,7 @@
* the License. * the License.
*/ */
package org.thialfihar.android.apg.ui.widget; package org.apg.ui.widget;
import android.content.Context; import android.content.Context;
import android.preference.ListPreference; import android.preference.ListPreference;

View File

@ -14,14 +14,14 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.ui.widget; package org.apg.ui.widget;
import org.apg.Apg;
import org.apg.Id;
import org.apg.util.Choice;
import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.thialfihar.android.apg.Apg; import org.apg.R;
import org.thialfihar.android.apg.Id;
import org.thialfihar.android.apg.R;
import org.thialfihar.android.apg.utils.Choice;
import android.app.DatePickerDialog; import android.app.DatePickerDialog;
import android.app.Dialog; import android.app.Dialog;

View File

@ -14,9 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.ui.widget; package org.apg.ui.widget;
import org.thialfihar.android.apg.R; import org.apg.R;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;

View File

@ -14,15 +14,15 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.ui.widget; package org.apg.ui.widget;
import org.apg.Apg;
import org.apg.Id;
import org.apg.ui.widget.Editor.EditorListener;
import org.apg.util.Choice;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.thialfihar.android.apg.Apg; import org.apg.R;
import org.thialfihar.android.apg.Id;
import org.thialfihar.android.apg.R;
import org.thialfihar.android.apg.ui.widget.Editor.EditorListener;
import org.thialfihar.android.apg.utils.Choice;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.ProgressDialog; import android.app.ProgressDialog;

View File

@ -14,12 +14,12 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.ui.widget; package org.apg.ui.widget;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.thialfihar.android.apg.R; import org.apg.R;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;

View File

@ -14,10 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.utils; package org.apg.util;
import org.thialfihar.android.apg.IApgService; import org.apg.util.ApgConInterface.OnCallFinishListener;
import org.thialfihar.android.apg.utils.ApgConInterface.OnCallFinishListener; import org.apg.IApgService;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.ContentResolver; import android.content.ContentResolver;

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg.utils; package org.apg.util;
public interface ApgConInterface { public interface ApgConInterface {
public static interface OnCallFinishListener { public static interface OnCallFinishListener {

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.utils; package org.apg.util;
public class Choice { public class Choice {
private String mName; private String mName;

View File

@ -1,4 +1,4 @@
package org.thialfihar.android.apg.utils; package org.apg.util;
import java.lang.reflect.Method; import java.lang.reflect.Method;

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.thialfihar.android.apg.utils; package org.apg.util;
import java.util.Iterator; import java.util.Iterator;

View File

@ -1,464 +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;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Locale;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class BaseActivity extends Activity
implements Runnable, ProgressDialogUpdater,
AskForSecretKeyPassPhrase.PassPhraseCallbackInterface {
private ProgressDialog mProgressDialog = null;
private PausableThread mRunningThread = null;
private Thread mDeletingThread = null;
private long mSecretKeyId = 0;
private String mDeleteFile = null;
protected Preferences mPreferences;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
handlerCallback(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPreferences = Preferences.getPreferences(this);
setLanguage(this, mPreferences.getLanguage());
Apg.initialize(this);
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
}
}
startCacheService(this, mPreferences);
}
public static void startCacheService(Activity activity, Preferences preferences) {
Intent intent = new Intent(activity, Service.class);
intent.putExtra(Service.EXTRA_TTL, preferences.getPassPhraseCacheTtl());
activity.startService(intent);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences)
.setIcon(android.R.drawable.ic_menu_preferences);
menu.add(0, Id.menu.option.about, 1, R.string.menu_about)
.setIcon(android.R.drawable.ic_menu_info_details);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case Id.menu.option.about: {
showDialog(Id.dialog.about);
return true;
}
case Id.menu.option.preferences: {
startActivity(new Intent(this, PreferencesActivity.class));
return true;
}
case Id.menu.option.search: {
startSearch("", false, null, false);
return true;
}
default: {
break;
}
}
return false;
}
@Override
protected Dialog onCreateDialog(int id) {
// in case it is a progress dialog
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false);
switch (id) {
case Id.dialog.encrypting: {
mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
return mProgressDialog;
}
case Id.dialog.decrypting: {
mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
return mProgressDialog;
}
case Id.dialog.saving: {
mProgressDialog.setMessage(this.getString(R.string.progress_saving));
return mProgressDialog;
}
case Id.dialog.importing: {
mProgressDialog.setMessage(this.getString(R.string.progress_importing));
return mProgressDialog;
}
case Id.dialog.exporting: {
mProgressDialog.setMessage(this.getString(R.string.progress_exporting));
return mProgressDialog;
}
case Id.dialog.deleting: {
mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
return mProgressDialog;
}
case Id.dialog.querying: {
mProgressDialog.setMessage(this.getString(R.string.progress_querying));
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setCancelable(false);
return mProgressDialog;
}
case Id.dialog.signing: {
mProgressDialog.setMessage(this.getString(R.string.progress_signing));
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setCancelable(false);
return mProgressDialog;
}
default: {
break;
}
}
mProgressDialog = null;
switch (id) {
case Id.dialog.about: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("About " + Apg.getFullVersion(this));
LayoutInflater inflater =
(LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.info, null);
TextView message = (TextView) layout.findViewById(R.id.message);
message.setText("This is an attempt to bring OpenPGP to Android. " +
"It is far from complete, but more features are planned (see website).\n\n" +
"Feel free to send bug reports, suggestions, feature requests, feedback, " +
"photographs.\n\n" +
"mail: thi@thialfihar.org\n" +
"site: http://apg.thialfihar.org\n\n" +
"This software is provided \"as is\", without warranty of any kind.");
alert.setView(layout);
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
BaseActivity.this.removeDialog(Id.dialog.about);
}
});
return alert.create();
}
case Id.dialog.pass_phrase: {
return AskForSecretKeyPassPhrase.createDialog(this, getSecretKeyId(), this);
}
case Id.dialog.pass_phrases_do_not_match: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setIcon(android.R.drawable.ic_dialog_alert);
alert.setTitle(R.string.error);
alert.setMessage(R.string.passPhrasesDoNotMatch);
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.pass_phrases_do_not_match);
}
});
alert.setCancelable(false);
return alert.create();
}
case Id.dialog.no_pass_phrase: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setIcon(android.R.drawable.ic_dialog_alert);
alert.setTitle(R.string.error);
alert.setMessage(R.string.passPhraseMustNotBeEmpty);
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.no_pass_phrase);
}
});
alert.setCancelable(false);
return alert.create();
}
case Id.dialog.delete_file: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setIcon(android.R.drawable.ic_dialog_alert);
alert.setTitle(R.string.warning);
alert.setMessage(this.getString(R.string.fileDeleteConfirmation, getDeleteFile()));
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.delete_file);
final File file = new File(getDeleteFile());
showDialog(Id.dialog.deleting);
mDeletingThread = new Thread(new Runnable() {
public void run() {
Bundle data = new Bundle();
data.putInt(Constants.extras.status, Id.message.delete_done);
try {
Apg.deleteFileSecurely(BaseActivity.this, file, BaseActivity.this);
} catch (FileNotFoundException e) {
data.putString(Apg.EXTRA_ERROR,
BaseActivity.this.getString(
R.string.error_fileNotFound, file));
} catch (IOException e) {
data.putString(Apg.EXTRA_ERROR,
BaseActivity.this.getString(
R.string.error_fileDeleteFailed, file));
}
Message msg = new Message();
msg.setData(data);
sendMessage(msg);
}
});
mDeletingThread.start();
}
});
alert.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.delete_file);
}
});
alert.setCancelable(true);
return alert.create();
}
default: {
break;
}
}
return super.onCreateDialog(id);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case Id.request.secret_keys: {
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
setSecretKeyId(bundle.getLong(Apg.EXTRA_KEY_ID));
} else {
setSecretKeyId(Id.key.none);
}
break;
}
default: {
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
public void setProgress(int resourceId, int progress, int max) {
setProgress(getString(resourceId), progress, max);
}
public void setProgress(int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt(Constants.extras.status, Id.message.progress_update);
data.putInt(Constants.extras.progress, progress);
data.putInt(Constants.extras.progress_max, max);
msg.setData(data);
mHandler.sendMessage(msg);
}
public void setProgress(String message, int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
data.putInt(Constants.extras.status, Id.message.progress_update);
data.putString(Constants.extras.message, message);
data.putInt(Constants.extras.progress, progress);
data.putInt(Constants.extras.progress_max, max);
msg.setData(data);
mHandler.sendMessage(msg);
}
public void handlerCallback(Message msg) {
Bundle data = msg.getData();
if (data == null) {
return;
}
int type = data.getInt(Constants.extras.status);
switch (type) {
case Id.message.progress_update: {
String message = data.getString(Constants.extras.message);
if (mProgressDialog != null) {
if (message != null) {
mProgressDialog.setMessage(message);
}
mProgressDialog.setMax(data.getInt(Constants.extras.progress_max));
mProgressDialog.setProgress(data.getInt(Constants.extras.progress));
}
break;
}
case Id.message.delete_done: {
mProgressDialog = null;
deleteDoneCallback(msg);
break;
}
case Id.message.import_done: // intentionally no break
case Id.message.export_done: // intentionally no break
case Id.message.query_done: // intentionally no break
case Id.message.done: {
mProgressDialog = null;
doneCallback(msg);
break;
}
default: {
break;
}
}
}
public void doneCallback(Message msg) {
}
public void deleteDoneCallback(Message msg) {
removeDialog(Id.dialog.deleting);
mDeletingThread = null;
Bundle data = msg.getData();
String error = data.getString(Apg.EXTRA_ERROR);
String message;
if (error != null) {
message = getString(R.string.errorMessage, error);
} else {
message = getString(R.string.fileDeleteSuccessful);
}
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
public void passPhraseCallback(long keyId, String passPhrase) {
Apg.setCachedPassPhrase(keyId, passPhrase);
}
public void sendMessage(Message msg) {
mHandler.sendMessage(msg);
}
public PausableThread getRunningThread() {
return mRunningThread;
}
public void startThread() {
mRunningThread = new PausableThread(this);
mRunningThread.start();
}
public void run() {
}
public void setSecretKeyId(long id) {
mSecretKeyId = id;
}
public long getSecretKeyId() {
return mSecretKeyId;
}
protected void setDeleteFile(String deleteFile) {
mDeleteFile = deleteFile;
}
protected String getDeleteFile() {
return mDeleteFile;
}
public static void setLanguage(Context context, String language)
{
Locale locale;
if (language == null || language.equals(""))
{
locale = Locale.getDefault();
}
else
{
locale = new Locale(language);
}
Configuration config = new Configuration();
config.locale = locale;
context.getResources().updateConfiguration(config,
context.getResources().getDisplayMetrics());
}
}

View File

@ -1,79 +0,0 @@
package org.thialfihar.android.apg;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.thialfihar.android.apg.Apg.GeneralException;
import android.content.Context;
import android.os.Environment;
public class DataDestination {
private String mStreamFilename;
private String mFilename;
private int mMode = Id.mode.undefined;
public DataDestination() {
}
public void setMode(int mode) {
mMode = mode;
}
public void setFilename(String filename) {
mFilename = filename;
}
public String getStreamFilename() {
return mStreamFilename;
}
protected OutputStream getOutputStream(Context context)
throws Apg.GeneralException, FileNotFoundException, IOException {
OutputStream out = null;
mStreamFilename = null;
switch (mMode) {
case Id.mode.stream: {
try {
while (true) {
mStreamFilename = Apg.generateRandomString(32);
if (mStreamFilename == null) {
throw new Apg.GeneralException("couldn't generate random file name");
}
context.openFileInput(mStreamFilename).close();
}
} catch (FileNotFoundException e) {
// found a name that isn't used yet
}
out = context.openFileOutput(mStreamFilename, Context.MODE_PRIVATE);
break;
}
case Id.mode.byte_array: {
out = new ByteArrayOutputStream();
break;
}
case Id.mode.file: {
if (mFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
throw new GeneralException(context.getString(R.string.error_externalStorageNotReady));
}
}
out = new FileOutputStream(mFilename);
break;
}
default: {
break;
}
}
return out;
}
}